Skip to content

Commit fa388f8

Browse files
committed
Type-erase standard format parameters
This will be needed to get back the original type T of the format parameter when forming Formatter<T>.
1 parent c3f24f9 commit fa388f8

File tree

4 files changed

+198
-61
lines changed

4 files changed

+198
-61
lines changed

fly/types/string/detail/format_parameters.hpp

+139-34
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,26 @@ struct StringValue
4242
void (*m_format)(const void *value, std::size_t size, FormatContext &context);
4343
};
4444

45+
/**
46+
* Structure to store a type-erased standard value.
47+
*/
48+
template <typename FormatContext>
49+
struct StandardValue
50+
{
51+
union
52+
{
53+
const void *m_pointer;
54+
std::int64_t m_signed_int;
55+
std::uint64_t m_unsigned_int;
56+
float m_float;
57+
double m_double;
58+
long double m_long_double;
59+
bool m_bool;
60+
};
61+
62+
void (*m_format)(StandardValue value, FormatContext &context);
63+
};
64+
4565
/**
4666
* Re-form a type-erased user-defined value and format that value.
4767
*
@@ -67,6 +87,18 @@ void format_user_defined_value(const void *value, FormatContext &context);
6787
template <typename FormatContext, typename T>
6888
void format_string_value(const void *value, std::size_t size, FormatContext &context);
6989

90+
/**
91+
* Re-form a type-erased standard value and format that value.
92+
*
93+
* @tparam FormatContext The formatting context type.
94+
* @tparam T The standrd type.
95+
*
96+
* @param value The container holding the type-erased value.
97+
* @param context The context holding the formatting state.
98+
*/
99+
template <typename FormatContext, typename T>
100+
void format_standard_value(StandardValue<FormatContext> value, FormatContext &context);
101+
70102
/**
71103
* A container to hold a single type-erased format parameter.
72104
*
@@ -139,6 +171,13 @@ class BasicFormatParameter
139171
template <typename T, fly::enable_if<BasicFormatTraits::is_default_formatted_enum<T>> = 0>
140172
explicit constexpr BasicFormatParameter(T value) noexcept;
141173

174+
/**
175+
* Apply the type-erased formatting function to the stored format parameter.
176+
*
177+
* @param context The context holding the formatting state.
178+
*/
179+
constexpr void format(FormatContext &context) const;
180+
142181
/**
143182
* Apply the provided visitor to the stored format parameter.
144183
*
@@ -174,13 +213,7 @@ class BasicFormatParameter
174213
MonoState m_monostate;
175214
UserDefinedValue<FormatContext> m_generic;
176215
StringValue<FormatContext> m_string;
177-
const void *m_pointer;
178-
std::int64_t m_signed_int;
179-
std::uint64_t m_unsigned_int;
180-
float m_float;
181-
double m_double;
182-
long double m_long_double;
183-
bool m_bool;
216+
StandardValue<FormatContext> m_standard;
184217
};
185218

186219
Type m_type;
@@ -246,6 +279,53 @@ inline void format_string_value(const void *value, std::size_t size, FormatConte
246279
Formatter<decltype(view), typename FormatContext::char_type>().format(view, context);
247280
}
248281

282+
//==================================================================================================
283+
template <typename FormatContext, typename T>
284+
inline void format_standard_value(StandardValue<FormatContext> value, FormatContext &context)
285+
{
286+
Formatter<T, typename FormatContext::char_type> formatter {};
287+
288+
if constexpr (BasicFormatTraits::is_pointer_v<T>)
289+
{
290+
if constexpr (std::is_null_pointer_v<T>)
291+
{
292+
formatter.format(nullptr, context);
293+
}
294+
else if constexpr (std::is_const_v<T>)
295+
{
296+
formatter.format(static_cast<T>(value.m_pointer), context);
297+
}
298+
else
299+
{
300+
formatter.format(static_cast<T>(const_cast<void *>(value.m_pointer)), context);
301+
}
302+
}
303+
else if constexpr (std::is_same_v<T, float>)
304+
{
305+
formatter.format(value.m_float, context);
306+
}
307+
else if constexpr (std::is_same_v<T, double>)
308+
{
309+
formatter.format(value.m_double, context);
310+
}
311+
else if constexpr (std::is_same_v<T, long double>)
312+
{
313+
formatter.format(value.m_long_double, context);
314+
}
315+
else if constexpr (std::is_same_v<T, bool>)
316+
{
317+
formatter.format(value.m_bool, context);
318+
}
319+
else if constexpr (std::is_signed_v<T>)
320+
{
321+
formatter.format(static_cast<T>(value.m_signed_int), context);
322+
}
323+
else
324+
{
325+
formatter.format(static_cast<T>(value.m_unsigned_int), context);
326+
}
327+
}
328+
249329
//==================================================================================================
250330
template <typename FormatContext>
251331
constexpr inline BasicFormatParameter<FormatContext>::BasicFormatParameter() noexcept :
@@ -305,7 +385,7 @@ template <typename FormatContext>
305385
template <typename T, fly::enable_if<BasicFormatTraits::is_pointer<T>>>
306386
constexpr inline BasicFormatParameter<FormatContext>::BasicFormatParameter(T value) noexcept :
307387
m_type(Type::Pointer),
308-
m_value {.m_pointer {value}}
388+
m_value {.m_standard {.m_pointer {value}, .m_format {format_standard_value<FormatContext, T>}}}
309389
{
310390
}
311391

@@ -314,38 +394,37 @@ template <typename FormatContext>
314394
template <typename T, fly::enable_if<std::is_arithmetic<T>>>
315395
constexpr inline BasicFormatParameter<FormatContext>::BasicFormatParameter(T value) noexcept
316396
{
317-
if constexpr (std::is_floating_point_v<T>)
397+
m_value.m_standard.m_format = format_standard_value<FormatContext, T>;
398+
399+
if constexpr (std::is_same_v<T, float>)
318400
{
319-
if constexpr (std::is_same_v<T, float>)
320-
{
321-
m_type = Type::Float;
322-
m_value.m_float = value;
323-
}
324-
else if constexpr (std::is_same_v<T, double>)
325-
{
326-
m_type = Type::Double;
327-
m_value.m_double = value;
328-
}
329-
else if constexpr (std::is_same_v<T, long double>)
330-
{
331-
m_type = Type::LongDouble;
332-
m_value.m_long_double = value;
333-
}
401+
m_type = Type::Float;
402+
m_value.m_standard.m_float = value;
403+
}
404+
else if constexpr (std::is_same_v<T, double>)
405+
{
406+
m_type = Type::Double;
407+
m_value.m_standard.m_double = value;
408+
}
409+
else if constexpr (std::is_same_v<T, long double>)
410+
{
411+
m_type = Type::LongDouble;
412+
m_value.m_standard.m_long_double = value;
334413
}
335414
else if constexpr (std::is_same_v<T, bool>)
336415
{
337416
m_type = Type::Bool;
338-
m_value.m_bool = value;
417+
m_value.m_standard.m_bool = value;
339418
}
340419
else if constexpr (std::is_signed_v<T>)
341420
{
342421
m_type = Type::SignedInt;
343-
m_value.m_signed_int = value;
422+
m_value.m_standard.m_signed_int = value;
344423
}
345424
else
346425
{
347426
m_type = Type::UnsignedInt;
348-
m_value.m_unsigned_int = value;
427+
m_value.m_standard.m_unsigned_int = value;
349428
}
350429
}
351430

@@ -357,6 +436,32 @@ constexpr inline BasicFormatParameter<FormatContext>::BasicFormatParameter(T val
357436
{
358437
}
359438

439+
//==================================================================================================
440+
template <typename FormatContext>
441+
constexpr inline void BasicFormatParameter<FormatContext>::format(FormatContext &context) const
442+
{
443+
switch (m_type)
444+
{
445+
case Type::Generic:
446+
m_value.m_generic.m_format(m_value.m_generic.m_value, context);
447+
break;
448+
case Type::String:
449+
m_value.m_string.m_format(m_value.m_string.m_value, m_value.m_string.m_size, context);
450+
break;
451+
case Type::Pointer:
452+
case Type::SignedInt:
453+
case Type::UnsignedInt:
454+
case Type::Float:
455+
case Type::Double:
456+
case Type::LongDouble:
457+
case Type::Bool:
458+
m_value.m_standard.m_format(m_value.m_standard, context);
459+
break;
460+
default:
461+
break;
462+
}
463+
}
464+
360465
//==================================================================================================
361466
template <typename FormatContext>
362467
template <typename Visitor>
@@ -369,19 +474,19 @@ constexpr inline auto BasicFormatParameter<FormatContext>::visit(Visitor &&visit
369474
case Type::String:
370475
return visitor(m_value.m_string);
371476
case Type::Pointer:
372-
return visitor(m_value.m_pointer);
477+
return visitor(m_value.m_standard.m_pointer);
373478
case Type::SignedInt:
374-
return visitor(m_value.m_signed_int);
479+
return visitor(m_value.m_standard.m_signed_int);
375480
case Type::UnsignedInt:
376-
return visitor(m_value.m_unsigned_int);
481+
return visitor(m_value.m_standard.m_unsigned_int);
377482
case Type::Float:
378-
return visitor(m_value.m_float);
483+
return visitor(m_value.m_standard.m_float);
379484
case Type::Double:
380-
return visitor(m_value.m_double);
485+
return visitor(m_value.m_standard.m_double);
381486
case Type::LongDouble:
382-
return visitor(m_value.m_long_double);
487+
return visitor(m_value.m_standard.m_long_double);
383488
case Type::Bool:
384-
return visitor(m_value.m_bool);
489+
return visitor(m_value.m_standard.m_bool);
385490
default:
386491
return visitor(m_value.m_monostate);
387492
}

fly/types/string/string.hpp

+7-23
Original file line numberDiff line numberDiff line change
@@ -872,24 +872,6 @@ StringType BasicString<StringType>::format(
872872
detail::make_format_parameters<FormatContext>(std::forward<ParameterTypes>(parameters)...);
873873
FormatContext context(std::back_inserter(formatted), params);
874874

875-
auto format_value = [&context](auto value)
876-
{
877-
using T = std::remove_cvref_t<decltype(value)>;
878-
879-
if constexpr (std::is_same_v<T, detail::UserDefinedValue<FormatContext>>)
880-
{
881-
value.m_format(value.m_value, context);
882-
}
883-
else if constexpr (std::is_same_v<T, detail::StringValue<FormatContext>>)
884-
{
885-
value.m_format(value.m_value, value.m_size, context);
886-
}
887-
else
888-
{
889-
Formatter<T, char_type>().format(value, context);
890-
}
891-
};
892-
893875
for (std::size_t pos = 0; pos < view.size();)
894876
{
895877
switch (const auto &ch = view[pos])
@@ -899,13 +881,15 @@ StringType BasicString<StringType>::format(
899881
{
900882
*context.out()++ = ch;
901883
pos += 2;
902-
break;
903884
}
885+
else
886+
{
887+
context.spec() = *std::move(fmt.next_specifier());
888+
pos += context.spec().m_size;
904889

905-
context.spec() = *std::move(fmt.next_specifier());
906-
context.arg(context.spec().m_position).visit(format_value);
907-
908-
pos += context.spec().m_size;
890+
const auto parameter = context.arg(context.spec().m_position);
891+
parameter.format(context);
892+
}
909893
break;
910894

911895
case s_right_brace:

test/types/string/format_parameters.cpp

+33-3
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ CATCH_TEMPLATE_TEST_CASE(
5757
{
5858
CATCH_CHECK(std::is_same_v<decltype(value), fly::detail::MonoState>);
5959
});
60+
61+
parameter.format(context);
6062
}
6163

6264
CATCH_SECTION("A single parameter can be visited, but no others")
@@ -135,6 +137,34 @@ CATCH_TEMPLATE_TEST_CASE(
135137
CATCH_CHECK_FALSE(context.arg(3));
136138
}
137139

140+
CATCH_SECTION("Pointers are coerced to the appropriate type")
141+
{
142+
int i = 0;
143+
144+
std::nullptr_t p1 = nullptr;
145+
void *p2 = &i;
146+
const void *p3 = &i;
147+
148+
auto params = fly::detail::make_format_parameters<FormatContext>(p1, p2, p3);
149+
FormatContext context(out, params);
150+
151+
auto verify = [](auto expected_value, auto actual_value)
152+
{
153+
CATCH_CHECK(std::is_same_v<decltype(actual_value), const void *>);
154+
155+
if constexpr (std::is_same_v<decltype(actual_value), const void *>)
156+
{
157+
CATCH_CHECK(static_cast<const void *>(expected_value) == actual_value);
158+
}
159+
};
160+
161+
context.arg(0).visit(std::bind(verify, p1, std::placeholders::_1));
162+
context.arg(1).visit(std::bind(verify, p2, std::placeholders::_1));
163+
context.arg(2).visit(std::bind(verify, p3, std::placeholders::_1));
164+
165+
CATCH_CHECK_FALSE(context.arg(3));
166+
}
167+
138168
CATCH_SECTION("Floating-point values are coerced to the appropriate type")
139169
{
140170
float f = 3.14f;
@@ -200,7 +230,7 @@ CATCH_TEMPLATE_TEST_CASE(
200230

201231
if constexpr (std::is_same_v<decltype(actual_value), std::int64_t>)
202232
{
203-
CATCH_CHECK(static_cast<decltype(expected_value)>(actual_value) == actual_value);
233+
CATCH_CHECK(static_cast<decltype(expected_value)>(actual_value) == expected_value);
204234
}
205235
};
206236

@@ -228,7 +258,7 @@ CATCH_TEMPLATE_TEST_CASE(
228258

229259
if constexpr (std::is_same_v<decltype(actual_value), std::uint64_t>)
230260
{
231-
CATCH_CHECK(static_cast<decltype(expected_value)>(actual_value) == actual_value);
261+
CATCH_CHECK(static_cast<decltype(expected_value)>(actual_value) == expected_value);
232262
}
233263
};
234264

@@ -254,7 +284,7 @@ CATCH_TEMPLATE_TEST_CASE(
254284

255285
if constexpr (std::is_same_v<decltype(actual_value), bool>)
256286
{
257-
CATCH_CHECK(static_cast<decltype(expected_value)>(actual_value) == actual_value);
287+
CATCH_CHECK(static_cast<decltype(expected_value)>(actual_value) == expected_value);
258288
}
259289
};
260290

0 commit comments

Comments
 (0)