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>
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
if available, and enable additional functionality.
</p>

View File

@ -2765,7 +2765,7 @@ FMT_CONSTEXPR FMT_INLINE auto parse_replacement_field(const Char* begin,
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,
Handler&& handler) {
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.
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.
template <typename Char = char> struct vformat_args {
using type = basic_format_args<buffered_context<Char>>;
@ -3027,35 +3012,48 @@ template <typename Char, typename... Args> class basic_format_string {
private:
basic_string_view<Char> str_;
public:
template <
typename S,
FMT_ENABLE_IF(
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) {
using checker = detail::format_string_checker<Char, remove_cvref_t<Args>...>;
template <typename S>
FMT_CONSTEXPR FMT_ALWAYS_INLINE void check(const S& s) {
static_assert(
detail::count<
(std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
std::is_reference<Args>::value)...>() == 0,
"passing views as lvalues is disallowed");
detail::ignore_unused(s);
#if FMT_USE_CONSTEVAL
if constexpr (detail::count_named_args<Args...>() ==
detail::count_statically_named_args<Args...>()) {
using checker =
detail::format_string_checker<Char, remove_cvref_t<Args>...>;
detail::parse_format_string<true>(str_, checker(s));
detail::parse_format_string(str_, checker(str_));
}
#else
#endif
}
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(
detail::is_compile_string<S>::value,
FMT_USE_CONSTEVAL && sizeof(S) != 0,
"FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
"FMT_STRING.");
#endif
detail::check_format_string<Args...>(s);
#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) {}

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);
*/
#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
# define FMT_COMPILE(s) FMT_STRING(s)
#endif

View File

@ -1869,7 +1869,7 @@ inline auto find_escape(const char* begin, const char* end)
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 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); \
} \
}; \
typename FMT_COMPILE_STRING::char_type FMT_CHAR = {}; \
(void)FMT_CHAR; \
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.
* 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>
auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt {

View File

@ -34,7 +34,8 @@ struct format_string_char<
};
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;
};

View File

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