From 780b44bf8230fae1ad15a2899563a8b3e48caee1 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 4 Nov 2017 09:02:47 -0700 Subject: [PATCH] Add compile-time format string check --- include/fmt/format.h | 74 ++++++++++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 23 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 2bf9b571..4b9fbff0 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -3548,7 +3548,7 @@ struct id_adapter { }; template -constexpr void parse_format_string(Iterator it, Handler &handler) { +constexpr void parse_format_string(Iterator it, Handler &&handler) { using char_type = typename std::iterator_traits::value_type; auto start = it; while (*it) { @@ -3781,8 +3781,9 @@ void vformat_to(basic_buffer &buffer, basic_string_view format_str, basic_buffer &buffer; basic_context context; basic_arg arg; - } handler(buffer, format_str, args); - parse_format_string(iterator(format_str.begin(), format_str.end()), handler); + }; + parse_format_string(iterator(format_str.begin(), format_str.end()), + handler(buffer, format_str, args)); } // Casts ``p`` to ``const void*`` for pointer formatting. @@ -3796,8 +3797,44 @@ inline const void *ptr(const T *p) { return p; } namespace fmt { namespace internal { +# if FMT_UDL_TEMPLATE template -struct udl_format { +struct udl_format_handler { + bool error = false; + + constexpr void on_text(const Char *, const Char *) {} + + constexpr void on_arg_id() {} + + template + constexpr void on_arg_id(T) {} + + constexpr void on_replacement_field(const Char *) {} + + constexpr const Char *on_format_specs(const Char *s) { return s; } + + constexpr void on_error(const char *) { error = true; } +}; + +template +struct udl_formatter { + template + static constexpr bool check_format(const Char *s) { + udl_format_handler handler; + internal::parse_format_string(s, handler); + return !handler.error; + } + + template + std::basic_string operator()(const Args &... args) const { + constexpr Char s[] = {CHARS..., '\0'}; + static_assert(check_format(s), "invalid format string"); + return format(s, args...); + } +}; +# else +template +struct udl_formatter { const Char *str; template @@ -3806,6 +3843,7 @@ struct udl_format { return format(str, std::forward(args)...); } }; +# endif // FMT_UDL_TEMPLATE template struct UdlArg { @@ -3821,6 +3859,12 @@ struct UdlArg { inline namespace literals { +# if FMT_UDL_TEMPLATE +template +constexpr internal::udl_formatter operator""_format() { + return {}; +} +# else /** \rst C++11 literal equivalent of :func:`fmt::format`. @@ -3831,10 +3875,11 @@ inline namespace literals { std::string message = "The answer is {}"_format(42); \endrst */ -inline internal::udl_format +inline internal::udl_formatter operator"" _format(const char *s, std::size_t) { return {s}; } -inline internal::udl_format +inline internal::udl_formatter operator"" _format(const wchar_t *s, std::size_t) { return {s}; } +# endif // FMT_UDL_TEMPLATE /** \rst @@ -3850,23 +3895,6 @@ inline internal::UdlArg operator"" _a(const char *s, std::size_t) { return {s}; } inline internal::UdlArg operator"" _a(const wchar_t *s, std::size_t) { return {s}; } - -# if FMT_UDL_TEMPLATE -template -struct udl_formatter { - template - std::string operator()(const Args &... args) const { - const Char s[] = {CHARS...}; - // TODO - return s; - } -}; - -template -constexpr auto operator""_format() { - return udl_formatter(); -} -# endif } // inline namespace literals } // namespace fmt #endif // FMT_USE_USER_DEFINED_LITERALS