add support for statically named arguments with FMT_STRING
This commit is contained in:
parent
ea94d6d93c
commit
54f22a3eef
@ -191,24 +191,10 @@ constexpr const auto& get([[maybe_unused]] const T& first,
|
|||||||
return get<N - 1>(rest...);
|
return get<N - 1>(rest...);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr int invalid_arg_index = -1;
|
|
||||||
|
|
||||||
template <int N, typename T, typename... Args, typename Char>
|
|
||||||
constexpr int get_arg_index_by_name(basic_string_view<Char> name) {
|
|
||||||
if constexpr (detail::is_statically_named_arg<T>()) {
|
|
||||||
if (name == T::name) return N;
|
|
||||||
}
|
|
||||||
if constexpr (sizeof...(Args) == 0) {
|
|
||||||
return invalid_arg_index;
|
|
||||||
} else {
|
|
||||||
return get_arg_index_by_name<N + 1, Args...>(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename... Args>
|
template <typename Char, typename... Args>
|
||||||
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
|
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
|
||||||
type_list<Args...>) {
|
type_list<Args...>) {
|
||||||
return get_arg_index_by_name<0, Args...>(name);
|
return get_arg_index_by_name<Args...>(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int N, typename> struct get_type_impl;
|
template <int N, typename> struct get_type_impl;
|
||||||
|
@ -281,6 +281,16 @@
|
|||||||
# define FMT_COMPILE_TIME_CHECKS 0
|
# define FMT_COMPILE_TIME_CHECKS 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||||
|
# if defined(__cpp_nontype_template_args) && \
|
||||||
|
((FMT_GCC_VERSION >= 903 && __cplusplus >= 201709L) || \
|
||||||
|
__cpp_nontype_template_args >= 201911L)
|
||||||
|
# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 1
|
||||||
|
# else
|
||||||
|
# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 0
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
// Enable minimal optimizations for more compact code in debug mode.
|
// Enable minimal optimizations for more compact code in debug mode.
|
||||||
FMT_GCC_PRAGMA("GCC push_options")
|
FMT_GCC_PRAGMA("GCC push_options")
|
||||||
#ifndef __OPTIMIZE__
|
#ifndef __OPTIMIZE__
|
||||||
@ -991,6 +1001,7 @@ template <typename Char>
|
|||||||
inline void init_named_args(named_arg_info<Char>*, int, int) {}
|
inline void init_named_args(named_arg_info<Char>*, int, int) {}
|
||||||
|
|
||||||
template <typename T> struct is_named_arg : std::false_type {};
|
template <typename T> struct is_named_arg : std::false_type {};
|
||||||
|
template <typename T> struct is_statically_named_arg : std::false_type {};
|
||||||
|
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
struct is_named_arg<named_arg<Char, T>> : std::true_type {};
|
struct is_named_arg<named_arg<Char, T>> : std::true_type {};
|
||||||
@ -2425,6 +2436,36 @@ class compile_parse_context
|
|||||||
using base::check_arg_id;
|
using base::check_arg_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr int invalid_arg_index = -1;
|
||||||
|
|
||||||
|
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||||
|
template <int N, typename T, typename... Args, typename Char>
|
||||||
|
constexpr int get_arg_index_by_name(basic_string_view<Char> name) {
|
||||||
|
if constexpr (detail::is_statically_named_arg<T>()) {
|
||||||
|
if (name == T::name) return N;
|
||||||
|
}
|
||||||
|
if constexpr (sizeof...(Args) == 0) {
|
||||||
|
return invalid_arg_index;
|
||||||
|
} else {
|
||||||
|
return get_arg_index_by_name<N + 1, Args...>(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args, typename Char>
|
||||||
|
constexpr int get_arg_index_by_name(basic_string_view<Char> name) {
|
||||||
|
if constexpr (sizeof...(Args) == 0) {
|
||||||
|
return invalid_arg_index;
|
||||||
|
} else {
|
||||||
|
return get_arg_index_by_name<0, Args...>(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
template <typename... Args, typename Char>
|
||||||
|
constexpr int get_arg_index_by_name(basic_string_view<Char>) {
|
||||||
|
return invalid_arg_index;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
template <typename Char, typename ErrorHandler, typename... Args>
|
template <typename Char, typename ErrorHandler, typename... Args>
|
||||||
class format_string_checker {
|
class format_string_checker {
|
||||||
public:
|
public:
|
||||||
@ -2437,9 +2478,16 @@ class format_string_checker {
|
|||||||
|
|
||||||
FMT_CONSTEXPR int on_arg_id() { return context_.next_arg_id(); }
|
FMT_CONSTEXPR int on_arg_id() { return context_.next_arg_id(); }
|
||||||
FMT_CONSTEXPR int on_arg_id(int id) { return context_.check_arg_id(id), id; }
|
FMT_CONSTEXPR int on_arg_id(int id) { return context_.check_arg_id(id), id; }
|
||||||
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
|
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) {
|
||||||
on_error("compile-time checks don't support named arguments");
|
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||||
|
auto index = get_arg_index_by_name<Args...>(id);
|
||||||
|
if (index == invalid_arg_index) on_error("named argument is not found");
|
||||||
|
return context_.check_arg_id(index), index;
|
||||||
|
#else
|
||||||
|
(void)id;
|
||||||
|
on_error("compile-time checks for named arguments require C++20 support");
|
||||||
return 0;
|
return 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
|
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
|
||||||
|
@ -255,16 +255,6 @@ inline int ctzll(uint64_t x) {
|
|||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
|
||||||
# if defined(__cpp_nontype_template_args) && \
|
|
||||||
((FMT_GCC_VERSION >= 903 && __cplusplus >= 201709L) || \
|
|
||||||
__cpp_nontype_template_args >= 201911L)
|
|
||||||
# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 1
|
|
||||||
# else
|
|
||||||
# define FMT_USE_NONTYPE_TEMPLATE_PARAMETERS 0
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
@ -3226,9 +3216,6 @@ template <typename Char> struct udl_formatter {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename = void>
|
|
||||||
struct is_statically_named_arg : std::false_type {};
|
|
||||||
|
|
||||||
# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||||
template <typename T, typename Char, size_t N, fixed_string<Char, N> Str>
|
template <typename T, typename Char, size_t N, fixed_string<Char, N> Str>
|
||||||
struct statically_named_arg : view {
|
struct statically_named_arg : view {
|
||||||
|
@ -1694,6 +1694,14 @@ TEST(format_test, compile_time_string) {
|
|||||||
EXPECT_EQ(L"42", fmt::format(FMT_STRING(L"{}"), 42));
|
EXPECT_EQ(L"42", fmt::format(FMT_STRING(L"{}"), 42));
|
||||||
EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like()));
|
EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like()));
|
||||||
|
|
||||||
|
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||||
|
using namespace fmt::literals;
|
||||||
|
EXPECT_EQ("foobar", fmt::format(FMT_STRING("{foo}{bar}"), "bar"_a = "bar",
|
||||||
|
"foo"_a = "foo"));
|
||||||
|
EXPECT_EQ("", fmt::format(FMT_STRING("")));
|
||||||
|
EXPECT_EQ("", fmt::format(FMT_STRING(""), "arg"_a = 42));
|
||||||
|
#endif
|
||||||
|
|
||||||
(void)static_with_null;
|
(void)static_with_null;
|
||||||
(void)static_with_null_wide;
|
(void)static_with_null_wide;
|
||||||
(void)static_no_null;
|
(void)static_no_null;
|
||||||
@ -2339,8 +2347,15 @@ TEST(format_test, format_string_errors) {
|
|||||||
# else
|
# else
|
||||||
fmt::print("warning: constexpr is broken in this version of MSVC\n");
|
fmt::print("warning: constexpr is broken in this version of MSVC\n");
|
||||||
# endif
|
# endif
|
||||||
EXPECT_ERROR("{foo", "compile-time checks don't support named arguments",
|
# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||||
|
EXPECT_ERROR("{foo}", "named argument is not found", decltype("bar"_a = 42));
|
||||||
|
EXPECT_ERROR("{foo}", "named argument is not found",
|
||||||
|
decltype(fmt::arg("foo", 42)));
|
||||||
|
# else
|
||||||
|
EXPECT_ERROR("{foo}",
|
||||||
|
"compile-time checks for named arguments require C++20 support",
|
||||||
int);
|
int);
|
||||||
|
# endif
|
||||||
EXPECT_ERROR_NOARGS("{10000000000}", "number is too big");
|
EXPECT_ERROR_NOARGS("{10000000000}", "number is too big");
|
||||||
EXPECT_ERROR_NOARGS("{0x}", "invalid format string");
|
EXPECT_ERROR_NOARGS("{0x}", "invalid format string");
|
||||||
EXPECT_ERROR_NOARGS("{-}", "invalid format string");
|
EXPECT_ERROR_NOARGS("{-}", "invalid format string");
|
||||||
|
Loading…
Reference in New Issue
Block a user