Skip to content

Commit 77072a1

Browse files
committed
Format strong enumerations from format parameters
1 parent 9b37487 commit 77072a1

File tree

3 files changed

+87
-8
lines changed

3 files changed

+87
-8
lines changed

fly/types/string/detail/string_formatter.hpp

+34-2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ using is_format_pointer = std::conjunction<
3535
std::disjunction<std::is_pointer<T>, std::is_null_pointer<T>>,
3636
std::negation<detail::is_like_supported_string<T>>>;
3737

38+
/**
39+
* Helper trait to classify an enumeration type as default-formatted (i.e. the user has not defined
40+
* a custom operator<< for this type).
41+
*/
42+
template <typename StringType, typename T>
43+
using is_default_formatted_enum = std::conjunction<
44+
std::is_enum<T>,
45+
std::negation<typename BasicStringTraits<StringType>::OstreamTraits::template is_declared<T>>>;
46+
3847
/**
3948
* Class to format parameters according to a provided format string.
4049
*
@@ -92,7 +101,8 @@ class BasicStringFormatter
92101
fly::disable_if_any<
93102
detail::is_like_supported_string<T>,
94103
is_format_pointer<T>,
95-
std::is_arithmetic<T>> = 0>
104+
std::is_arithmetic<T>,
105+
is_default_formatted_enum<StringType, T>> = 0>
96106
void format_value(FormatSpecifier &&specifier, const T &value);
97107

98108
/**
@@ -165,6 +175,17 @@ class BasicStringFormatter
165175
template <typename T, fly::enable_if<std::is_same<T, bool>> = 0>
166176
void format_value(FormatSpecifier &&specifier, T value);
167177

178+
/**
179+
* Format a single replacement field with the provided enumeration.
180+
*
181+
* @tparam T The type of the value to format.
182+
*
183+
* @param specifier The replacement field to format.
184+
* @param value The value to format.
185+
*/
186+
template <typename T, fly::enable_if<is_default_formatted_enum<StringType, T>> = 0>
187+
void format_value(FormatSpecifier &&specifier, T value);
188+
168189
/**
169190
* Append a string-like value to the buffer.
170191
*
@@ -308,7 +329,8 @@ template <
308329
fly::disable_if_any<
309330
detail::is_like_supported_string<T>,
310331
is_format_pointer<T>,
311-
std::is_arithmetic<T>>>
332+
std::is_arithmetic<T>,
333+
is_default_formatted_enum<StringType, T>>>
312334
inline void BasicStringFormatter<StringType, ParameterTypes...>::format_value(
313335
FormatSpecifier &&specifier,
314336
const T &value)
@@ -604,6 +626,16 @@ inline void BasicStringFormatter<StringType, ParameterTypes...>::format_value(
604626
}
605627
}
606628

629+
//==================================================================================================
630+
template <typename StringType, typename... ParameterTypes>
631+
template <typename T, fly::enable_if<is_default_formatted_enum<StringType, T>>>
632+
inline void BasicStringFormatter<StringType, ParameterTypes...>::format_value(
633+
FormatSpecifier &&specifier,
634+
T value)
635+
{
636+
format_value(std::move(specifier), static_cast<std::underlying_type_t<T>>(value));
637+
}
638+
607639
//==================================================================================================
608640
template <typename StringType, typename... ParameterTypes>
609641
template <typename T, fly::enable_if<detail::is_like_supported_string<T>>>

fly/types/string/string.hpp

+14-6
Original file line numberDiff line numberDiff line change
@@ -413,13 +413,17 @@ class BasicString
413413
* from the format string type. If the type differs, the format parameter is transcoded to
414414
* the type of the format string.
415415
*
416-
* 3. Any general type for which an operator<< overload is defined will be converted to a
416+
* 3. Any generic type for which an operator<< overload is defined will be converted to a
417417
* string using that overload. Formatting options will then be applied to that string.
418418
*
419-
* 4. This implementation is exceptionless. Any error encountered (such as failed transcoding
419+
* 4. Formatting of strong enumeration types defaults to the format of the enumeration's
420+
* underlying type. However, if an overload of operator<< is defined, the type is treated
421+
* as a generic type according to (3) above.
422+
*
423+
* 5. This implementation is exceptionless. Any error encountered (such as failed transcoding
420424
* in (2) above) results in the format parameter that caused the error to be dropped.
421425
*
422-
* 5. Locale-specific form is not supported. If the option appears in the format string, it
426+
* 6. Locale-specific form is not supported. If the option appears in the format string, it
423427
* will be parsed, but will be ignored.
424428
*
425429
* The format string type is implicitly constructed from a C-string literal. Callers should only
@@ -469,13 +473,17 @@ class BasicString
469473
* from the format string type. If the type differs, the format parameter is transcoded to
470474
* the type of the format string.
471475
*
472-
* 3. Any general type for which an operator<< overload is defined will be converted to a
476+
* 3. Any generic type for which an operator<< overload is defined will be converted to a
473477
* string using that overload. Formatting options will then be applied to that string.
474478
*
475-
* 4. This implementation is exceptionless. Any error encountered (such as failed transcoding
479+
* 4. Formatting of strong enumeration types defaults to the format of the enumeration's
480+
* underlying type. However, if an overload of operator<< is defined, the type is treated
481+
* as a generic type according to (3) above.
482+
*
483+
* 5. This implementation is exceptionless. Any error encountered (such as failed transcoding
476484
* in (2) above) results in the format parameter that caused the error to be dropped.
477485
*
478-
* 5. Locale-specific form is not supported. If the option appears in the format string, it
486+
* 6. Locale-specific form is not supported. If the option appears in the format string, it
479487
* will be parsed, but will be ignored.
480488
*
481489
* The format string type is implicitly constructed from a C-string literal. Callers should only

test/types/string/string_formatter.cpp

+39
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,30 @@ StringType reserved_codepoint()
6262
return result;
6363
}
6464

65+
enum class DefaultFormattedEnum
66+
{
67+
One = 1,
68+
Two = 2,
69+
};
70+
71+
enum class UserFormattedEnum
72+
{
73+
One = 1,
74+
Two = 2,
75+
};
76+
77+
[[maybe_unused]] std::ostream &operator<<(std::ostream &stream, const UserFormattedEnum &u)
78+
{
79+
stream << (u == UserFormattedEnum::One ? "One" : "Two");
80+
return stream;
81+
}
82+
83+
[[maybe_unused]] std::wostream &operator<<(std::wostream &stream, const UserFormattedEnum &u)
84+
{
85+
stream << (u == UserFormattedEnum::One ? "One" : "Two");
86+
return stream;
87+
}
88+
6589
} // namespace
6690

6791
CATCH_TEMPLATE_TEST_CASE(
@@ -445,6 +469,10 @@ CATCH_TEMPLATE_TEST_CASE(
445469
test_format(FMT("{:c}"), FMT("a"), U'a');
446470
test_format(FMT("{:c}"), FMT("\n"), FLY_CHR(char_type, '\n'));
447471
test_format(FMT("{:c}"), FMT("a"), 0x61);
472+
test_format(
473+
FMT("{:c}"),
474+
StringType(1, static_cast<char_type>(DefaultFormattedEnum::One)),
475+
DefaultFormattedEnum::One);
448476
test_format(FMT("{:c}"), StringType(1, static_cast<char_type>(true)), true);
449477
test_format(FMT("{:c}"), StringType(1, static_cast<char_type>(false)), false);
450478
}
@@ -487,6 +515,9 @@ CATCH_TEMPLATE_TEST_CASE(
487515

488516
test_format(FMT("{:s}"), FMT("true"), true);
489517
test_format(FMT("{:s}"), FMT("false"), false);
518+
519+
test_format(FMT("{:s}"), FMT("One"), UserFormattedEnum::One);
520+
test_format(FMT("{:s}"), FMT("Two"), UserFormattedEnum::Two);
490521
}
491522

492523
CATCH_SECTION("Presentation type may be set (pointer)")
@@ -508,6 +539,8 @@ CATCH_TEMPLATE_TEST_CASE(
508539
test_format(FMT("{:b}"), FMT("1000001"), char8_t(0x41));
509540
test_format(FMT("{:b}"), FMT("1000001"), char16_t(0x41));
510541
test_format(FMT("{:b}"), FMT("1000001"), char32_t(0x41));
542+
test_format(FMT("{:b}"), FMT("1"), DefaultFormattedEnum::One);
543+
test_format(FMT("{:b}"), FMT("10"), DefaultFormattedEnum::Two);
511544

512545
test_format(FMT("{:b}"), FMT("11111111"), std::numeric_limits<std::uint8_t>::max());
513546
test_format(FMT("{:b}"), FMT("0"), std::numeric_limits<std::uint8_t>::min());
@@ -539,6 +572,8 @@ CATCH_TEMPLATE_TEST_CASE(
539572
test_format(FMT("{:o}"), FMT("101"), char8_t(0x41));
540573
test_format(FMT("{:o}"), FMT("101"), char16_t(0x41));
541574
test_format(FMT("{:o}"), FMT("101"), char32_t(0x41));
575+
test_format(FMT("{:o}"), FMT("1"), DefaultFormattedEnum::One);
576+
test_format(FMT("{:o}"), FMT("2"), DefaultFormattedEnum::Two);
542577

543578
test_format(FMT("{:o}"), FMT("377"), std::numeric_limits<std::uint8_t>::max());
544579
test_format(FMT("{:o}"), FMT("0"), std::numeric_limits<std::uint8_t>::min());
@@ -570,6 +605,8 @@ CATCH_TEMPLATE_TEST_CASE(
570605
test_format(FMT("{:d}"), FMT("65"), char8_t(0x41));
571606
test_format(FMT("{:d}"), FMT("65"), char16_t(0x41));
572607
test_format(FMT("{:d}"), FMT("65"), char32_t(0x41));
608+
test_format(FMT("{:d}"), FMT("1"), DefaultFormattedEnum::One);
609+
test_format(FMT("{:d}"), FMT("2"), DefaultFormattedEnum::Two);
573610

574611
test_format(FMT("{:d}"), FMT("255"), std::numeric_limits<std::uint8_t>::max());
575612
test_format(FMT("{:d}"), FMT("0"), std::numeric_limits<std::uint8_t>::min());
@@ -601,6 +638,8 @@ CATCH_TEMPLATE_TEST_CASE(
601638
test_format(FMT("{:x}"), FMT("41"), char8_t(0x41));
602639
test_format(FMT("{:x}"), FMT("41"), char16_t(0x41));
603640
test_format(FMT("{:x}"), FMT("41"), char32_t(0x41));
641+
test_format(FMT("{:x}"), FMT("1"), DefaultFormattedEnum::One);
642+
test_format(FMT("{:x}"), FMT("2"), DefaultFormattedEnum::Two);
604643

605644
test_format(FMT("{:X}"), FMT("BEEF"), 0xbeef);
606645

0 commit comments

Comments
 (0)