Cleanup compile-time checks

This commit is contained in:
Victor Zverovich 2024-08-31 10:08:08 -07:00
parent db496b47c1
commit 6797f0c39a
6 changed files with 40 additions and 39 deletions

View File

@ -122,7 +122,7 @@ hide:
</p> </p>
<p> <p>
The library is highly portable and requires only a minimal <b>subset of The library is highly portable and requires only a minimal <b>subset of
C++11</b> features which are available in GCC 4.8, Clang 3.4, MSVC 19.0 C++11</b> features which are available in GCC 4.9, Clang 3.4, MSVC 19.0
(2015) and later. Newer compiler and standard library features are used (2015) and later. Newer compiler and standard library features are used
if available, and enable additional functionality. if available, and enable additional functionality.
</p> </p>

View File

@ -2765,7 +2765,7 @@ FMT_CONSTEXPR FMT_INLINE auto parse_replacement_field(const Char* begin,
return begin + 1; return begin + 1;
} }
template <bool IS_CONSTEXPR, typename Char, typename Handler> template <bool IS_CONSTEXPR = true, typename Char, typename Handler>
FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str, FMT_CONSTEXPR void parse_format_string(basic_string_view<Char> format_str,
Handler&& handler) { Handler&& handler) {
auto begin = format_str.data(); auto begin = format_str.data();
@ -2938,21 +2938,6 @@ template <typename Char, typename... Args> class format_string_checker {
// A base class for compile-time strings. // A base class for compile-time strings.
struct compile_string {}; struct compile_string {};
template <typename S>
using is_compile_string = std::is_base_of<compile_string, S>;
// Reports a compile-time error if S is not a valid format string for T.
template <typename... T, typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
void check_format_string(S fmt) {
using char_type = typename S::char_type;
FMT_CONSTEXPR auto s = basic_string_view<char_type>(fmt);
using checker = format_string_checker<char_type, remove_cvref_t<T>...>;
FMT_CONSTEXPR bool error = (parse_format_string<true>(s, checker(s)), true);
ignore_unused(error);
}
template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
FMT_ALWAYS_INLINE void check_format_string(S) {}
// Use vformat_args and avoid type_identity to keep symbols short. // Use vformat_args and avoid type_identity to keep symbols short.
template <typename Char = char> struct vformat_args { template <typename Char = char> struct vformat_args {
using type = basic_format_args<buffered_context<Char>>; using type = basic_format_args<buffered_context<Char>>;
@ -3027,36 +3012,49 @@ template <typename Char, typename... Args> class basic_format_string {
private: private:
basic_string_view<Char> str_; basic_string_view<Char> str_;
public: using checker = detail::format_string_checker<Char, remove_cvref_t<Args>...>;
template <
typename S, template <typename S>
FMT_ENABLE_IF( FMT_CONSTEXPR FMT_ALWAYS_INLINE void check(const S& s) {
std::is_convertible<const S&, basic_string_view<Char>>::value ||
(detail::is_compile_string<S>::value &&
std::is_constructible<basic_string_view<Char>, const S&>::value))>
FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_format_string(const S& s) : str_(s) {
static_assert( static_assert(
detail::count< detail::count<
(std::is_base_of<detail::view, remove_reference_t<Args>>::value && (std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
std::is_reference<Args>::value)...>() == 0, std::is_reference<Args>::value)...>() == 0,
"passing views as lvalues is disallowed"); "passing views as lvalues is disallowed");
detail::ignore_unused(s);
#if FMT_USE_CONSTEVAL #if FMT_USE_CONSTEVAL
if constexpr (detail::count_named_args<Args...>() == if constexpr (detail::count_named_args<Args...>() ==
detail::count_statically_named_args<Args...>()) { detail::count_statically_named_args<Args...>()) {
using checker = detail::parse_format_string(str_, checker(str_));
detail::format_string_checker<Char, remove_cvref_t<Args>...>;
detail::parse_format_string<true>(str_, checker(s));
} }
#else #endif
# ifdef FMT_ENFORCE_COMPILE_STRING }
public:
// Reports a compile-time error if S is not a valid format string for Args.
template <typename S,
FMT_ENABLE_IF(
std::is_convertible<const S&, basic_string_view<Char>>::value)>
FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_format_string(const S& s) : str_(s) {
check(s);
#ifdef FMT_ENFORCE_COMPILE_STRING
static_assert( static_assert(
detail::is_compile_string<S>::value, FMT_USE_CONSTEVAL && sizeof(S) != 0,
"FMT_ENFORCE_COMPILE_STRING requires all format strings to use " "FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
"FMT_STRING."); "FMT_STRING.");
# endif
detail::check_format_string<Args...>(s);
#endif #endif
} }
template <typename S,
FMT_ENABLE_IF(std::is_base_of<detail::compile_string, S>::value&&
std::is_constructible<basic_string_view<Char>,
const S&>::value)>
FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_format_string(const S& s) : str_(s) {
check(s);
if (FMT_USE_CONSTEVAL) return;
FMT_CONSTEXPR auto v = basic_string_view<Char>(S());
FMT_CONSTEXPR int ignore = (detail::parse_format_string(v, checker(v)), 0);
detail::ignore_unused(ignore);
}
basic_format_string(runtime_format_string<Char> fmt) : str_(fmt.str) {} basic_format_string(runtime_format_string<Char> fmt) : str_(fmt.str) {}
FMT_ALWAYS_INLINE operator basic_string_view<Char>() const { return str_; } FMT_ALWAYS_INLINE operator basic_string_view<Char>() const { return str_; }

View File

@ -36,7 +36,7 @@ struct is_compiled_string : std::is_base_of<compiled_string, S> {};
* std::string s = fmt::format(FMT_COMPILE("{}"), 42); * std::string s = fmt::format(FMT_COMPILE("{}"), 42);
*/ */
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
# define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string, explicit) # define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string)
#else #else
# define FMT_COMPILE(s) FMT_STRING(s) # define FMT_COMPILE(s) FMT_STRING(s)
#endif #endif

View File

@ -1869,7 +1869,7 @@ inline auto find_escape(const char* begin, const char* end)
return result; return result;
} }
#define FMT_STRING_IMPL(s, base, explicit) \ #define FMT_STRING_IMPL(s, base) \
[] { \ [] { \
/* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \
/* Use a macro-like name to avoid shadowing warnings. */ \ /* Use a macro-like name to avoid shadowing warnings. */ \
@ -1880,6 +1880,8 @@ inline auto find_escape(const char* begin, const char* end)
return fmt::detail_exported::compile_string_to_view<char_type>(s); \ return fmt::detail_exported::compile_string_to_view<char_type>(s); \
} \ } \
}; \ }; \
typename FMT_COMPILE_STRING::char_type FMT_CHAR = {}; \
(void)FMT_CHAR; \
return FMT_COMPILE_STRING(); \ return FMT_COMPILE_STRING(); \
}() }()
@ -1891,7 +1893,7 @@ inline auto find_escape(const char* begin, const char* end)
* // A compile-time error because 'd' is an invalid specifier for strings. * // A compile-time error because 'd' is an invalid specifier for strings.
* std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); * std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
*/ */
#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string, ) #define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string)
template <size_t width, typename Char, typename OutputIt> template <size_t width, typename Char, typename OutputIt>
auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt { auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt {

View File

@ -34,7 +34,8 @@ struct format_string_char<
}; };
template <typename S> template <typename S>
struct format_string_char<S, enable_if_t<is_compile_string<S>::value>> { struct format_string_char<
S, enable_if_t<std::is_base_of<detail::compile_string, S>::value>> {
using type = typename S::char_type; using type = typename S::char_type;
}; };

View File

@ -2203,7 +2203,7 @@ TEST(format_test, vformat_to) {
fmt::vformat_to(std::back_inserter(s), "{}", args); fmt::vformat_to(std::back_inserter(s), "{}", args);
EXPECT_EQ(s, "42"); EXPECT_EQ(s, "42");
s.clear(); s.clear();
fmt::vformat_to(std::back_inserter(s), FMT_STRING("{}"), args); fmt::vformat_to(std::back_inserter(s), "{}", args);
EXPECT_EQ(s, "42"); EXPECT_EQ(s, "42");
} }