ICU-13800 Adding clone() on [Un]LocalizedNumber[Range]Formatter.
- Returns a LocalPointer that can be converted to std::unique_ptr.
diff --git a/icu4c/source/i18n/number_fluent.cpp b/icu4c/source/i18n/number_fluent.cpp
index 37f780c..5bc1f98 100644
--- a/icu4c/source/i18n/number_fluent.cpp
+++ b/icu4c/source/i18n/number_fluent.cpp
@@ -328,6 +328,16 @@ UnicodeString NumberFormatterSettings<Derived>::toSkeleton(UErrorCode& status) c
return skeleton::generate(fMacros, status);
}
+template<typename Derived>
+LocalPointer<Derived> NumberFormatterSettings<Derived>::clone() const & {
+ return LocalPointer<Derived>(new Derived(*this));
+}
+
+template<typename Derived>
+LocalPointer<Derived> NumberFormatterSettings<Derived>::clone() && {
+ return LocalPointer<Derived>(new Derived(std::move(*this)));
+}
+
// Declare all classes that implement NumberFormatterSettings
// See https://stackoverflow.com/a/495056/1407170
template
diff --git a/icu4c/source/i18n/numrange_fluent.cpp b/icu4c/source/i18n/numrange_fluent.cpp
index 27ffd0b..10479f9 100644
--- a/icu4c/source/i18n/numrange_fluent.cpp
+++ b/icu4c/source/i18n/numrange_fluent.cpp
@@ -162,6 +162,16 @@ Derived NumberRangeFormatterSettings<Derived>::identityFallback(UNumberRangeIden
return move;
}
+template<typename Derived>
+LocalPointer<Derived> NumberRangeFormatterSettings<Derived>::clone() const & {
+ return LocalPointer<Derived>(new Derived(*this));
+}
+
+template<typename Derived>
+LocalPointer<Derived> NumberRangeFormatterSettings<Derived>::clone() && {
+ return LocalPointer<Derived>(new Derived(std::move(*this)));
+}
+
// Declare all classes that implement NumberRangeFormatterSettings
// See https://stackoverflow.com/a/495056/1407170
template
diff --git a/icu4c/source/i18n/unicode/numberformatter.h b/icu4c/source/i18n/unicode/numberformatter.h
index 611fd7e..eb3e9f8 100644
--- a/icu4c/source/i18n/unicode/numberformatter.h
+++ b/icu4c/source/i18n/unicode/numberformatter.h
@@ -44,18 +44,19 @@
* .format(1234)
* .toString(); // €1.2K in en-US
*
- * // Create a formatter in a singleton for use later:
+ * // Create a formatter in a singleton by value for use later:
* static const LocalizedNumberFormatter formatter = NumberFormatter::withLocale(...)
* .unit(NoUnit::percent())
* .precision(Precision::fixedFraction(3));
* formatter.format(5.9831).toString(); // 5.983% in en-US
*
- * // Create a "template" in a singleton but without setting a locale until the call site:
- * static const UnlocalizedNumberFormatter template = NumberFormatter::with()
+ * // Create a "template" in a singleton unique_ptr but without setting a locale until the call site:
+ * std::unique_ptr<UnlocalizedNumberFormatter> template = NumberFormatter::with()
* .sign(UNumberSignDisplay::UNUM_SIGN_ALWAYS)
* .adoptUnit(MeasureUnit::createMeter(status))
- * .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME);
- * template.locale(...).format(1234).toString(); // +1,234 meters in en-US
+ * .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME)
+ * .clone();
+ * template->locale(...).format(1234).toString(); // +1,234 meters in en-US
* </pre>
*
* <p>
@@ -2079,6 +2080,28 @@ class U_I18N_API NumberFormatterSettings {
UnicodeString toSkeleton(UErrorCode& status) const;
/**
+ * Returns the current (Un)LocalizedNumberFormatter as a LocalPointer
+ * wrapping a heap-allocated copy of the current object.
+ *
+ * This is equivalent to new-ing the move constructor with a value object
+ * as the argument.
+ *
+ * @return A wrapped (Un)LocalizedNumberFormatter pointer, or a wrapped
+ * nullptr on failure.
+ * @draft ICU 64
+ */
+ LocalPointer<Derived> clone() const &;
+
+ /**
+ * Overload of clone for use on an rvalue reference.
+ *
+ * @return A wrapped (Un)LocalizedNumberFormatter pointer, or a wrapped
+ * nullptr on failure.
+ * @draft ICU 64
+ */
+ LocalPointer<Derived> clone() &&;
+
+ /**
* Sets the UErrorCode if an error occurred in the fluent chain.
* Preserves older error codes in the outErrorCode.
* @return TRUE if U_FAILURE(outErrorCode)
diff --git a/icu4c/source/i18n/unicode/numberrangeformatter.h b/icu4c/source/i18n/unicode/numberrangeformatter.h
index dee75c6..3cbf290 100644
--- a/icu4c/source/i18n/unicode/numberrangeformatter.h
+++ b/icu4c/source/i18n/unicode/numberrangeformatter.h
@@ -446,6 +446,28 @@ class U_I18N_API NumberRangeFormatterSettings {
Derived identityFallback(UNumberRangeIdentityFallback identityFallback) &&;
/**
+ * Returns the current (Un)LocalizedNumberRangeFormatter as a LocalPointer
+ * wrapping a heap-allocated copy of the current object.
+ *
+ * This is equivalent to new-ing the move constructor with a value object
+ * as the argument.
+ *
+ * @return A wrapped (Un)LocalizedNumberRangeFormatter pointer, or a wrapped
+ * nullptr on failure.
+ * @draft ICU 64
+ */
+ LocalPointer<Derived> clone() const &;
+
+ /**
+ * Overload of clone for use on an rvalue reference.
+ *
+ * @return A wrapped (Un)LocalizedNumberRangeFormatter pointer, or a wrapped
+ * nullptr on failure.
+ * @draft ICU 64
+ */
+ LocalPointer<Derived> clone() &&;
+
+ /**
* Sets the UErrorCode if an error occurred in the fluent chain.
* Preserves older error codes in the outErrorCode.
* @return TRUE if U_FAILURE(outErrorCode)
diff --git a/icu4c/source/test/intltest/numbertest.h b/icu4c/source/test/intltest/numbertest.h
index 5750cec..9efc12d 100644
--- a/icu4c/source/test/intltest/numbertest.h
+++ b/icu4c/source/test/intltest/numbertest.h
@@ -78,6 +78,7 @@ class NumberFormatterApiTest : public IntlTestWithFieldPosition {
void validRanges();
void copyMove();
void localPointerCAPI();
+ void toObject();
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0);
@@ -270,6 +271,7 @@ class NumberRangeFormatterTest : public IntlTestWithFieldPosition {
void testPlurals();
void testFieldPositions();
void testCopyMove();
+ void toObject();
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0);
diff --git a/icu4c/source/test/intltest/numbertest_api.cpp b/icu4c/source/test/intltest/numbertest_api.cpp
index 26f13ca..bfa0ba2 100644
--- a/icu4c/source/test/intltest/numbertest_api.cpp
+++ b/icu4c/source/test/intltest/numbertest_api.cpp
@@ -8,6 +8,7 @@
#include "charstr.h"
#include <cstdarg>
#include <cmath>
+#include <memory>
#include "unicode/unum.h"
#include "unicode/numberformatter.h"
#include "number_asformat.h"
@@ -91,6 +92,7 @@ void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const cha
TESTCASE_AUTO(validRanges);
TESTCASE_AUTO(copyMove);
TESTCASE_AUTO(localPointerCAPI);
+ TESTCASE_AUTO(toObject);
TESTCASE_AUTO_END;
}
@@ -2783,6 +2785,64 @@ void NumberFormatterApiTest::localPointerCAPI() {
// No need to do any cleanup since we are using LocalPointer.
}
+void NumberFormatterApiTest::toObject() {
+ IcuTestErrorCode status(*this, "toObject");
+
+ // const lvalue version
+ {
+ LocalizedNumberFormatter lnf = NumberFormatter::withLocale("en")
+ .precision(Precision::fixedFraction(2));
+ LocalPointer<LocalizedNumberFormatter> lnf2(lnf.clone());
+ assertFalse("should create successfully, const lvalue", lnf2.isNull());
+ assertEquals("object API test, const lvalue", u"1,000.00",
+ lnf2->formatDouble(1000, status).toString(status));
+ }
+
+ // rvalue reference version
+ {
+ LocalPointer<LocalizedNumberFormatter> lnf(
+ NumberFormatter::withLocale("en")
+ .precision(Precision::fixedFraction(2))
+ .clone());
+ assertFalse("should create successfully, rvalue reference", lnf.isNull());
+ assertEquals("object API test, rvalue reference", u"1,000.00",
+ lnf->formatDouble(1000, status).toString(status));
+ }
+
+ // to std::unique_ptr via constructor
+ {
+ std::unique_ptr<LocalizedNumberFormatter> lnf(
+ NumberFormatter::withLocale("en")
+ .precision(Precision::fixedFraction(2))
+ .clone());
+ assertTrue("should create successfully, unique_ptr", static_cast<bool>(lnf));
+ assertEquals("object API test, unique_ptr", u"1,000.00",
+ lnf->formatDouble(1000, status).toString(status));
+ }
+
+ // to std::unique_ptr via assignment
+ {
+ std::unique_ptr<LocalizedNumberFormatter> lnf =
+ NumberFormatter::withLocale("en")
+ .precision(Precision::fixedFraction(2))
+ .clone();
+ assertTrue("should create successfully, unique_ptr B", static_cast<bool>(lnf));
+ assertEquals("object API test, unique_ptr B", u"1,000.00",
+ lnf->formatDouble(1000, status).toString(status));
+ }
+
+ // to LocalPointer via assignment
+ {
+ LocalPointer<UnlocalizedNumberFormatter> f =
+ NumberFormatter::with().clone();
+ }
+
+ // make sure no memory leaks
+ {
+ NumberFormatter::with().clone();
+ }
+}
+
void NumberFormatterApiTest::assertFormatDescending(const char16_t* umessage, const char16_t* uskeleton,
const UnlocalizedNumberFormatter& f, Locale locale,
diff --git a/icu4c/source/test/intltest/numbertest_range.cpp b/icu4c/source/test/intltest/numbertest_range.cpp
index a15ef9d..75816ba 100644
--- a/icu4c/source/test/intltest/numbertest_range.cpp
+++ b/icu4c/source/test/intltest/numbertest_range.cpp
@@ -50,6 +50,7 @@ void NumberRangeFormatterTest::runIndexedTest(int32_t index, UBool exec, const c
TESTCASE_AUTO(testPlurals);
TESTCASE_AUTO(testFieldPositions);
TESTCASE_AUTO(testCopyMove);
+ TESTCASE_AUTO(toObject);
TESTCASE_AUTO_END;
}
@@ -823,6 +824,42 @@ void NumberRangeFormatterTest::testCopyMove() {
assertEquals("FormattedNumberRange move assignment", u"3,00–6,00 $US", result.toString(status));
}
+void NumberRangeFormatterTest::toObject() {
+ IcuTestErrorCode status(*this, "toObject");
+
+ // const lvalue version
+ {
+ LocalizedNumberRangeFormatter lnf = NumberRangeFormatter::withLocale("en");
+ LocalPointer<LocalizedNumberRangeFormatter> lnf2(lnf.clone());
+ assertFalse("should create successfully, const lvalue", lnf2.isNull());
+ assertEquals("object API test, const lvalue", u"5–7",
+ lnf2->formatFormattableRange(5, 7, status).toString(status));
+ }
+
+ // rvalue reference version
+ {
+ LocalPointer<LocalizedNumberRangeFormatter> lnf(
+ NumberRangeFormatter::withLocale("en").clone());
+ assertFalse("should create successfully, rvalue reference", lnf.isNull());
+ assertEquals("object API test, rvalue reference", u"5–7",
+ lnf->formatFormattableRange(5, 7, status).toString(status));
+ }
+
+ // to std::unique_ptr via assignment
+ {
+ std::unique_ptr<LocalizedNumberRangeFormatter> lnf =
+ NumberRangeFormatter::withLocale("en").clone();
+ assertTrue("should create successfully, unique_ptr B", static_cast<bool>(lnf));
+ assertEquals("object API test, unique_ptr B", u"5–7",
+ lnf->formatFormattableRange(5, 7, status).toString(status));
+ }
+
+ // make sure no memory leaks
+ {
+ NumberRangeFormatter::with().clone();
+ }
+}
+
void NumberRangeFormatterTest::assertFormatRange(
const char16_t* message,
const UnlocalizedNumberRangeFormatter& f,