diff --git a/include/fmt/core.h b/include/fmt/core.h index d6c941fa1271..9c90e6398d47 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1099,6 +1099,7 @@ FMT_CONSTEXPR void basic_format_parse_context::check_dynamic_spec( FMT_EXPORT template class basic_format_arg; FMT_EXPORT template class basic_format_args; +FMT_EXPORT template class format_arg_store; FMT_EXPORT template class dynamic_format_arg_store; // A formatter for objects of type T. @@ -1129,7 +1130,7 @@ template class basic_appender { using difference_type = ptrdiff_t; FMT_UNCHECKED_ITERATOR(basic_appender); - basic_appender(detail::buffer& buf) : buffer_(&buf) {} + FMT_CONSTEXPR basic_appender(detail::buffer& buf) : buffer_(&buf) {} auto operator=(T c) -> basic_appender& { buffer_->push_back(c); @@ -1733,147 +1734,6 @@ FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( return arg.visit(static_cast(vis)); } -// Formatting context. -template class basic_format_context { - private: - OutputIt out_; - basic_format_args args_; - detail::locale_ref loc_; - - public: - using iterator = OutputIt; - using format_arg = basic_format_arg; - using format_args = basic_format_args; - using parse_context_type = basic_format_parse_context; - template using formatter_type = formatter; - - /** The character type for the output. */ - using char_type = Char; - - basic_format_context(basic_format_context&&) = default; - basic_format_context(const basic_format_context&) = delete; - void operator=(const basic_format_context&) = delete; - /** - Constructs a ``basic_format_context`` object. References to the arguments - are stored in the object so make sure they have appropriate lifetimes. - */ - constexpr basic_format_context(OutputIt out, format_args ctx_args, - detail::locale_ref loc = {}) - : out_(out), args_(ctx_args), loc_(loc) {} - - constexpr auto arg(int id) const -> format_arg { return args_.get(id); } - FMT_CONSTEXPR auto arg(basic_string_view name) -> format_arg { - return args_.get(name); - } - FMT_CONSTEXPR auto arg_id(basic_string_view name) -> int { - return args_.get_id(name); - } - auto args() const -> const format_args& { return args_; } - - // This function is intentionally not constexpr to give a compile-time error. - void on_error(const char* message) { throw_format_error(message); } - - // Returns an iterator to the beginning of the output range. - FMT_CONSTEXPR auto out() -> iterator { return out_; } - - // Advances the begin iterator to ``it``. - void advance_to(iterator it) { - if (!detail::is_back_insert_iterator()) out_ = it; - } - - FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; } -}; - -template -using buffer_context = basic_format_context, Char>; -using format_context = basic_format_context; - -template -using is_formattable = bool_constant>() - .map(std::declval()))>::value>; - -/** - \rst - An array of references to arguments. It can be implicitly converted into - `~fmt::basic_format_args` for passing into type-erased formatting functions - such as `~fmt::vformat`. - \endrst - */ -template -class format_arg_store -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 - // Workaround a GCC template argument substitution bug. - : public basic_format_args -#endif -{ - private: - static const size_t num_args = sizeof...(Args); - static constexpr size_t num_named_args = detail::count_named_args(); - static const bool is_packed = num_args <= detail::max_packed_args; - - using value_type = conditional_t, - basic_format_arg>; - - detail::arg_data - data_; - - friend class basic_format_args; - - static constexpr unsigned long long desc = - (is_packed ? detail::encode_types() - : detail::is_unpacked_bit | num_args) | - (num_named_args != 0 - ? static_cast(detail::has_named_args_bit) - : 0); - - public: - template - FMT_CONSTEXPR FMT_INLINE format_arg_store(T&... args) - : -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 - basic_format_args(*this), -#endif - data_{detail::make_arg(args)...} { - if (detail::const_check(num_named_args != 0)) - detail::init_named_args(data_.named_args(), 0, 0, args...); - } -}; - -/** - \rst - Constructs a `~fmt::format_arg_store` object that contains references to - arguments and can be implicitly converted to `~fmt::format_args`. `Context` - can be omitted in which case it defaults to `~fmt::format_context`. - See `~fmt::arg` for lifetime considerations. - \endrst - */ -// Arguments are taken by lvalue references to avoid some lifetime issues. -template -constexpr auto make_format_args(T&... args) - -> format_arg_store...> { - return {args...}; -} - -/** - \rst - Returns a named argument to be used in a formatting function. - It should only be used in a call to a formatting function or - `dynamic_format_arg_store::push_back`. - - **Example**:: - - fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); - \endrst - */ -template -inline auto arg(const Char* name, const T& arg) -> detail::named_arg { - static_assert(!detail::is_named_arg(), "nested named arguments"); - return {name, arg}; -} -FMT_END_EXPORT - /** \rst A view of a collection of formatting arguments. To avoid lifetime issues it @@ -1908,7 +1768,7 @@ template class basic_format_args { constexpr auto is_packed() const -> bool { return (desc_ & detail::is_unpacked_bit) == 0; } - auto has_named_args() const -> bool { + constexpr auto has_named_args() const -> bool { return (desc_ & detail::has_named_args_bit) != 0; } @@ -1978,7 +1838,7 @@ template class basic_format_args { } template - auto get_id(basic_string_view name) const -> int { + FMT_CONSTEXPR auto get_id(basic_string_view name) const -> int { if (!has_named_args()) return -1; const auto& named_args = (is_packed() ? values_[-1] : args_[-1].value_).named_args; @@ -1995,6 +1855,151 @@ template class basic_format_args { } }; +// A formatting context. +class context { + private: + appender out_; + basic_format_args args_; + detail::locale_ref loc_; + + public: + /** The character type for the output. */ + using char_type = char; + + using iterator = appender; + using format_arg = basic_format_arg; + using format_args = basic_format_args; + using parse_context_type = basic_format_parse_context; + template using formatter_type = formatter; + + /** + Constructs a ``basic_format_context`` object. References to the arguments + are stored in the object so make sure they have appropriate lifetimes. + */ + FMT_CONSTEXPR context(iterator out, format_args ctx_args, + detail::locale_ref loc = {}) + : out_(out), args_(ctx_args), loc_(loc) {} + context(context&&) = default; + context(const context&) = delete; + void operator=(const context&) = delete; + + FMT_CONSTEXPR auto arg(int id) const -> format_arg { return args_.get(id); } + auto arg(string_view name) -> format_arg { return args_.get(name); } + FMT_CONSTEXPR auto arg_id(string_view name) -> int { + return args_.get_id(name); + } + auto args() const -> const format_args& { return args_; } + + // This function is intentionally not constexpr to give a compile-time error. + void on_error(const char* message) { throw_format_error(message); } + + // Returns an iterator to the beginning of the output range. + FMT_CONSTEXPR auto out() -> iterator { return out_; } + + // Advances the begin iterator to ``it``. + void advance_to(iterator) {} + + FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; } +}; + +template class generic_context; + +// Longer aliases for C++20 compatibility. +template +using basic_format_context = + conditional_t::value, context, + generic_context>; +using format_context = context; + +template +using buffer_context = basic_format_context, Char>; + +template +using is_formattable = bool_constant>() + .map(std::declval()))>::value>; + +/** + \rst + An array of references to arguments. It can be implicitly converted into + `~fmt::basic_format_args` for passing into type-erased formatting functions + such as `~fmt::vformat`. + \endrst + */ +template +class format_arg_store +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + // Workaround a GCC template argument substitution bug. + : public basic_format_args +#endif +{ + private: + static const size_t num_args = sizeof...(Args); + static constexpr size_t num_named_args = detail::count_named_args(); + static const bool is_packed = num_args <= detail::max_packed_args; + + using value_type = conditional_t, + basic_format_arg>; + + detail::arg_data + data_; + + friend class basic_format_args; + + static constexpr unsigned long long desc = + (is_packed ? detail::encode_types() + : detail::is_unpacked_bit | num_args) | + (num_named_args != 0 + ? static_cast(detail::has_named_args_bit) + : 0); + + public: + template + FMT_CONSTEXPR FMT_INLINE format_arg_store(T&... args) + : +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 + basic_format_args(*this), +#endif + data_{detail::make_arg(args)...} { + if (detail::const_check(num_named_args != 0)) + detail::init_named_args(data_.named_args(), 0, 0, args...); + } +}; + +/** + \rst + Constructs a `~fmt::format_arg_store` object that contains references to + arguments and can be implicitly converted to `~fmt::format_args`. `Context` + can be omitted in which case it defaults to `~fmt::format_context`. + See `~fmt::arg` for lifetime considerations. + \endrst + */ +// Arguments are taken by lvalue references to avoid some lifetime issues. +template +constexpr auto make_format_args(T&... args) + -> format_arg_store...> { + return {args...}; +} + +/** + \rst + Returns a named argument to be used in a formatting function. + It should only be used in a call to a formatting function or + `dynamic_format_arg_store::push_back`. + + **Example**:: + + fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); + \endrst + */ +template +inline auto arg(const Char* name, const T& arg) -> detail::named_arg { + static_assert(!detail::is_named_arg(), "nested named arguments"); + return {name, arg}; +} +FMT_END_EXPORT + /** An alias to ``basic_format_args``. */ // A separate type would result in shorter symbols but break ABI compatibility // between clang and gcc on ARM (#1919). diff --git a/include/fmt/format.h b/include/fmt/format.h index 282aeae1b915..9b74a89d2f88 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1071,6 +1071,50 @@ constexpr auto compile_string_to_view(detail::std_string_view s) } } // namespace detail_exported +// A generic formatting context with custom output iterator and character +// (code unit) support. +template class generic_context { + private: + OutputIt out_; + basic_format_args args_; + detail::locale_ref loc_; + + public: + using char_type = Char; + using iterator = OutputIt; + using format_args = basic_format_args; + using parse_context_type = basic_format_parse_context; + template using formatter_type = formatter; + + constexpr generic_context(OutputIt out, format_args ctx_args, + detail::locale_ref loc = {}) + : out_(out), args_(ctx_args), loc_(loc) {} + generic_context(generic_context&&) = default; + generic_context(const generic_context&) = delete; + void operator=(const generic_context&) = delete; + + constexpr auto arg(int id) const -> basic_format_arg { + return args_.get(id); + } + auto arg(basic_string_view name) -> basic_format_arg { + return args_.get(name); + } + FMT_CONSTEXPR auto arg_id(basic_string_view name) -> int { + return args_.get_id(name); + } + auto args() const -> const format_args& { return args_; } + + void on_error(const char* message) { throw_format_error(message); } + + FMT_CONSTEXPR auto out() -> iterator { return out_; } + + void advance_to(iterator it) { + if (!detail::is_back_insert_iterator()) out_ = it; + } + + FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; } +}; + class loc_value { private: basic_format_arg value_; diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h index 26fb3b5ac071..9906e5f22c73 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -143,9 +143,8 @@ template struct basic_ostream_formatter : formatter, Char> { void set_debug_format() = delete; - template - auto format(const T& value, basic_format_context& ctx) const - -> OutputIt { + template + auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) { auto buffer = basic_memory_buffer(); detail::format_value(buffer, value); return formatter, Char>::format( @@ -158,9 +157,9 @@ using ostream_formatter = basic_ostream_formatter; template struct formatter, Char> : basic_ostream_formatter { - template - auto format(detail::streamed_view view, - basic_format_context& ctx) const -> OutputIt { + template + auto format(detail::streamed_view view, Context& ctx) const + -> decltype(ctx.out()) { return basic_ostream_formatter::format(view.value, ctx); } }; diff --git a/include/fmt/std.h b/include/fmt/std.h index 885176c0fdae..d8f30a192d67 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -400,9 +400,9 @@ struct formatter< return it; } - template - auto format(const std::exception& ex, - basic_format_context& ctx) const -> OutputIt { + template + auto format(const std::exception& ex, Context& ctx) const + -> decltype(ctx.out()) { format_specs spec; auto out = ctx.out(); if (!with_typename_) diff --git a/test/core-test.cc b/test/core-test.cc index 3ebe3ee7a971..4a96ccda9a03 100644 --- a/test/core-test.cc +++ b/test/core-test.cc @@ -368,7 +368,7 @@ VISIT_TYPE(unsigned long, unsigned long long); { \ testing::StrictMock> visitor; \ EXPECT_CALL(visitor, visit(expected)); \ - using iterator = std::back_insert_iterator>; \ + using iterator = fmt::basic_appender; \ auto var = value; \ fmt::detail::make_arg>(var) \ .visit(visitor); \ @@ -379,7 +379,6 @@ VISIT_TYPE(unsigned long, unsigned long long); using value_type = decltype(value); \ typename visit_type::type expected = value; \ CHECK_ARG(char, expected, value) \ - CHECK_ARG(wchar_t, expected, value) \ } template class numeric_arg_test : public testing::Test {}; @@ -419,23 +418,10 @@ TEST(arg_test, string_arg) { CHECK_ARG(char, sv, std::string(str)); } -TEST(arg_test, wstring_arg) { - wchar_t str_data[] = L"test"; - wchar_t* str = str_data; - const wchar_t* cstr = str; - - auto sv = fmt::basic_string_view(str); - CHECK_ARG(wchar_t, cstr, str); - CHECK_ARG(wchar_t, cstr, cstr); - CHECK_ARG(wchar_t, sv, std::wstring(str)); - CHECK_ARG(wchar_t, sv, fmt::basic_string_view(str)); -} - TEST(arg_test, pointer_arg) { void* p = nullptr; const void* cp = nullptr; CHECK_ARG(char, cp, p); - CHECK_ARG(wchar_t, cp, p); CHECK_ARG_SIMPLE(cp); }