Skip to content

Commit 2b27531

Browse files
committed
Use C++20 concepts more pervasively in string library
The main caveat is in the Formatter<T, CharType> specializations. Due to a clang bug, the specializations cannot separate forward declarations of their |format| method from their implementations. For example: template <typename T, typename CharType = char> struct Formatter; template <detail::FormattableString T, typename CharType> struct Formatter<T, CharType> { void format(const T &); }; template <detail::FormattableString T, typename CharType> void Formatter<T, CharType>::format(const T &) { } This raises a compile error about defining the format method without a declaration. See: https://stackoverflow.com/a/65670768 https://bugs.llvm.org/show_bug.cgi?id=48020
1 parent ea00050 commit 2b27531

7 files changed

+799
-762
lines changed

fly/types/string/detail/classifier.hpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#pragma once
22

3-
#include "fly/traits/traits.hpp"
3+
#include "fly/types/string/detail/string_concepts.hpp"
44
#include "fly/types/string/detail/string_traits.hpp"
55
#include "fly/types/string/literals.hpp"
66

@@ -33,7 +33,7 @@ class BasicClassifier
3333
*
3434
* @return The length of the string-like value.
3535
*/
36-
template <typename T, enable_if<detail::is_like_supported_string<T>> = 0>
36+
template <StringLike T>
3737
static constexpr size_type size(T &&value);
3838

3939
/**
@@ -187,7 +187,7 @@ class BasicClassifier
187187

188188
//==================================================================================================
189189
template <typename CharType>
190-
template <typename T, enable_if<detail::is_like_supported_string<T>>>
190+
template <StringLike T>
191191
constexpr inline auto BasicClassifier<CharType>::size(T &&value) -> size_type
192192
{
193193
using U = std::remove_cvref_t<T>;

fly/types/string/detail/format_parameter_type.hpp

+7-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include "fly/types/string/detail/string_concepts.hpp"
34
#include "fly/types/string/detail/string_traits.hpp"
45

56
#include <cstdint>
@@ -30,29 +31,27 @@ enum class ParameterType : std::uint8_t
3031
template <typename T>
3132
constexpr ParameterType infer_parameter_type()
3233
{
33-
using U = std::remove_cvref_t<T>;
34-
35-
if constexpr (is_supported_character_v<U>)
34+
if constexpr (is_supported_character_v<T>)
3635
{
3736
return ParameterType::Character;
3837
}
39-
else if constexpr (is_like_supported_string_v<U>)
38+
else if constexpr (FormattableString<T>)
4039
{
4140
return ParameterType::String;
4241
}
43-
else if constexpr (std::is_pointer_v<U> || std::is_null_pointer_v<U>)
42+
else if constexpr (FormattablePointer<T>)
4443
{
4544
return ParameterType::Pointer;
4645
}
47-
else if constexpr (BasicFormatTraits::is_integer_v<U>)
46+
else if constexpr (FormattableIntegral<T>)
4847
{
4948
return ParameterType::Integral;
5049
}
51-
else if constexpr (std::is_floating_point_v<U>)
50+
else if constexpr (FormattableFloatingPoint<T>)
5251
{
5352
return ParameterType::FloatingPoint;
5453
}
55-
else if constexpr (std::is_same_v<U, bool>)
54+
else if constexpr (FormattableBoolean<T>)
5655
{
5756
return ParameterType::Boolean;
5857
}

fly/types/string/detail/format_parameters.hpp

+76-49
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#pragma once
22

3-
#include "fly/traits/traits.hpp"
43
#include "fly/types/string/detail/classifier.hpp"
54
#include "fly/types/string/detail/format_parse_context.hpp"
65
#include "fly/types/string/detail/format_specifier.hpp"
@@ -9,6 +8,7 @@
98
#include "fly/types/string/formatters.hpp"
109

1110
#include <array>
11+
#include <concepts>
1212
#include <cstdint>
1313
#include <string_view>
1414
#include <type_traits>
@@ -153,7 +153,7 @@ class BasicFormatParameter
153153
*
154154
* @param value The user-defined value.
155155
*/
156-
template <typename T, fly::enable_if<BasicFormatTraits::is_user_defined<T>> = 0>
156+
template <FormattableUserDefined T>
157157
explicit constexpr BasicFormatParameter(const T &value) noexcept;
158158

