diff --git a/doc/index.md b/doc/index.md
index 457857c4..b170f9f2 100644
--- a/doc/index.md
+++ b/doc/index.md
@@ -122,7 +122,7 @@ hide:
The library is highly portable and requires only a minimal subset of
- C++11 features which are available in GCC 4.8, Clang 3.4, MSVC 19.0
+ C++11 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.
diff --git a/include/fmt/base.h b/include/fmt/base.h
index 017d582b..48e3d340 100644
--- a/include/fmt/base.h
+++ b/include/fmt/base.h
@@ -2765,7 +2765,7 @@ FMT_CONSTEXPR FMT_INLINE auto parse_replacement_field(const Char* begin,
return begin + 1;
}
-template
+template
FMT_CONSTEXPR void parse_format_string(basic_string_view format_str,
Handler&& handler) {
auto begin = format_str.data();
@@ -2938,21 +2938,6 @@ template class format_string_checker {
// A base class for compile-time strings.
struct compile_string {};
-template
-using is_compile_string = std::is_base_of;
-
-// Reports a compile-time error if S is not a valid format string for T.
-template ::value)>
-void check_format_string(S fmt) {
- using char_type = typename S::char_type;
- FMT_CONSTEXPR auto s = basic_string_view(fmt);
- using checker = format_string_checker...>;
- FMT_CONSTEXPR bool error = (parse_format_string(s, checker(s)), true);
- ignore_unused(error);
-}
-template ::value)>
-FMT_ALWAYS_INLINE void check_format_string(S) {}
-
// Use vformat_args and avoid type_identity to keep symbols short.
template struct vformat_args {
using type = basic_format_args>;
@@ -3027,36 +3012,49 @@ template class basic_format_string {
private:
basic_string_view str_;
- public:
- template <
- typename S,
- FMT_ENABLE_IF(
- std::is_convertible>::value ||
- (detail::is_compile_string::value &&
- std::is_constructible, const S&>::value))>
- FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_format_string(const S& s) : str_(s) {
+ using checker = detail::format_string_checker...>;
+
+ template
+ FMT_CONSTEXPR FMT_ALWAYS_INLINE void check(const S& s) {
static_assert(
detail::count<
(std::is_base_of>::value &&
- std::is_reference::value)...>() == 0,
+ std::is_reference::value)...>() == 0,
"passing views as lvalues is disallowed");
+ detail::ignore_unused(s);
#if FMT_USE_CONSTEVAL
if constexpr (detail::count_named_args() ==
detail::count_statically_named_args()) {
- using checker =
- detail::format_string_checker...>;
- detail::parse_format_string(str_, checker(s));
+ detail::parse_format_string(str_, checker(str_));
}
-#else
-# ifdef FMT_ENFORCE_COMPILE_STRING
+#endif
+ }
+
+ public:
+ // Reports a compile-time error if S is not a valid format string for Args.
+ template >::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::value,
+ FMT_USE_CONSTEVAL && sizeof(S) != 0,
"FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
"FMT_STRING.");
-# endif
- detail::check_format_string(s);
#endif
}
+ template ::value&&
+ std::is_constructible,
+ 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(S());
+ FMT_CONSTEXPR int ignore = (detail::parse_format_string(v, checker(v)), 0);
+ detail::ignore_unused(ignore);
+ }
basic_format_string(runtime_format_string fmt) : str_(fmt.str) {}
FMT_ALWAYS_INLINE operator basic_string_view() const { return str_; }
diff --git a/include/fmt/compile.h b/include/fmt/compile.h
index 63fa19a9..4a5dc71d 100644
--- a/include/fmt/compile.h
+++ b/include/fmt/compile.h
@@ -36,7 +36,7 @@ struct is_compiled_string : std::is_base_of {};
* 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
diff --git a/include/fmt/format.h b/include/fmt/format.h
index 6278489a..c7d75fd1 100644
--- a/include/fmt/format.h
+++ b/include/fmt/format.h
@@ -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(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
auto write_codepoint(OutputIt out, char prefix, uint32_t cp) -> OutputIt {
diff --git a/include/fmt/xchar.h b/include/fmt/xchar.h
index b1f39ed2..35618de9 100644
--- a/include/fmt/xchar.h
+++ b/include/fmt/xchar.h
@@ -34,7 +34,8 @@ struct format_string_char<
};
template
-struct format_string_char::value>> {
+struct format_string_char<
+ S, enable_if_t::value>> {
using type = typename S::char_type;
};
diff --git a/test/format-test.cc b/test/format-test.cc
index e6be9935..75baa290 100644
--- a/test/format-test.cc
+++ b/test/format-test.cc
@@ -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");
}