// 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 #include #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 constexpr bool Integral = is_integral_v; template 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 basic_format_parse_context; using format_parse_context = basic_format_parse_context; using wformat_parse_context = basic_format_parse_context; template class basic_format_context; using format_context = basic_format_context< /* unspecified */ std::back_insert_iterator>, char>; using wformat_context = basic_format_context< /* unspecified */ std::back_insert_iterator>, wchar_t>; template struct formatter { formatter() = delete; }; // [format.arguments], arguments template class basic_format_arg; template /* see below */ auto visit_format_arg(Visitor&& vis, basic_format_arg arg); template struct format_arg_store; // exposition only template class basic_format_args; using format_args = basic_format_args; using wformat_args = basic_format_args; template using format_args_t = basic_format_args>; template format_arg_store make_format_args(const Args&... args); template format_arg_store make_wformat_args(const Args&... args); // [format.functions], formatting functions template string format(string_view fmt, const Args&... args); template 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 Out format_to(Out out, string_view fmt, const Args&... args); template Out format_to(Out out, wstring_view fmt, const Args&... args); template Out vformat_to(Out out, string_view fmt, format_args_t, char> args); template Out vformat_to(Out out, wstring_view fmt, format_args_t, wchar_t> args); template struct format_to_n_result { Out out; iter_difference_t size; }; template format_to_n_result format_to_n(Out out, iter_difference_t n, string_view fmt, const Args&... args); template format_to_n_result format_to_n(Out out, iter_difference_t n, wstring_view fmt, const Args&... args); template size_t formatted_size(string_view fmt, const Args&... args); template 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 basic_format_parse_context { public: using char_type = charT; using const_iterator = typename basic_string_view::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 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 /* explicit */ constexpr basic_format_parse_context:: basic_format_parse_context(basic_string_view fmt, size_t num_args) noexcept : begin_(fmt.begin()), end_(fmt.end()), indexing_(unknown), next_arg_id_(0), num_args_(num_args) {} template constexpr typename basic_format_parse_context::const_iterator basic_format_parse_context::begin() const noexcept { return begin_; } template constexpr typename basic_format_parse_context::const_iterator basic_format_parse_context::end() const noexcept { return end_; } template constexpr void basic_format_parse_context::advance_to(typename basic_format_parse_context::iterator it) { begin_ = it; } template constexpr size_t basic_format_parse_context::next_arg_id() { if (indexing_ == manual) throw format_error("manual to automatic indexing"); if (indexing_ == unknown) indexing_ = automatic; return next_arg_id_++; } template constexpr void basic_format_parse_context::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 basic_format_context { basic_format_args args_; // exposition only Out out_; // exposition only public: using iterator = Out; using char_type = charT; template using formatter_type = formatter; basic_format_arg arg(size_t id) const; iterator out(); void advance_to(iterator it); // Implementation details: using format_arg = basic_format_arg; basic_format_context(Out out, basic_format_args args, fmt::internal::locale_ref) : args_(args), out_(out) {} detail::error_handler error_handler() const { return {}; } basic_format_arg arg(fmt::basic_string_view) const { return {}; // unused: named arguments are not supported yet } void on_error(const char* msg) { error_handler().on_error(msg); } }; } namespace std { template basic_format_arg> basic_format_context::arg(size_t id) const { return args_.get(id); } template typename basic_format_context::iterator basic_format_context::out() { return out_; } template void basic_format_context::advance_to(typename basic_format_context::iterator it) { out_ = it; } } namespace std { namespace detail { template constexpr bool is_standard_integer_v = std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v; template constexpr bool is_standard_unsigned_integer_v = std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v; template struct formatter; } } // https://fmt.dev/Text%20Formatting.html#format.arg namespace std { template class basic_format_arg { public: class handle; private: using char_type = typename Context::char_type; // exposition only variant, const void*, handle> value; // exposition only template || std::is_same_v || (std::is_same_v && std::is_same_v) || detail::is_standard_integer_v || detail::is_standard_unsigned_integer_v || sizeof(typename Context::template formatter_type().format(declval(), declval())) != 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 explicit basic_format_arg( basic_string_view s) noexcept; // exposition only template explicit basic_format_arg( const basic_string& s) noexcept; // exposition only explicit basic_format_arg(nullptr_t) noexcept; // exposition only template>> explicit basic_format_arg(const T* p) noexcept; // exposition only // Fails due to a bug in clang //template // friend auto visit_format_arg(Visitor&& vis, // basic_format_arg arg); // exposition only friend auto get_value(basic_format_arg arg) { return arg.value; } template friend struct detail::formatter; template friend format_arg_store make_format_args(const Args&... args); // exposition only public: basic_format_arg() noexcept; explicit operator bool() const noexcept; }; } namespace std { template basic_format_arg::basic_format_arg() noexcept {} template template /* explicit */ basic_format_arg::basic_format_arg(const T& v) noexcept { if constexpr (std::is_same_v || std::is_same_v) value = v; else if constexpr (std::is_same_v && std::is_same_v) value = static_cast(v); else if constexpr (detail::is_standard_integer_v && sizeof(T) <= sizeof(int)) value = static_cast(v); else if constexpr (detail::is_standard_unsigned_integer_v && sizeof(T) <= sizeof(unsigned)) value = static_cast(v); else if constexpr (detail::is_standard_integer_v) value = static_cast(v); else if constexpr (detail::is_standard_unsigned_integer_v) value = static_cast(v); else if constexpr (sizeof(typename Context::template formatter_type().format(declval(), declval())) != 0) value = handle(v); } template /* explicit */ basic_format_arg::basic_format_arg(float n) noexcept : value(static_cast(n)) {} template /* explicit */ basic_format_arg::basic_format_arg(double n) noexcept : value(n) {} template /* explicit */ basic_format_arg::basic_format_arg(long double n) noexcept : value(n) {} template /* explicit */ basic_format_arg::basic_format_arg(const typename basic_format_arg::char_type* s) : value(s) { assert(s != nullptr); } template template /* explicit */ basic_format_arg::basic_format_arg(basic_string_view s) noexcept : value(s) {} template template /* explicit */ basic_format_arg::basic_format_arg( const basic_string& s) noexcept : value(basic_string_view(s.data(), s.size())) {} template /* explicit */ basic_format_arg::basic_format_arg(nullptr_t) noexcept : value(static_cast(nullptr)) {} template template /* explicit */ basic_format_arg::basic_format_arg(const T* p) noexcept : value(p) {} template /* explicit */ basic_format_arg::operator bool() const noexcept { return !holds_alternative(value); } } namespace std { template class basic_format_arg::handle { const void* ptr_; // exposition only void (*format_)(basic_format_parse_context&, Context&, const void*); // exposition only template explicit handle(const T& val) noexcept; // exposition only friend class basic_format_arg; // exposition only public: void format(basic_format_parse_context&, Context& ctx) const; }; } namespace std { template template /* explicit */ basic_format_arg::handle::handle(const T& val) noexcept : ptr_(&val), format_([](basic_format_parse_context& parse_ctx, Context& format_ctx, const void* ptr) { typename Context::template formatter_type f; parse_ctx.advance_to(f.parse(parse_ctx)); format_ctx.advance_to(f.format(*static_cast(ptr), format_ctx)); }) {} template void basic_format_arg::handle::format(basic_format_parse_context& parse_ctx, Context& format_ctx) const { format_(parse_ctx, format_ctx, ptr_); } // https://fmt.dev/Text%20Formatting.html#format.visit template auto visit_format_arg(Visitor&& vis, basic_format_arg arg) { return visit(vis, get_value(arg)); } } // https://fmt.dev/Text%20Formatting.html#format.store namespace std { template struct format_arg_store { // exposition only array, sizeof...(Args)> args; }; } // https://fmt.dev/Text%20Formatting.html#format.basic_args namespace std { template class basic_format_args { size_t size_; // exposition only const basic_format_arg* data_; // exposition only public: basic_format_args() noexcept; template basic_format_args(const format_arg_store& store) noexcept; basic_format_arg get(size_t i) const noexcept; }; } namespace std { template basic_format_args::basic_format_args() noexcept : size_(0) {} template template basic_format_args::basic_format_args(const format_arg_store& store) noexcept : size_(sizeof...(Args)), data_(store.args.data()) {} template basic_format_arg basic_format_args::get(size_t i) const noexcept { return i < size_ ? data_[i] : basic_format_arg(); } } namespace std { // https://fmt.dev/Text%20Formatting.html#format.make_args template format_arg_store make_format_args(const Args&... args) { return {basic_format_arg(args)...}; } // https://fmt.dev/Text%20Formatting.html#format.make_wargs template format_arg_store make_wformat_args(const Args&... args) { return make_format_args(args...); } } namespace std { namespace detail { template class arg_formatter : public fmt::internal::arg_formatter_base { private: using char_type = typename Range::value_type; using base = fmt::internal::arg_formatter_base; using format_context = std::basic_format_context; using parse_context = basic_format_parse_context; parse_context* parse_ctx_; format_context& ctx_; public: typedef Range range; typedef typename base::iterator iterator; typedef typename base::format_specs 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(Range(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::handle handle) { handle.format(*parse_ctx_, ctx_); return this->out(); } iterator operator()(monostate) { throw format_error(""); } }; template inline fmt::internal::type get_type(basic_format_arg arg) { return visit_format_arg([&] (auto val) { using char_type = typename Context::char_type; using T = decltype(val); if (std::is_same_v) return fmt::internal::type::none_type; if (std::is_same_v) return fmt::internal::type::bool_type; if (std::is_same_v) return fmt::internal::type::char_type; if (std::is_same_v) return fmt::internal::type::int_type; if (std::is_same_v) return fmt::internal::type::uint_type; if (std::is_same_v) return fmt::internal::type::long_long_type; if (std::is_same_v) return fmt::internal::type::ulong_long_type; if (std::is_same_v) return fmt::internal::type::double_type; if (std::is_same_v) return fmt::internal::type::long_double_type; if (std::is_same_v) return fmt::internal::type::cstring_type; if (std::is_same_v>) return fmt::internal::type::string_type; if (std::is_same_v) return fmt::internal::type::pointer_type; assert(get_value(arg).index() == 12); return fmt::internal::type::custom_type; }, arg); } template class custom_formatter { private: using parse_context = basic_format_parse_context; 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::handle h) const { h.format(parse_ctx_, format_ctx_); return true; } template bool operator()(T) const { return false; } }; template struct format_handler : detail::error_handler { typedef typename ArgFormatter::range range; format_handler(range r, basic_string_view str, basic_format_args format_args, fmt::internal::locale_ref loc) : parse_ctx(str), context(r.begin(), format_args, loc) {} void on_text(const Char* begin, const Char* end) { auto size = fmt::internal::to_unsigned(end - begin); auto out = context.out(); auto&& it = fmt::internal::reserve(out, size); it = std::copy_n(begin, size, it); context.advance_to(out); } void on_arg_id() { arg = context.arg(parse_ctx.next_arg_id()); } void on_arg_id(unsigned id) { parse_ctx.check_arg_id(id); arg = context.arg(id); } void on_arg_id(fmt::basic_string_view) {} void on_replacement_field(const Char* p) { parse_ctx.advance_to(parse_ctx.begin() + (p - &*parse_ctx.begin())); custom_formatter 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(const Char* begin, const Char* end) { parse_ctx.advance_to(parse_ctx.begin() + (begin - &*parse_ctx.begin())); custom_formatter f(parse_ctx, context); if (visit_format_arg(f, arg)) return &*parse_ctx.begin(); fmt::basic_format_specs specs; using fmt::internal::specs_handler; using parse_context = basic_format_parse_context; fmt::internal::specs_checker> handler( specs_handler(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 parse_ctx; Context context; basic_format_arg arg; }; template struct formatter { // Parses format specifiers stopping either at the end of the range or at the // terminating '}'. template FMT_CONSTEXPR typename ParseContext::iterator parse(ParseContext& ctx) { namespace internal = fmt::internal; typedef internal::dynamic_specs_handler handler_type; auto type = internal::mapped_type_constant>::value; internal::specs_checker 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 internal::type::none_type: FMT_ASSERT(false, "invalid argument type"); break; case internal::type::int_type: case internal::type::uint_type: case internal::type::long_long_type: case internal::type::ulong_long_type: case internal::type::bool_type: handle_int_type_spec(type_spec, internal::int_type_checker(eh)); break; case internal::type::char_type: handle_char_specs( &specs_, internal::char_specs_checker(type_spec, eh)); break; case internal::type::double_type: case internal::type::long_double_type: internal::parse_float_type_spec(specs_, eh); break; case internal::type::cstring_type: internal::handle_cstring_type_spec( type_spec, internal::cstring_type_checker(eh)); break; case internal::type::string_type: internal::check_string_type_spec(type_spec, eh); break; case internal::type::pointer_type: internal::check_pointer_type_spec(type_spec, eh); break; case internal::type::custom_type: // Custom format specifiers should be checked in parse functions of // formatter specializations. break; } return it; } template auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) { fmt::internal::handle_dynamic_spec( specs_.width, specs_.width_ref, ctx); fmt::internal::handle_dynamic_spec( specs_.precision, specs_.precision_ref, ctx); using range_type = fmt::internal::output_range; return visit_format_arg(arg_formatter(ctx, nullptr, &specs_), basic_format_arg(val)); } private: fmt::internal::dynamic_format_specs specs_; }; } // namespace detail // https://fmt.dev/Text%20Formatting.html#format.functions template string format(string_view fmt, const Args&... args) { return vformat(fmt, make_format_args(args...)); } template 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::internal::buffer& buf = mbuf; using range = fmt::buffer_range; detail::format_handler, char, format_context> h(range(std::back_inserter(buf)), fmt, args, {}); fmt::internal::parse_format_string(fmt::to_string_view(fmt), h); return to_string(mbuf); } wstring vformat(wstring_view fmt, wformat_args args); template Out format_to(Out out, string_view fmt, const Args&... args) { using context = basic_format_context; return vformat_to(out, fmt, make_format_args(args...)); } template Out format_to(Out out, wstring_view fmt, const Args&... args) { using context = basic_format_context; return vformat_to(out, fmt, make_format_args(args...)); } template Out vformat_to(Out out, string_view fmt, format_args_t, char> args) { using range = fmt::internal::output_range; detail::format_handler, char, basic_format_context> h(range(out), fmt, args, {}); fmt::internal::parse_format_string(fmt::to_string_view(fmt), h); return h.context.out(); } template Out vformat_to(Out out, wstring_view fmt, format_args_t, wchar_t> args); template format_to_n_result format_to_n(Out out, iter_difference_t n, string_view fmt, const Args&... args); template format_to_n_result format_to_n(Out out, iter_difference_t n, wstring_view fmt, const Args&... args); template size_t formatted_size(string_view fmt, const Args&... args); template size_t formatted_size(wstring_view fmt, const Args&... args); #define charT char template<> struct formatter : detail::formatter {}; template<> struct formatter; template<> struct formatter : detail::formatter {}; template<> struct formatter : detail::formatter {}; template struct formatter : detail::formatter, charT> {}; template struct formatter, charT> : detail::formatter, charT> {}; template struct formatter, charT> : detail::formatter, charT> {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter, charT> {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter, charT> {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; #undef charT #define charT wchar_t template<> struct formatter : detail::formatter {}; template<> struct formatter : detail::formatter {}; template<> struct formatter : detail::formatter {}; template<> struct formatter : detail::formatter {}; template struct formatter : detail::formatter, charT> {}; template struct formatter, charT> : detail::formatter, charT> {}; template struct formatter, charT> : detail::formatter, charT> {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter, charT> {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter, charT> {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; template <> struct formatter : detail::formatter {}; #undef charT template<> struct formatter { formatter() = delete; }; } #endif // FMT_FORMAT_