159159
/**
@@ -164,7 +164,7 @@ class BasicFormatParameter
164164
*
165165
* @param value The string-like value.
166166
*/
167-
template <typename T, fly::enable_if<fly::detail::is_like_supported_string<T>> = 0>
167+
template <FormattableString T>
168168
explicit constexpr BasicFormatParameter(const T &value) noexcept;
169169

170170
/**
@@ -174,17 +174,37 @@ class BasicFormatParameter
174174
*
175175
* @param value The pointer value.
176176
*/
177-
template <typename T, fly::enable_if<BasicFormatTraits::is_pointer<T>> = 0>
177+
template <FormattablePointer T>
178178
explicit constexpr BasicFormatParameter(T value) noexcept;
179179

180180
/**
181-
* Constructor. Initialize the format parameter to store an arithmetic value.
181+
* Constructor. Initialize the format parameter to store an integral value.
182182
*
183-
* @tparam T The arithmetic type.
183+
* @tparam T The integral type.
184184
*
185-
* @param value The arithmetic value.
185+
* @param value The integral value.
186186
*/
187-
template <typename T, fly::enable_if<std::is_arithmetic<T>> = 0>
187+
template <FormattableIntegral T>
188+
explicit constexpr BasicFormatParameter(T value) noexcept;
189+
190+
/**
191+
* Constructor. Initialize the format parameter to store a floating-point value.
192+
*
193+
* @tparam T The floating-point type.
194+
*
195+
* @param value The floating-point value.
196+
*/
197+
template <FormattableFloatingPoint T>
198+
explicit constexpr BasicFormatParameter(T value) noexcept;
199+
200+
/**
201+
* Constructor. Initialize the format parameter to store a boolean value.
202+
*
203+
* @tparam T The boolean type.
204+
*
205+
* @param value The boolean value.
206+
*/
207+
template <FormattableBoolean T>
188208
explicit constexpr BasicFormatParameter(T value) noexcept;
189209

