Remove std-format-test
This commit is contained in:
parent
784e2a7b42
commit
664cd6067d
@ -138,12 +138,6 @@ endif ()
|
||||
message(STATUS "FMT_PEDANTIC: ${FMT_PEDANTIC}")
|
||||
|
||||
if (FMT_PEDANTIC AND CXX_STANDARD LESS 20)
|
||||
# MSVC fails to compile GMock when C++17 is enabled.
|
||||
if (FMT_HAS_VARIANT AND NOT MSVC)
|
||||
add_fmt_test(std-format-test)
|
||||
set_property(TARGET std-format-test PROPERTY CXX_STANDARD 17)
|
||||
endif ()
|
||||
|
||||
# Test that the library can be compiled with exceptions disabled.
|
||||
# -fno-exception is broken in icc: https://github.com/fmtlib/fmt/issues/822.
|
||||
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
|
||||
|
856
test/format
856
test/format
@ -1,856 +0,0 @@
|
||||
// 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 <algorithm>
|
||||
#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_
|
@ -1,161 +0,0 @@
|
||||
#include <format>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
TEST(std_format_test, escaping) {
|
||||
using namespace std;
|
||||
string s = format("{0}-{{", 8); // s == "8-{"
|
||||
EXPECT_EQ(s, "8-{");
|
||||
}
|
||||
|
||||
TEST(std_format_test, indexing) {
|
||||
using namespace std;
|
||||
string s0 = format("{} to {}", "a", "b"); // OK: automatic indexing
|
||||
string s1 = format("{1} to {0}", "a", "b"); // OK: manual indexing
|
||||
EXPECT_EQ(s0, "a to b");
|
||||
EXPECT_EQ(s1, "b to a");
|
||||
// Error: mixing automatic and manual indexing
|
||||
EXPECT_THROW(string s2 = format("{0} to {}", "a", "b"), std::format_error);
|
||||
// Error: mixing automatic and manual indexing
|
||||
EXPECT_THROW(string s3 = format("{} to {1}", "a", "b"), std::format_error);
|
||||
}
|
||||
|
||||
TEST(std_format_test, alignment) {
|
||||
using namespace std;
|
||||
char c = 120;
|
||||
string s0 = format("{:6}", 42); // s0 == " 42"
|
||||
string s1 = format("{:6}", 'x'); // s1 == "x "
|
||||
string s2 = format("{:*<6}", 'x'); // s2 == "x*****"
|
||||
string s3 = format("{:*>6}", 'x'); // s3 == "*****x"
|
||||
string s4 = format("{:*^6}", 'x'); // s4 == "**x***"
|
||||
// Error: '=' with charT and no integer presentation type
|
||||
EXPECT_THROW(string s5 = format("{:=6}", 'x'), std::format_error);
|
||||
string s6 = format("{:6d}", c); // s6 == " 120"
|
||||
string s7 = format("{:6}", true); // s9 == "true "
|
||||
EXPECT_EQ(s0, " 42");
|
||||
EXPECT_EQ(s1, "x ");
|
||||
EXPECT_EQ(s2, "x*****");
|
||||
EXPECT_EQ(s3, "*****x");
|
||||
EXPECT_EQ(s4, "**x***");
|
||||
EXPECT_EQ(s6, " 120");
|
||||
EXPECT_EQ(s7, "true ");
|
||||
}
|
||||
|
||||
TEST(std_format_test, float) {
|
||||
using namespace std;
|
||||
double inf = numeric_limits<double>::infinity();
|
||||
double nan = numeric_limits<double>::quiet_NaN();
|
||||
string s0 = format("{0:} {0:+} {0:-} {0: }", 1); // s0 == "1 +1 1 1"
|
||||
string s1 = format("{0:} {0:+} {0:-} {0: }", -1); // s1 == "-1 -1 -1 -1"
|
||||
string s2 =
|
||||
format("{0:} {0:+} {0:-} {0: }", inf); // s2 == "inf +inf inf inf"
|
||||
string s3 =
|
||||
format("{0:} {0:+} {0:-} {0: }", nan); // s3 == "nan +nan nan nan"
|
||||
EXPECT_EQ(s0, "1 +1 1 1");
|
||||
EXPECT_EQ(s1, "-1 -1 -1 -1");
|
||||
EXPECT_EQ(s2, "inf +inf inf inf");
|
||||
EXPECT_EQ(s3, "nan +nan nan nan");
|
||||
}
|
||||
|
||||
TEST(std_format_test, int) {
|
||||
using namespace std;
|
||||
string s0 = format("{}", 42); // s0 == "42"
|
||||
string s1 = format("{0:b} {0:d} {0:o} {0:x}", 42); // s1 == "101010 42 52 2a"
|
||||
string s2 = format("{0:#x} {0:#X}", 42); // s2 == "0x2a 0X2A"
|
||||
string s3 = format("{:L}", 1234); // s3 == "1234" (depends on the locale)
|
||||
EXPECT_EQ(s0, "42");
|
||||
EXPECT_EQ(s1, "101010 42 52 2a");
|
||||
EXPECT_EQ(s2, "0x2a 0X2A");
|
||||
EXPECT_EQ(s3, "1234");
|
||||
}
|
||||
|
||||
#include <format>
|
||||
|
||||
enum color { red, green, blue };
|
||||
|
||||
const char* color_names[] = {"red", "green", "blue"};
|
||||
|
||||
template <> struct std::formatter<color> : std::formatter<const char*> {
|
||||
auto format(color c, format_context& ctx) {
|
||||
return formatter<const char*>::format(color_names[c], ctx);
|
||||
}
|
||||
};
|
||||
|
||||
struct err {};
|
||||
|
||||
TEST(std_format_test, formatter) {
|
||||
std::string s0 = std::format("{}", 42); // OK: library-provided formatter
|
||||
// std::string s1 = std::format("{}", L"foo"); // Ill-formed: disabled
|
||||
// formatter
|
||||
std::string s2 = std::format("{}", red); // OK: user-provided formatter
|
||||
// std::string s3 = std::format("{}", err{}); // Ill-formed: disabled
|
||||
// formatter
|
||||
EXPECT_EQ(s0, "42");
|
||||
EXPECT_EQ(s2, "red");
|
||||
}
|
||||
|
||||
struct S {
|
||||
int value;
|
||||
};
|
||||
|
||||
template <> struct std::formatter<S> {
|
||||
size_t width_arg_id = 0;
|
||||
|
||||
// Parses a width argument id in the format { <digit> }.
|
||||
constexpr auto parse(format_parse_context& ctx) {
|
||||
constexpr auto is_ascii_digit = [](const char c) {
|
||||
return c >= '0' && c <= '9';
|
||||
};
|
||||
|
||||
auto iter = ctx.begin();
|
||||
// auto get_char = [&]() { return iter != ctx.end() ? *iter : 0; };
|
||||
auto get_char = [&]() { return iter != ctx.end() ? *iter : '\0'; };
|
||||
if (get_char() != '{') return iter;
|
||||
++iter;
|
||||
char c = get_char();
|
||||
if (!is_ascii_digit(c) || (++iter, get_char()) != '}')
|
||||
throw format_error("invalid format");
|
||||
width_arg_id = fmt::detail::to_unsigned(c - '0');
|
||||
ctx.check_arg_id(width_arg_id);
|
||||
return ++iter;
|
||||
}
|
||||
|
||||
// Formats S with width given by the argument width_arg_id.
|
||||
auto format(S s, format_context& ctx) {
|
||||
int width = visit_format_arg(
|
||||
[](auto value) -> int {
|
||||
using type = decltype(value);
|
||||
if constexpr (!is_integral_v<type> || is_same_v<type, bool>)
|
||||
throw format_error("width is not integral");
|
||||
// else if (value < 0 || value > numeric_limits<int>::max())
|
||||
else if (fmt::detail::is_negative(value) ||
|
||||
value > numeric_limits<int>::max())
|
||||
throw format_error("invalid width");
|
||||
else
|
||||
return static_cast<int>(value);
|
||||
},
|
||||
ctx.arg(width_arg_id));
|
||||
return format_to(ctx.out(), "{0:{1}}", s.value, width);
|
||||
}
|
||||
};
|
||||
|
||||
TEST(std_format_test, parsing) {
|
||||
std::string s = std::format("{0:{1}}", S{42}, 10); // s == " 42"
|
||||
EXPECT_EQ(s, " 42");
|
||||
}
|
||||
|
||||
#if FMT_USE_INT128
|
||||
template <> struct std::formatter<__int128_t> : std::formatter<long long> {
|
||||
auto format(__int128_t n, format_context& ctx) {
|
||||
// Format as a long long since we only want to check if it is possible to
|
||||
// specialize formatter for __int128_t.
|
||||
return formatter<long long>::format(static_cast<long long>(n), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
TEST(std_format_test, int128) {
|
||||
__int128_t n = 42;
|
||||
auto s = std::format("{}", n);
|
||||
EXPECT_EQ(s, "42");
|
||||
}
|
||||
#endif // FMT_USE_INT128
|
Loading…
Reference in New Issue
Block a user