| // Formatting library for C++ - the standard API implementation |
| // |
| // Copyright (c) 2012 - present, Victor Zverovich |
| // All rights reserved. |
| // |
| // For the license information refer to format.h. |
| |
| #ifndef FMT_FORMAT_ |
| #define FMT_FORMAT_ |
| |
| #include <cassert> |
| #include <variant> |
| #include "fmt/format.h" |
| |
| // This implementation verifies the correctness of the standard API proposed in |
| // P0645 Text Formatting and is optimized for copy-pasting from the paper, not |
| // for efficiency or readability. An efficient implementation should not use |
| // std::variant and should store packed argument type tags separately from |
| // values in basic_format_args for small number of arguments. |
| |
| namespace std { |
| template<class T> |
| constexpr bool Integral = is_integral_v<T>; |
| |
| template <class O> |
| using iter_difference_t = ptrdiff_t; |
| } |
| |
| // https://fmt.dev/Text%20Formatting.html#format.syn |
| namespace std { |
| // [format.error], class format_error |
| class format_error; |
| |
| // [format.formatter], formatter |
| template<class charT> class basic_format_parse_context; |
| using format_parse_context = basic_format_parse_context<char>; |
| using wformat_parse_context = basic_format_parse_context<wchar_t>; |
| |
| template<class Out, class charT> class basic_format_context; |
| using format_context = basic_format_context< |
| /* unspecified */ fmt::detail::buffer_appender<char>, char>; |
| using wformat_context = basic_format_context< |
| /* unspecified */ fmt::detail::buffer_appender<wchar_t>, wchar_t>; |
| |
| template<class T, class charT = char> struct formatter { |
| formatter() = delete; |
| }; |
| |
| // [format.arguments], arguments |
| template<class Context> class basic_format_arg; |
| |
| template<class Visitor, class Context> |
| /* see below */ auto visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg); |
| |
| template<class Context, class... Args> struct format_arg_store; // exposition only |
| |
| template<class Context> class basic_format_args; |
| using format_args = basic_format_args<format_context>; |
| using wformat_args = basic_format_args<wformat_context>; |
| |
| template<class Out, class charT> |
| using format_args_t = basic_format_args<basic_format_context<Out, charT>>; |
| |
| template<class Context = format_context, class... Args> |
| format_arg_store<Context, Args...> |
| make_format_args(const Args&... args); |
| template<class... Args> |
| format_arg_store<wformat_context, Args...> |
| make_wformat_args(const Args&... args); |
| |
| // [format.functions], formatting functions |
| template<class... Args> |
| string format(string_view fmt, const Args&... args); |
| template<class... Args> |
| wstring format(wstring_view fmt, const Args&... args); |
| |
| string vformat(string_view fmt, format_args args); |
| wstring vformat(wstring_view fmt, wformat_args args); |
| |
| template<class Out, class... Args> |
| Out format_to(Out out, string_view fmt, const Args&... args); |
| template<class Out, class... Args> |
| Out format_to(Out out, wstring_view fmt, const Args&... args); |
| |
| template<class Out> |
| Out vformat_to(Out out, string_view fmt, format_args_t<fmt::type_identity_t<Out>, char> args); |
| template<class Out> |
| Out vformat_to(Out out, wstring_view fmt, format_args_t<fmt::type_identity_t<Out>, wchar_t> args); |
| |
| template<class Out> |
| struct format_to_n_result { |
| Out out; |
| iter_difference_t<Out> size; |
| }; |
| |
| template<class Out, class... Args> |
| format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n, |
| string_view fmt, const Args&... args); |
| template<class Out, class... Args> |
| format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n, |
| wstring_view fmt, const Args&... args); |
| |
| template<class... Args> |
| size_t formatted_size(string_view fmt, const Args&... args); |
| template<class... Args> |
| size_t formatted_size(wstring_view fmt, const Args&... args); |
| } |
| |
| // https://fmt.dev/Text%20Formatting.html#format.error |
| namespace std { |
| class format_error : public runtime_error { |
| public: |
| explicit format_error(const string& what_arg) : runtime_error(what_arg) {} |
| explicit format_error(const char* what_arg) : runtime_error(what_arg) {} |
| }; |
| } |
| |
| namespace std { |
| namespace detail { |
| struct error_handler { |
| // This function is intentionally not constexpr to give a compile-time error. |
| void on_error(const char* message) { |
| throw std::format_error(message); |
| } |
| }; |
| } |
| } |
| |
| // https://fmt.dev/Text%20Formatting.html#format.parse_context |
| namespace std { |
| template<class charT> |
| class basic_format_parse_context { |
| public: |
| using char_type = charT; |
| using const_iterator = typename basic_string_view<charT>::const_iterator; |
| using iterator = const_iterator; |
| |
| private: |
| iterator begin_; // exposition only |
| iterator end_; // exposition only |
| enum indexing { unknown, manual, automatic }; // exposition only |
| indexing indexing_; // exposition only |
| size_t next_arg_id_; // exposition only |
| size_t num_args_; // exposition only |
| |
| public: |
| explicit constexpr basic_format_parse_context(basic_string_view<charT> fmt, |
| size_t num_args = 0) noexcept; |
| basic_format_parse_context(const basic_format_parse_context&) = delete; |
| basic_format_parse_context& operator=(const basic_format_parse_context&) = delete; |
| |
| constexpr const_iterator begin() const noexcept; |
| constexpr const_iterator end() const noexcept; |
| constexpr void advance_to(const_iterator it); |
| |
| constexpr size_t next_arg_id(); |
| constexpr void check_arg_id(size_t id); |
| |
| // Implementation detail: |
| constexpr void check_arg_id(fmt::string_view) {} |
| detail::error_handler error_handler() const { return {}; } |
| void on_error(const char* msg) { error_handler().on_error(msg); } |
| }; |
| } |
| |
| namespace std { |
| template<class charT> |
| /* explicit */ constexpr basic_format_parse_context<charT>:: |
| basic_format_parse_context(basic_string_view<charT> fmt, |
| size_t num_args) noexcept |
| : begin_(fmt.begin()), end_(fmt.end()), indexing_(unknown), next_arg_id_(0), num_args_(num_args) {} |
| |
| template<class charT> |
| constexpr typename basic_format_parse_context<charT>::const_iterator basic_format_parse_context<charT>::begin() const noexcept { return begin_; } |
| |
| template<class charT> |
| constexpr typename basic_format_parse_context<charT>::const_iterator basic_format_parse_context<charT>::end() const noexcept { return end_; } |
| |
| template<class charT> |
| constexpr void basic_format_parse_context<charT>::advance_to(typename basic_format_parse_context<charT>::iterator it) { begin_ = it; } |
| |
| template<class charT> |
| constexpr size_t basic_format_parse_context<charT>::next_arg_id() { |
| if (indexing_ == manual) |
| throw format_error("manual to automatic indexing"); |
| if (indexing_ == unknown) |
| indexing_ = automatic; |
| return next_arg_id_++; |
| } |
| |
| template<class charT> |
| constexpr void basic_format_parse_context<charT>::check_arg_id(size_t id) { |
| // clang doesn't support __builtin_is_constant_evaluated yet |
| //if (!(!__builtin_is_constant_evaluated() || id < num_args_)) |
| // throw format_error(invalid index is out of range"); |
| if (indexing_ == automatic) |
| throw format_error("automatic to manual indexing"); |
| if (indexing_ == unknown) |
| indexing_ = manual; |
| } |
| } |
| |
| // https://fmt.dev/Text%20Formatting.html#format.context |
| namespace std { |
| template<class Out, class charT> |
| class basic_format_context { |
| basic_format_args<basic_format_context> args_; // exposition only |
| Out out_; // exposition only |
| |
| public: |
| using iterator = Out; |
| using char_type = charT; |
| template<class T> using formatter_type = formatter<T, charT>; |
| |
| basic_format_arg<basic_format_context> arg(size_t id) const; |
| |
| iterator out(); |
| void advance_to(iterator it); |
| |
| // Implementation details: |
| using format_arg = basic_format_arg<basic_format_context>; |
| basic_format_context(Out out, basic_format_args<basic_format_context> args, fmt::detail::locale_ref) |
| : args_(args), out_(out) {} |
| detail::error_handler error_handler() const { return {}; } |
| basic_format_arg<basic_format_context> arg(fmt::basic_string_view<charT>) const { |
| return {}; // unused: named arguments are not supported yet |
| } |
| void on_error(const char* msg) { error_handler().on_error(msg); } |
| }; |
| } |
| |
| namespace std { |
| template<class O, class charT> |
| basic_format_arg<basic_format_context<O, charT>> basic_format_context<O, charT>::arg(size_t id) const { return args_.get(id); } |
| |
| template<class O, class charT> |
| typename basic_format_context<O, charT>::iterator basic_format_context<O, charT>::out() { return out_; } |
| |
| template<class O, class charT> |
| void basic_format_context<O, charT>::advance_to(typename basic_format_context<O, charT>::iterator it) { out_ = it; } |
| } |
| |
| namespace std { |
| namespace detail { |
| template <typename T> |
| constexpr bool is_standard_integer_v = |
| std::is_same_v<T, signed char> || |
| std::is_same_v<T, short int> || |
| std::is_same_v<T, int> || |
| std::is_same_v<T, long int> || |
| std::is_same_v<T, long long int>; |
| |
| template <typename T> |
| constexpr bool is_standard_unsigned_integer_v = |
| std::is_same_v<T, unsigned char> || |
| std::is_same_v<T, unsigned short int> || |
| std::is_same_v<T, unsigned int> || |
| std::is_same_v<T, unsigned long int> || |
| std::is_same_v<T, unsigned long long int>; |
| |
| template <typename T, typename Char> struct formatter; |
| } |
| } |
| |
| // https://fmt.dev/Text%20Formatting.html#format.arg |
| namespace std { |
| template<class Context> |
| class basic_format_arg { |
| public: |
| class handle; |
| |
| private: |
| using char_type = typename Context::char_type; // exposition only |
| |
| variant<monostate, bool, char_type, |
| int, unsigned int, long long int, unsigned long long int, |
| double, long double, |
| const char_type*, basic_string_view<char_type>, |
| const void*, handle> value; // exposition only |
| |
| template<typename T, |
| typename = enable_if_t< |
| std::is_same_v<T, bool> || |
| std::is_same_v<T, char_type> || |
| (std::is_same_v<T, char> && std::is_same_v<char_type, wchar_t>) || |
| detail::is_standard_integer_v<T> || |
| detail::is_standard_unsigned_integer_v<T> || |
| sizeof(typename Context::template formatter_type<T>().format(declval<const T&>(), declval<Context&>())) != 0 |
| >> explicit basic_format_arg(const T& v) noexcept; // exposition only |
| explicit basic_format_arg(float n) noexcept; // exposition only |
| explicit basic_format_arg(double n) noexcept; // exposition only |
| explicit basic_format_arg(long double n) noexcept; // exposition only |
| explicit basic_format_arg(const char_type* s); // exposition only |
| |
| template<class traits> |
| explicit basic_format_arg( |
| basic_string_view<char_type, traits> s) noexcept; // exposition only |
| |
| template<class traits, class Allocator> |
| explicit basic_format_arg( |
| const basic_string<char_type, traits, Allocator>& s) noexcept; // exposition only |
| |
| explicit basic_format_arg(nullptr_t) noexcept; // exposition only |
| |
| template<class T, typename = enable_if_t<is_void_v<T>>> |
| explicit basic_format_arg(const T* p) noexcept; // exposition only |
| |
| // Fails due to a bug in clang |
| //template<class Visitor, class Ctx> |
| // friend auto visit_format_arg(Visitor&& vis, |
| // basic_format_arg<Ctx> arg); // exposition only |
| |
| friend auto get_value(basic_format_arg arg) { |
| return arg.value; |
| } |
| |
| template <typename T, typename Char> friend struct detail::formatter; |
| |
| template<class Ctx, class... Args> |
| friend format_arg_store<Ctx, Args...> |
| make_format_args(const Args&... args); // exposition only |
| |
| public: |
| basic_format_arg() noexcept; |
| |
| explicit operator bool() const noexcept; |
| }; |
| } |
| |
| namespace std { |
| template<class Context> |
| basic_format_arg<Context>::basic_format_arg() noexcept {} |
| |
| template<class Context> |
| template<class T, typename> /* explicit */ basic_format_arg<Context>::basic_format_arg(const T& v) noexcept { |
| if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, char_type>) |
| value = v; |
| else if constexpr (std::is_same_v<T, char> && std::is_same_v<char_type, wchar_t>) |
| value = static_cast<wchar_t>(v); |
| else if constexpr (detail::is_standard_integer_v<T> && sizeof(T) <= sizeof(int)) |
| value = static_cast<int>(v); |
| else if constexpr (detail::is_standard_unsigned_integer_v<T> && sizeof(T) <= sizeof(unsigned)) |
| value = static_cast<unsigned>(v); |
| else if constexpr (detail::is_standard_integer_v<T>) |
| value = static_cast<long long int>(v); |
| else if constexpr (detail::is_standard_unsigned_integer_v<T>) |
| value = static_cast<unsigned long long int>(v); |
| else if constexpr (sizeof(typename Context::template formatter_type<T>().format(declval<const T&>(), declval<Context&>())) != 0) |
| value = handle(v); |
| } |
| |
| template<class Context> |
| /* explicit */ basic_format_arg<Context>::basic_format_arg(float n) noexcept |
| : value(static_cast<double>(n)) {} |
| |
| template<class Context> |
| /* explicit */ basic_format_arg<Context>::basic_format_arg(double n) noexcept |
| : value(n) {} |
| |
| template<class Context> |
| /* explicit */ basic_format_arg<Context>::basic_format_arg(long double n) noexcept |
| : value(n) {} |
| |
| template<class Context> |
| /* explicit */ basic_format_arg<Context>::basic_format_arg(const typename basic_format_arg<Context>::char_type* s) |
| : value(s) { |
| assert(s != nullptr); |
| } |
| |
| template<class Context> |
| template<class traits> |
| /* explicit */ basic_format_arg<Context>::basic_format_arg(basic_string_view<char_type, traits> s) noexcept |
| : value(s) {} |
| |
| template<class Context> |
| template<class traits, class Allocator> |
| /* explicit */ basic_format_arg<Context>::basic_format_arg( |
| const basic_string<char_type, traits, Allocator>& s) noexcept |
| : value(basic_string_view<char_type>(s.data(), s.size())) {} |
| |
| |
| template<class Context> |
| /* explicit */ basic_format_arg<Context>::basic_format_arg(nullptr_t) noexcept |
| : value(static_cast<const void*>(nullptr)) {} |
| |
| template<class Context> |
| template<class T, typename> /* explicit */ basic_format_arg<Context>::basic_format_arg(const T* p) noexcept |
| : value(p) {} |
| |
| template<class Context> |
| /* explicit */ basic_format_arg<Context>::operator bool() const noexcept { |
| return !holds_alternative<monostate>(value); |
| } |
| } |
| |
| namespace std { |
| template<class Context> |
| class basic_format_arg<Context>::handle { |
| const void* ptr_; // exposition only |
| void (*format_)(basic_format_parse_context<char_type>&, |
| Context&, const void*); // exposition only |
| |
| template<class T> explicit handle(const T& val) noexcept; // exposition only |
| |
| friend class basic_format_arg<Context>; // exposition only |
| |
| public: |
| void format(basic_format_parse_context<char_type>&, Context& ctx) const; |
| }; |
| } |
| |
| namespace std { |
| template<class Context> |
| template<class T> /* explicit */ basic_format_arg<Context>::handle::handle(const T& val) noexcept |
| : ptr_(&val), format_([](basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx, const void* ptr) { |
| typename Context::template formatter_type<T> f; |
| parse_ctx.advance_to(f.parse(parse_ctx)); |
| format_ctx.advance_to(f.format(*static_cast<const T*>(ptr), format_ctx)); |
| }) {} |
| |
| template<class Context> |
| void basic_format_arg<Context>::handle::format(basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx) const { |
| format_(parse_ctx, format_ctx, ptr_); |
| } |
| |
| // https://fmt.dev/Text%20Formatting.html#format.visit |
| template<class Visitor, class Context> |
| auto visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg) { |
| return visit(vis, get_value(arg)); |
| } |
| } |
| |
| // https://fmt.dev/Text%20Formatting.html#format.store |
| namespace std { |
| template<class Context, class... Args> |
| struct format_arg_store { // exposition only |
| array<basic_format_arg<Context>, sizeof...(Args)> args; |
| }; |
| } |
| |
| // https://fmt.dev/Text%20Formatting.html#format.basic_args |
| namespace std { |
| template<class Context> |
| class basic_format_args { |
| size_t size_; // exposition only |
| const basic_format_arg<Context>* data_; // exposition only |
| |
| public: |
| basic_format_args() noexcept; |
| |
| template<class... Args> |
| basic_format_args(const format_arg_store<Context, Args...>& store) noexcept; |
| |
| basic_format_arg<Context> get(size_t i) const noexcept; |
| }; |
| } |
| |
| namespace std { |
| |
| template<class Context> |
| basic_format_args<Context>::basic_format_args() noexcept : size_(0) {} |
| |
| template<class Context> |
| template<class... Args> |
| basic_format_args<Context>::basic_format_args(const format_arg_store<Context, Args...>& store) noexcept |
| : size_(sizeof...(Args)), data_(store.args.data()) {} |
| |
| template<class Context> |
| basic_format_arg<Context> basic_format_args<Context>::get(size_t i) const noexcept { |
| return i < size_ ? data_[i] : basic_format_arg<Context>(); |
| } |
| } |
| |
| namespace std { |
| // https://fmt.dev/Text%20Formatting.html#format.make_args |
| template<class Context /*= format_context*/, class... Args> |
| format_arg_store<Context, Args...> make_format_args(const Args&... args) { |
| return {basic_format_arg<Context>(args)...}; |
| } |
| |
| // https://fmt.dev/Text%20Formatting.html#format.make_wargs |
| template<class... Args> |
| format_arg_store<wformat_context, Args...> make_wformat_args(const Args&... args) { |
| return make_format_args<wformat_context>(args...); |
| } |
| } |
| |
| namespace std { |
| namespace detail { |
| |
| template <typename OutputIt, typename Char> |
| class arg_formatter |
| : public fmt::detail::arg_formatter_base<OutputIt, Char, error_handler> { |
| private: |
| using char_type = Char; |
| using base = fmt::detail::arg_formatter_base<OutputIt, Char, error_handler>; |
| using format_context = std::basic_format_context<OutputIt, Char>; |
| using parse_context = basic_format_parse_context<Char>; |
| |
| parse_context* parse_ctx_; |
| format_context& ctx_; |
| |
| public: |
| using iterator = OutputIt; |
| using format_specs = typename base::format_specs; |
| |
| /** |
| \rst |
| Constructs an argument formatter object. |
| *ctx* is a reference to the formatting context, |
| *spec* contains format specifier information for standard argument types. |
| \endrst |
| */ |
| arg_formatter(format_context& ctx, parse_context* parse_ctx = nullptr, fmt::format_specs* spec = nullptr) |
| : base(ctx.out(), spec, {}), parse_ctx_(parse_ctx), ctx_(ctx) {} |
| |
| using base::operator(); |
| |
| /** Formats an argument of a user-defined type. */ |
| iterator operator()(typename std::basic_format_arg<format_context>::handle handle) { |
| handle.format(*parse_ctx_, ctx_); |
| return this->out(); |
| } |
| |
| iterator operator()(monostate) { |
| throw format_error(""); |
| } |
| }; |
| |
| template <typename Context> |
| inline fmt::detail::type get_type(basic_format_arg<Context> arg) { |
| return visit_format_arg([&] (auto val) { |
| using char_type = typename Context::char_type; |
| using T = decltype(val); |
| if (std::is_same_v<T, monostate>) |
| return fmt::detail::type::none_type; |
| if (std::is_same_v<T, bool>) |
| return fmt::detail::type::bool_type; |
| if (std::is_same_v<T, char_type>) |
| return fmt::detail::type::char_type; |
| if (std::is_same_v<T, int>) |
| return fmt::detail::type::int_type; |
| if (std::is_same_v<T, unsigned int>) |
| return fmt::detail::type::uint_type; |
| if (std::is_same_v<T, long long int>) |
| return fmt::detail::type::long_long_type; |
| if (std::is_same_v<T, unsigned long long int>) |
| return fmt::detail::type::ulong_long_type; |
| if (std::is_same_v<T, double>) |
| return fmt::detail::type::double_type; |
| if (std::is_same_v<T, long double>) |
| return fmt::detail::type::long_double_type; |
| if (std::is_same_v<T, const char_type*>) |
| return fmt::detail::type::cstring_type; |
| if (std::is_same_v<T, basic_string_view<char_type>>) |
| return fmt::detail::type::string_type; |
| if (std::is_same_v<T, const void*>) |
| return fmt::detail::type::pointer_type; |
| assert(get_value(arg).index() == 12); |
| return fmt::detail::type::custom_type; |
| }, arg); |
| } |
| |
| template <typename Context> |
| class custom_formatter { |
| private: |
| using parse_context = basic_format_parse_context<typename Context::char_type>; |
| parse_context& parse_ctx_; |
| Context& format_ctx_; |
| |
| public: |
| custom_formatter(parse_context& parse_ctx, Context& ctx) : parse_ctx_(parse_ctx), format_ctx_(ctx) {} |
| |
| bool operator()(typename basic_format_arg<Context>::handle h) const { |
| h.format(parse_ctx_, format_ctx_); |
| return true; |
| } |
| |
| template <typename T> bool operator()(T) const { return false; } |
| }; |
| |
| template <typename ArgFormatter, typename Char, typename Context> |
| struct format_handler : detail::error_handler { |
| using iterator = typename ArgFormatter::iterator; |
| |
| format_handler(iterator out, basic_string_view<Char> str, |
| basic_format_args<Context> format_args, |
| fmt::detail::locale_ref loc) |
| : parse_ctx(str), context(out, format_args, loc) {} |
| |
| void on_text(const Char* begin, const Char* end) { |
| auto size = fmt::detail::to_unsigned(end - begin); |
| auto out = context.out(); |
| auto&& it = fmt::detail::reserve(out, size); |
| it = std::copy_n(begin, size, it); |
| context.advance_to(out); |
| } |
| |
| int on_arg_id() { return parse_ctx.next_arg_id(); } |
| int on_arg_id(unsigned id) { return parse_ctx.check_arg_id(id), id; } |
| int on_arg_id(fmt::basic_string_view<Char>) { return 0; } |
| |
| void on_replacement_field(int id, const Char* p) { |
| auto arg = context.arg(id); |
| parse_ctx.advance_to(parse_ctx.begin() + (p - &*parse_ctx.begin())); |
| custom_formatter<Context> f(parse_ctx, context); |
| if (!visit_format_arg(f, arg)) |
| context.advance_to(visit_format_arg(ArgFormatter(context, &parse_ctx), arg)); |
| } |
| |
| const Char* on_format_specs(int id, const Char* begin, const Char* end) { |
| auto arg = context.arg(id); |
| parse_ctx.advance_to(parse_ctx.begin() + (begin - &*parse_ctx.begin())); |
| custom_formatter<Context> f(parse_ctx, context); |
| if (visit_format_arg(f, arg)) return &*parse_ctx.begin(); |
| fmt::basic_format_specs<Char> specs; |
| using fmt::detail::specs_handler; |
| using parse_context = basic_format_parse_context<Char>; |
| fmt::detail::specs_checker<specs_handler<parse_context, Context>> handler( |
| specs_handler<parse_context, Context>(specs, parse_ctx, context), get_type(arg)); |
| begin = parse_format_specs(begin, end, handler); |
| if (begin == end || *begin != '}') on_error("missing '}' in format string"); |
| parse_ctx.advance_to(parse_ctx.begin() + (begin - &*parse_ctx.begin())); |
| context.advance_to(visit_format_arg(ArgFormatter(context, &parse_ctx, &specs), arg)); |
| return begin; |
| } |
| |
| basic_format_parse_context<Char> parse_ctx; |
| Context context; |
| }; |
| |
| template <typename T, typename Char> |
| struct formatter { |
| // Parses format specifiers stopping either at the end of the range or at the |
| // terminating '}'. |
| template <typename ParseContext> |
| FMT_CONSTEXPR typename ParseContext::iterator parse(ParseContext& ctx) { |
| namespace detail = fmt::detail; |
| typedef detail::dynamic_specs_handler<ParseContext> handler_type; |
| auto type = detail::mapped_type_constant<T, fmt::buffer_context<Char>>::value; |
| detail::specs_checker<handler_type> handler(handler_type(specs_, ctx), |
| type); |
| auto it = parse_format_specs(ctx.begin(), ctx.end(), handler); |
| auto type_spec = specs_.type; |
| auto eh = ctx.error_handler(); |
| switch (type) { |
| case detail::type::none_type: |
| FMT_ASSERT(false, "invalid argument type"); |
| break; |
| case detail::type::int_type: |
| case detail::type::uint_type: |
| case detail::type::long_long_type: |
| case detail::type::ulong_long_type: |
| case detail::type::bool_type: |
| handle_int_type_spec(type_spec, |
| detail::int_type_checker<decltype(eh)>(eh)); |
| break; |
| case detail::type::char_type: |
| handle_char_specs( |
| &specs_, detail::char_specs_checker<decltype(eh)>(type_spec, eh)); |
| break; |
| case detail::type::double_type: |
| case detail::type::long_double_type: |
| detail::parse_float_type_spec(specs_, eh); |
| break; |
| case detail::type::cstring_type: |
| detail::handle_cstring_type_spec( |
| type_spec, detail::cstring_type_checker<decltype(eh)>(eh)); |
| break; |
| case detail::type::string_type: |
| detail::check_string_type_spec(type_spec, eh); |
| break; |
| case detail::type::pointer_type: |
| detail::check_pointer_type_spec(type_spec, eh); |
| break; |
| case detail::type::custom_type: |
| // Custom format specifiers should be checked in parse functions of |
| // formatter specializations. |
| break; |
| } |
| return it; |
| } |
| |
| template <typename FormatContext> |
| auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) { |
| fmt::detail::handle_dynamic_spec<fmt::detail::width_checker>( |
| specs_.width, specs_.width_ref, ctx); |
| fmt::detail::handle_dynamic_spec<fmt::detail::precision_checker>( |
| specs_.precision, specs_.precision_ref, ctx); |
| using af = arg_formatter<typename FormatContext::iterator, |
| typename FormatContext::char_type>; |
| return visit_format_arg(af(ctx, nullptr, &specs_), |
| basic_format_arg<FormatContext>(val)); |
| } |
| |
| private: |
| fmt::detail::dynamic_format_specs<Char> specs_; |
| }; |
| } // namespace detail |
| |
| // https://fmt.dev/Text%20Formatting.html#format.functions |
| template<class... Args> |
| string format(string_view fmt, const Args&... args) { |
| return vformat(fmt, make_format_args(args...)); |
| } |
| |
| template<class... Args> |
| wstring format(wstring_view fmt, const Args&... args) { |
| return vformat(fmt, make_wformat_args(args...)); |
| } |
| |
| string vformat(string_view fmt, format_args args) { |
| fmt::memory_buffer mbuf; |
| fmt::detail::buffer<char>& buf = mbuf; |
| using af = detail::arg_formatter<fmt::format_context::iterator, char>; |
| detail::format_handler<af, char, format_context> |
| h(fmt::detail::buffer_appender<char>(buf), fmt, args, {}); |
| fmt::detail::parse_format_string<false>(fmt::to_string_view(fmt), h); |
| return to_string(mbuf); |
| } |
| |
| wstring vformat(wstring_view fmt, wformat_args args); |
| |
| template<class Out, class... Args> |
| Out format_to(Out out, string_view fmt, const Args&... args) { |
| using context = basic_format_context<Out, decltype(fmt)::value_type>; |
| return vformat_to(out, fmt, make_format_args<context>(args...)); |
| } |
| |
| template<class Out, class... Args> |
| Out format_to(Out out, wstring_view fmt, const Args&... args) { |
| using context = basic_format_context<Out, decltype(fmt)::value_type>; |
| return vformat_to(out, fmt, make_format_args<context>(args...)); |
| } |
| |
| template<class Out> |
| Out vformat_to(Out out, string_view fmt, format_args_t<fmt::type_identity_t<Out>, char> args) { |
| using af = detail::arg_formatter<Out, char>; |
| detail::format_handler<af, char, basic_format_context<Out, char>> |
| h(out, fmt, args, {}); |
| fmt::detail::parse_format_string<false>(fmt::to_string_view(fmt), h); |
| return h.context.out(); |
| } |
| |
| template<class Out> |
| Out vformat_to(Out out, wstring_view fmt, format_args_t<fmt::type_identity_t<Out>, wchar_t> args); |
| |
| template<class Out, class... Args> |
| format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n, |
| string_view fmt, const Args&... args); |
| template<class Out, class... Args> |
| format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n, |
| wstring_view fmt, const Args&... args); |
| |
| template<class... Args> |
| size_t formatted_size(string_view fmt, const Args&... args); |
| template<class... Args> |
| size_t formatted_size(wstring_view fmt, const Args&... args); |
| |
| #define charT char |
| |
| template<> struct formatter<charT, charT> : detail::formatter<charT, charT> {}; |
| |
| template<> struct formatter<char, wchar_t>; |
| |
| template<> struct formatter<charT*, charT> : detail::formatter<const charT*, charT> {}; |
| |
| template<> struct formatter<const charT*, charT> : detail::formatter<const charT*, charT> {}; |
| |
| template<size_t N> struct formatter<const charT[N], charT> |
| : detail::formatter<std::basic_string_view<charT>, charT> {}; |
| |
| template<class traits, class Allocator> |
| struct formatter<basic_string<charT, traits, Allocator>, charT> |
| : detail::formatter<std::basic_string_view<charT>, charT> {}; |
| |
| template<class traits> |
| struct formatter<basic_string_view<charT, traits>, charT> |
| : detail::formatter<std::basic_string_view<charT>, charT> {}; |
| |
| template <> struct formatter<nullptr_t, charT> : detail::formatter<const void*, charT> {}; |
| template <> struct formatter<void*, charT> : detail::formatter<const void*, charT> {}; |
| template <> struct formatter<const void*, charT> : detail::formatter<const void*, charT> {}; |
| template <> struct formatter<bool, charT> : detail::formatter<bool, charT> {}; |
| |
| template <> struct formatter<signed char, charT> : detail::formatter<int, charT> {}; |
| template <> struct formatter<short, charT> : detail::formatter<int, charT> {}; |
| template <> struct formatter<int, charT> : detail::formatter<int, charT> {}; |
| template <> struct formatter<long, charT> |
| : detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), int, long long>, charT> {}; |
| template <> struct formatter<long long, charT> : detail::formatter<long long, charT> {}; |
| template <> struct formatter<unsigned char, charT> : detail::formatter<unsigned int, charT> {}; |
| template <> struct formatter<unsigned short, charT> : detail::formatter<unsigned int, charT> {}; |
| template <> struct formatter<unsigned int, charT> : detail::formatter<unsigned int, charT> {}; |
| template <> struct formatter<unsigned long, charT> |
| : detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), unsigned, unsigned long long>, charT> {}; |
| template <> struct formatter<unsigned long long, charT> : detail::formatter<unsigned long long, charT> {}; |
| |
| template <> struct formatter<float, charT> : detail::formatter<double, charT> {}; |
| template <> struct formatter<double, charT> : detail::formatter<double, charT> {}; |
| template <> struct formatter<long double, charT> : detail::formatter<long double, charT> {}; |
| |
| #undef charT |
| |
| #define charT wchar_t |
| |
| template<> struct formatter<charT, charT> : detail::formatter<charT, charT> {}; |
| |
| template<> struct formatter<char, wchar_t> : detail::formatter<charT, charT> {}; |
| |
| template<> struct formatter<charT*, charT> : detail::formatter<const charT*, charT> {}; |
| |
| template<> struct formatter<const charT*, charT> : detail::formatter<const charT*, charT> {}; |
| |
| template<size_t N> struct formatter<const charT[N], charT> |
| : detail::formatter<std::basic_string_view<charT>, charT> {}; |
| |
| template<class traits, class Allocator> |
| struct formatter<std::basic_string<charT, traits, Allocator>, charT> |
| : detail::formatter<std::basic_string_view<charT>, charT> {}; |
| |
| template<class traits> |
| struct formatter<std::basic_string_view<charT, traits>, charT> |
| : detail::formatter<std::basic_string_view<charT>, charT> {}; |
| |
| template <> struct formatter<nullptr_t, charT> : detail::formatter<const void*, charT> {}; |
| template <> struct formatter<void*, charT> : detail::formatter<const void*, charT> {}; |
| template <> struct formatter<const void*, charT> : detail::formatter<const void*, charT> {}; |
| template <> struct formatter<bool, charT> : detail::formatter<bool, charT> {}; |
| |
| template <> struct formatter<signed char, charT> : detail::formatter<int, charT> {}; |
| template <> struct formatter<short, charT> : detail::formatter<int, charT> {}; |
| template <> struct formatter<int, charT> : detail::formatter<int, charT> {}; |
| template <> struct formatter<long, charT> |
| : detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), int, long long>, charT> {}; |
| template <> struct formatter<long long, charT> : detail::formatter<long long, charT> {}; |
| template <> struct formatter<unsigned char, charT> : detail::formatter<unsigned int, charT> {}; |
| template <> struct formatter<unsigned short, charT> : detail::formatter<unsigned int, charT> {}; |
| template <> struct formatter<unsigned int, charT> : detail::formatter<unsigned int, charT> {}; |
| template <> struct formatter<unsigned long, charT> |
| : detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), unsigned, unsigned long long>, charT> {}; |
| template <> struct formatter<unsigned long long, charT> : detail::formatter<unsigned long long, charT> {}; |
| |
| template <> struct formatter<float, charT> : detail::formatter<double, charT> {}; |
| template <> struct formatter<double, charT> : detail::formatter<double, charT> {}; |
| template <> struct formatter<long double, charT> : detail::formatter<long double, charT> {}; |
| |
| #undef charT |
| |
| template<> struct formatter<const wchar_t, char> { |
| formatter() = delete; |
| }; |
| } |
| |
| #endif // FMT_FORMAT_ |