190210
/**
@@ -274,13 +294,9 @@ class BasicFormatParameters
274294
*
275295
* @param parameters The format parameters to store.
276296
*/
277-
template <typename FormatContext, typename... ParameterTypes>
278-
constexpr inline auto make_format_parameters(ParameterTypes &&...parameters)
297+
template <typename FormatContext, Formattable<FormatContext>... ParameterTypes>
298+
constexpr auto make_format_parameters(ParameterTypes &&...parameters)
279299
{
280-
static_assert(
281-
(BasicFormatTraits::has_formatter_v<FormatContext, ParameterTypes> && ...),
282-
"A specialization of fly::Formatter<T, CharType> is required for all format parameters");
283-
284300
return BasicFormatParameters<FormatContext, ParameterTypes...> {
285301
std::forward<ParameterTypes>(parameters)...};
286302
}
@@ -298,7 +314,7 @@ inline void format_user_defined_value(
298314
Formatter formatter;
299315
parse_context.lexer().set_position(specifier.m_parse_index);
300316

301-
if constexpr (FormatterWithParsing<decltype(parse_context), Formatter>)
317+
if constexpr (FormattableWithParsing<decltype(parse_context), Formatter>)
302318
{
303319
formatter.parse(parse_context);
304320
}
@@ -342,7 +358,7 @@ inline void format_standard_value(
342358
{
343359
typename FormatContext::template formatter_type<T> formatter(std::move(specifier));
344360

345-
if constexpr (BasicFormatTraits::is_pointer_v<T>)
361+
if constexpr (FormattablePointer<T>)
346362
{
347363
if constexpr (std::is_null_pointer_v<T>)
348364
{
@@ -357,19 +373,19 @@ inline void format_standard_value(
357373
formatter.format(static_cast<T>(const_cast<void *>(value.m_pointer)), context);
358374
}
359375
}
360-
else if constexpr (std::is_same_v<T, float>)
376+
else if constexpr (std::same_as<T, float>)
361377
{
362378
formatter.format(value.m_float, context);
363379
}
364-
else if constexpr (std::is_same_v<T, double>)
380+
else if constexpr (std::same_as<T, double>)
365381
{
366382
formatter.format(value.m_double, context);
367383
}
368-
else if constexpr (std::is_same_v<T, long double>)
384+
else if constexpr (std::same_as<T, long double>)
369385
{
370386
formatter.format(value.m_long_double, context);
371387
}
372-
else if constexpr (std::is_same_v<T, bool>)
388+
else if constexpr (std::same_as<T, bool>)
373389
{
374390
formatter.format(value.m_bool, context);
375391
}
@@ -385,27 +401,25 @@ inline void format_standard_value(
385401

386402
//==================================================================================================
387403
template <typename FormatContext>
388-
constexpr inline BasicFormatParameter<FormatContext>::BasicFormatParameter() noexcept :
404+
constexpr BasicFormatParameter<FormatContext>::BasicFormatParameter() noexcept :
389405
m_type(Type::Invalid),
390406
m_value {.m_monostate {}}
391407
{
392408
}
393409

394410
//==================================================================================================
395411
template <typename FormatContext>
396-
template <typename T, fly::enable_if<BasicFormatTraits::is_user_defined<T>>>
397-
constexpr inline BasicFormatParameter<FormatContext>::BasicFormatParameter(const T &value) noexcept
398-
:
412+
template <FormattableUserDefined T>
413+
constexpr BasicFormatParameter<FormatContext>::BasicFormatParameter(const T &value) noexcept :
399414
m_type(Type::UserDefined),
400415
m_value {.m_user_defined {&value, format_user_defined_value<FormatContext, T>}}
401416
{
402417
}
403418

404419
//==================================================================================================
405420
template <typename FormatContext>
406-
template <typename T, fly::enable_if<fly::detail::is_like_supported_string<T>>>
407-
constexpr inline BasicFormatParameter<FormatContext>::BasicFormatParameter(const T &value) noexcept
408-
:
421+
template <FormattableString T>
422+
constexpr BasicFormatParameter<FormatContext>::BasicFormatParameter(const T &value) noexcept :
409423
m_type(Type::String)
410424
{
411425
using U = std::remove_cvref_t<T>;
@@ -433,17 +447,36 @@ constexpr inline BasicFormatParameter<FormatContext>::BasicFormatParameter(const
433447

434448
//==================================================================================================
435449
template <typename FormatContext>
436-
template <typename T, fly::enable_if<BasicFormatTraits::is_pointer<T>>>
437-
constexpr inline BasicFormatParameter<FormatContext>::BasicFormatParameter(T value) noexcept :
450+
template <FormattablePointer T>
451+
constexpr BasicFormatParameter<FormatContext>::BasicFormatParameter(T value) noexcept :
438452
m_type(Type::Pointer),
439453
m_value {.m_standard {.m_pointer {value}, .m_format {format_standard_value<FormatContext, T>}}}
440454
{
441455
}
442456

443457
//==================================================================================================
444458
template <typename FormatContext>
445-
template <typename T, fly::enable_if<std::is_arithmetic<T>>>
446-
constexpr inline BasicFormatParameter<FormatContext>::BasicFormatParameter(T value) noexcept
459+
template <FormattableIntegral T>
460+
constexpr BasicFormatParameter<FormatContext>::BasicFormatParameter(T value) noexcept
461+
{
462+
m_value.m_standard.m_format = format_standard_value<FormatContext, T>;
463+
464+
if constexpr (std::is_signed_v<T>)
465+
{
466+
m_type = Type::SignedInt;
467+
m_value.m_standard.m_signed_int = value;
468+
}
469+
else
470+
{
471+
m_type = Type::UnsignedInt;
472+
m_value.m_standard.m_unsigned_int = value;
473+
}
474+
}
475+
476+
//==================================================================================================
477+
template <typename FormatContext>
478+
template <FormattableFloatingPoint T>
479+
constexpr BasicFormatParameter<FormatContext>::BasicFormatParameter(T value) noexcept
447480
{
448481
m_value.m_standard.m_format = format_standard_value<FormatContext, T>;
449482

@@ -462,26 +495,20 @@ constexpr inline BasicFormatParameter<FormatContext>::BasicFormatParameter(T val
462495
m_type = Type::LongDouble;
463496
m_value.m_standard.m_long_double = value;
464497
}
465-
else if constexpr (std::is_same_v<T, bool>)
466-
{
467-
m_type = Type::Bool;
468-
m_value.m_standard.m_bool = value;
469-
}
470-
else if constexpr (std::is_signed_v<T>)
471-
{
472-
m_type = Type::SignedInt;
473-
m_value.m_standard.m_signed_int = value;
474-
}
475-
else
476-
{
477-
m_type = Type::UnsignedInt;
478-
m_value.m_standard.m_unsigned_int = value;
479-
}
480498
}
481499

482500
//==================================================================================================
483501
template <typename FormatContext>
484-
constexpr inline void BasicFormatParameter<FormatContext>::format(
502+
template <FormattableBoolean T>
503+
constexpr BasicFormatParameter<FormatContext>::BasicFormatParameter(T value) noexcept :
504+
m_type(Type::Bool),
505+
m_value {.m_standard {.m_bool {value}, .m_format {format_standard_value<FormatContext, T>}}}
506+
{
507+
}
508+
509+
//==================================================================================================
510+
template <typename FormatContext>
511+
constexpr void BasicFormatParameter<FormatContext>::format(
485512
BasicFormatParseContext<typename FormatContext::char_type> &parse_context,
486513
FormatContext &context,
487514
BasicFormatSpecifier<char_type> &&specifier) const
@@ -519,7 +546,7 @@ constexpr inline void BasicFormatParameter<FormatContext>::format(
519546
//==================================================================================================
520547
template <typename FormatContext>
521548
template <typename Visitor>
522-
constexpr inline auto BasicFormatParameter<FormatContext>::visit(Visitor &&visitor) const
549+
constexpr auto BasicFormatParameter<FormatContext>::visit(Visitor &&visitor) const
523550
{
524551
switch (m_type)
525552
{
@@ -555,7 +582,7 @@ inline BasicFormatParameter<FormatContext>::operator bool() const noexcept
555582

556583
//==================================================================================================
557584
template <typename FormatContext, typename... ParameterTypes>
558-
constexpr inline BasicFormatParameters<FormatContext, ParameterTypes...>::BasicFormatParameters(
585+
constexpr BasicFormatParameters<FormatContext, ParameterTypes...>::BasicFormatParameters(
559586
ParameterTypes &&...parameters) noexcept :
560587
m_parameters {FormatParameter {std::forward<ParameterTypes>(parameters)}...}
561588
{

fly/types/string/detail/format_specifier.hpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include "fly/traits/concepts.hpp"
34
#include "fly/types/string/detail/classifier.hpp"
45
#include "fly/types/string/detail/format_parameter_type.hpp"
56
#include "fly/types/string/detail/format_parse_context.hpp"
@@ -852,11 +853,11 @@ BasicFormatSpecifier<CharType>::resolve(FormatContext &context, std::size_t posi
852853
using T = std::remove_cvref_t<decltype(value)>;
853854
std::optional<std::size_t> resolved;
854855

855-
if constexpr (std::is_unsigned_v<T>)
856+
if constexpr (fly::UnsignedIntegral<T>)
856857
{
857858
resolved = static_cast<std::size_t>(value);
858859
}
859-
else if constexpr (std::is_integral_v<T>)
860+
else if constexpr (fly::SignedIntegral<T>)
860861
{
861862
if (value >= 0)
862863
{

0 commit comments

Comments
 (0)