mirror of
https://github.com/fmtlib/fmt.git
synced 2025-04-02 13:20:15 +00:00
Cleanup tests and format string compilation
This commit is contained in:
parent
e96a92f869
commit
ccf4ccde23
@ -135,333 +135,6 @@ const T& first(const T& value, const Tail&...) {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Part of a compiled format string. It can be either literal text or a
|
|
||||||
// replacement field.
|
|
||||||
template <typename Char> struct format_part {
|
|
||||||
enum class kind { arg_index, arg_name, text, replacement };
|
|
||||||
|
|
||||||
struct replacement {
|
|
||||||
arg_ref<Char> arg_id;
|
|
||||||
dynamic_format_specs<Char> specs;
|
|
||||||
};
|
|
||||||
|
|
||||||
kind part_kind;
|
|
||||||
union value {
|
|
||||||
int arg_index;
|
|
||||||
basic_string_view<Char> str;
|
|
||||||
replacement repl;
|
|
||||||
|
|
||||||
FMT_CONSTEXPR value(int index = 0) : arg_index(index) {}
|
|
||||||
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
|
|
||||||
FMT_CONSTEXPR value(replacement r) : repl(r) {}
|
|
||||||
} val;
|
|
||||||
// Position past the end of the argument id.
|
|
||||||
const Char* arg_id_end = nullptr;
|
|
||||||
|
|
||||||
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
|
|
||||||
: part_kind(k), val(v) {}
|
|
||||||
|
|
||||||
static FMT_CONSTEXPR format_part make_arg_index(int index) {
|
|
||||||
return format_part(kind::arg_index, index);
|
|
||||||
}
|
|
||||||
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
|
|
||||||
return format_part(kind::arg_name, name);
|
|
||||||
}
|
|
||||||
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
|
|
||||||
return format_part(kind::text, text);
|
|
||||||
}
|
|
||||||
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
|
|
||||||
return format_part(kind::replacement, repl);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char> struct part_counter {
|
|
||||||
unsigned num_parts = 0;
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
|
|
||||||
if (begin != end) ++num_parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; }
|
|
||||||
FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; }
|
|
||||||
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
|
|
||||||
return ++num_parts, 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
|
|
||||||
const Char* end) {
|
|
||||||
// Find the matching brace.
|
|
||||||
unsigned brace_counter = 0;
|
|
||||||
for (; begin != end; ++begin) {
|
|
||||||
if (*begin == '{') {
|
|
||||||
++brace_counter;
|
|
||||||
} else if (*begin == '}') {
|
|
||||||
if (brace_counter == 0u) break;
|
|
||||||
--brace_counter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_error(const char*) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Counts the number of parts in a format string.
|
|
||||||
template <typename Char>
|
|
||||||
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
|
|
||||||
part_counter<Char> counter;
|
|
||||||
parse_format_string<true>(format_str, counter);
|
|
||||||
return counter.num_parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename PartHandler>
|
|
||||||
class format_string_compiler : public error_handler {
|
|
||||||
private:
|
|
||||||
using part = format_part<Char>;
|
|
||||||
|
|
||||||
PartHandler handler_;
|
|
||||||
part part_;
|
|
||||||
basic_string_view<Char> format_str_;
|
|
||||||
basic_format_parse_context<Char> parse_context_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
|
|
||||||
PartHandler handler)
|
|
||||||
: handler_(handler),
|
|
||||||
format_str_(format_str),
|
|
||||||
parse_context_(format_str) {}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
|
|
||||||
if (begin != end)
|
|
||||||
handler_(part::make_text({begin, to_unsigned(end - begin)}));
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR int on_arg_id() {
|
|
||||||
part_ = part::make_arg_index(parse_context_.next_arg_id());
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR int on_arg_id(int id) {
|
|
||||||
parse_context_.check_arg_id(id);
|
|
||||||
part_ = part::make_arg_index(id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) {
|
|
||||||
part_ = part::make_arg_name(id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) {
|
|
||||||
part_.arg_id_end = ptr;
|
|
||||||
handler_(part_);
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
|
|
||||||
const Char* end) {
|
|
||||||
auto repl = typename part::replacement();
|
|
||||||
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
|
|
||||||
repl.specs, parse_context_);
|
|
||||||
auto it = parse_format_specs(begin, end, handler);
|
|
||||||
if (*it != '}') on_error("missing '}' in format string");
|
|
||||||
repl.arg_id = part_.part_kind == part::kind::arg_index
|
|
||||||
? arg_ref<Char>(part_.val.arg_index)
|
|
||||||
: arg_ref<Char>(part_.val.str);
|
|
||||||
auto replacement_part = part::make_replacement(repl);
|
|
||||||
replacement_part.arg_id_end = begin;
|
|
||||||
handler_(replacement_part);
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Compiles a format string and invokes handler(part) for each parsed part.
|
|
||||||
template <bool IS_CONSTEXPR, typename Char, typename PartHandler>
|
|
||||||
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
|
|
||||||
PartHandler handler) {
|
|
||||||
parse_format_string<IS_CONSTEXPR>(
|
|
||||||
format_str,
|
|
||||||
format_string_compiler<Char, PartHandler>(format_str, handler));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt, typename Context, typename Id>
|
|
||||||
void format_arg(
|
|
||||||
basic_format_parse_context<typename Context::char_type>& parse_ctx,
|
|
||||||
Context& ctx, Id arg_id) {
|
|
||||||
auto arg = ctx.arg(arg_id);
|
|
||||||
if (arg.type() == type::custom_type) {
|
|
||||||
visit_format_arg(custom_formatter<Context>(parse_ctx, ctx), arg);
|
|
||||||
} else {
|
|
||||||
ctx.advance_to(visit_format_arg(
|
|
||||||
default_arg_formatter<OutputIt, typename Context::char_type>{
|
|
||||||
ctx.out(), ctx.args(), ctx.locale()},
|
|
||||||
arg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// vformat_to is defined in a subnamespace to prevent ADL.
|
|
||||||
namespace cf {
|
|
||||||
template <typename Context, typename OutputIt, typename CompiledFormat>
|
|
||||||
auto vformat_to(OutputIt out, CompiledFormat& cf,
|
|
||||||
basic_format_args<Context> args) -> typename Context::iterator {
|
|
||||||
using char_type = typename Context::char_type;
|
|
||||||
basic_format_parse_context<char_type> parse_ctx(
|
|
||||||
to_string_view(cf.format_str_));
|
|
||||||
Context ctx(out, args);
|
|
||||||
|
|
||||||
const auto& parts = cf.parts();
|
|
||||||
for (auto part_it = std::begin(parts); part_it != std::end(parts);
|
|
||||||
++part_it) {
|
|
||||||
const auto& part = *part_it;
|
|
||||||
const auto& value = part.val;
|
|
||||||
|
|
||||||
using format_part_t = format_part<char_type>;
|
|
||||||
switch (part.part_kind) {
|
|
||||||
case format_part_t::kind::text: {
|
|
||||||
const auto text = value.str;
|
|
||||||
auto output = ctx.out();
|
|
||||||
auto&& it = reserve(output, text.size());
|
|
||||||
it = std::copy_n(text.begin(), text.size(), it);
|
|
||||||
ctx.advance_to(output);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case format_part_t::kind::arg_index:
|
|
||||||
advance_to(parse_ctx, part.arg_id_end);
|
|
||||||
detail::format_arg<OutputIt>(parse_ctx, ctx, value.arg_index);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case format_part_t::kind::arg_name:
|
|
||||||
advance_to(parse_ctx, part.arg_id_end);
|
|
||||||
detail::format_arg<OutputIt>(parse_ctx, ctx, value.str);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case format_part_t::kind::replacement: {
|
|
||||||
const auto& arg_id_value = value.repl.arg_id.val;
|
|
||||||
const auto arg = value.repl.arg_id.kind == arg_id_kind::index
|
|
||||||
? ctx.arg(arg_id_value.index)
|
|
||||||
: ctx.arg(arg_id_value.name);
|
|
||||||
|
|
||||||
auto specs = value.repl.specs;
|
|
||||||
|
|
||||||
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
|
|
||||||
handle_dynamic_spec<precision_checker>(specs.precision,
|
|
||||||
specs.precision_ref, ctx);
|
|
||||||
|
|
||||||
error_handler h;
|
|
||||||
numeric_specs_checker<error_handler> checker(h, arg.type());
|
|
||||||
if (specs.align == align::numeric) checker.require_numeric_argument();
|
|
||||||
if (specs.sign != sign::none) checker.check_sign();
|
|
||||||
if (specs.alt) checker.require_numeric_argument();
|
|
||||||
if (specs.precision >= 0) checker.check_precision();
|
|
||||||
|
|
||||||
advance_to(parse_ctx, part.arg_id_end);
|
|
||||||
ctx.advance_to(visit_format_arg(
|
|
||||||
arg_formatter<OutputIt, typename Context::char_type>(ctx, specs),
|
|
||||||
arg));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ctx.out();
|
|
||||||
}
|
|
||||||
} // namespace cf
|
|
||||||
|
|
||||||
struct basic_compiled_format {};
|
|
||||||
|
|
||||||
template <typename S, typename = void>
|
|
||||||
struct compiled_format_base : basic_compiled_format {
|
|
||||||
using char_type = char_t<S>;
|
|
||||||
using parts_container = std::vector<detail::format_part<char_type>>;
|
|
||||||
|
|
||||||
parts_container compiled_parts;
|
|
||||||
|
|
||||||
explicit compiled_format_base(basic_string_view<char_type> format_str) {
|
|
||||||
compile_format_string<false>(format_str,
|
|
||||||
[this](const format_part<char_type>& part) {
|
|
||||||
compiled_parts.push_back(part);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const parts_container& parts() const { return compiled_parts; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, unsigned N> struct format_part_array {
|
|
||||||
format_part<Char> data[N] = {};
|
|
||||||
FMT_CONSTEXPR format_part_array() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, unsigned N>
|
|
||||||
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
|
|
||||||
basic_string_view<Char> format_str) {
|
|
||||||
format_part_array<Char, N> parts;
|
|
||||||
unsigned counter = 0;
|
|
||||||
// This is not a lambda for compatibility with older compilers.
|
|
||||||
struct {
|
|
||||||
format_part<Char>* parts;
|
|
||||||
unsigned* counter;
|
|
||||||
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
|
|
||||||
parts[(*counter)++] = part;
|
|
||||||
}
|
|
||||||
} collector{parts.data, &counter};
|
|
||||||
compile_format_string<true>(format_str, collector);
|
|
||||||
if (counter < N) {
|
|
||||||
parts.data[counter] =
|
|
||||||
format_part<Char>::make_text(basic_string_view<Char>());
|
|
||||||
}
|
|
||||||
return parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
|
|
||||||
return (a < b) ? b : a;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename S>
|
|
||||||
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
|
|
||||||
: basic_compiled_format {
|
|
||||||
using char_type = char_t<S>;
|
|
||||||
|
|
||||||
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
|
|
||||||
|
|
||||||
// Workaround for old compilers. Format string compilation will not be
|
|
||||||
// performed there anyway.
|
|
||||||
#if FMT_USE_CONSTEXPR
|
|
||||||
static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
|
|
||||||
constexpr_max(count_parts(to_string_view(S())), 1u);
|
|
||||||
#else
|
|
||||||
static const unsigned num_format_parts = 1;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using parts_container = format_part<char_type>[num_format_parts];
|
|
||||||
|
|
||||||
const parts_container& parts() const {
|
|
||||||
static FMT_CONSTEXPR_DECL const auto compiled_parts =
|
|
||||||
compile_to_parts<char_type, num_format_parts>(
|
|
||||||
detail::to_string_view(S()));
|
|
||||||
return compiled_parts.data;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename S, typename... Args>
|
|
||||||
class compiled_format : private compiled_format_base<S> {
|
|
||||||
public:
|
|
||||||
using typename compiled_format_base<S>::char_type;
|
|
||||||
|
|
||||||
private:
|
|
||||||
basic_string_view<char_type> format_str_;
|
|
||||||
|
|
||||||
template <typename Context, typename OutputIt, typename CompiledFormat>
|
|
||||||
friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf,
|
|
||||||
basic_format_args<Context> args) ->
|
|
||||||
typename Context::iterator;
|
|
||||||
|
|
||||||
public:
|
|
||||||
compiled_format() = delete;
|
|
||||||
explicit constexpr compiled_format(basic_string_view<char_type> format_str)
|
|
||||||
: compiled_format_base<S>(format_str), format_str_(format_str) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef __cpp_if_constexpr
|
#ifdef __cpp_if_constexpr
|
||||||
template <typename... Args> struct type_list {};
|
template <typename... Args> struct type_list {};
|
||||||
|
|
||||||
@ -826,12 +499,6 @@ constexpr auto compile(S format_str) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
template <typename... Args, typename S,
|
|
||||||
FMT_ENABLE_IF(is_compile_string<S>::value)>
|
|
||||||
constexpr auto compile(S format_str) -> detail::compiled_format<S, Args...> {
|
|
||||||
return detail::compiled_format<S, Args...>(to_string_view(format_str));
|
|
||||||
}
|
|
||||||
#endif // __cpp_if_constexpr
|
#endif // __cpp_if_constexpr
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
@ -855,13 +522,11 @@ constexpr OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
|||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
return cf.format(out, args...);
|
return cf.format(out, args...);
|
||||||
}
|
}
|
||||||
#endif // __cpp_if_constexpr
|
|
||||||
|
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
||||||
Args&&... args) {
|
Args&&... args) {
|
||||||
#ifdef __cpp_if_constexpr
|
|
||||||
if constexpr (std::is_same<typename S::char_type, char>::value) {
|
if constexpr (std::is_same<typename S::char_type, char>::value) {
|
||||||
constexpr basic_string_view<typename S::char_type> str = S();
|
constexpr basic_string_view<typename S::char_type> str = S();
|
||||||
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
|
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
|
||||||
@ -874,9 +539,7 @@ FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
constexpr auto compiled = detail::compile<Args...>(S());
|
constexpr auto compiled = detail::compile<Args...>(S());
|
||||||
#ifdef __cpp_if_constexpr
|
|
||||||
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
||||||
detail::unknown_format>()) {
|
detail::unknown_format>()) {
|
||||||
return format(static_cast<basic_string_view<typename S::char_type>>(S()),
|
return format(static_cast<basic_string_view<typename S::char_type>>(S()),
|
||||||
@ -884,16 +547,12 @@ FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
|||||||
} else {
|
} else {
|
||||||
return format(compiled, std::forward<Args>(args)...);
|
return format(compiled, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
return format(compiled, std::forward<Args>(args)...);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename S, typename... Args,
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
|
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
|
||||||
constexpr auto compiled = detail::compile<Args...>(S());
|
constexpr auto compiled = detail::compile<Args...>(S());
|
||||||
#ifdef __cpp_if_constexpr
|
|
||||||
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
||||||
detail::unknown_format>()) {
|
detail::unknown_format>()) {
|
||||||
return format_to(out,
|
return format_to(out,
|
||||||
@ -902,10 +561,8 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
|
|||||||
} else {
|
} else {
|
||||||
return format_to(out, compiled, std::forward<Args>(args)...);
|
return format_to(out, compiled, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
return format_to(out, compiled, std::forward<Args>(args)...);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
template <typename OutputIt, typename S, typename... Args,
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
|
@ -5,49 +5,38 @@
|
|||||||
//
|
//
|
||||||
// For the license information refer to format.h.
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
#include <string>
|
#include "fmt/compile.h"
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
// Check that fmt/compile.h compiles with windows.h included before it.
|
|
||||||
#ifdef _WIN32
|
|
||||||
# include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "fmt/chrono.h"
|
#include "fmt/chrono.h"
|
||||||
#include "fmt/compile.h"
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "gtest-extra.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
TEST(IteratorTest, TruncatingIterator) {
|
TEST(iterator_test, truncating_iterator) {
|
||||||
char* p = nullptr;
|
char* p = nullptr;
|
||||||
fmt::detail::truncating_iterator<char*> it(p, 3);
|
auto it = fmt::detail::truncating_iterator<char*>(p, 3);
|
||||||
auto prev = it++;
|
auto prev = it++;
|
||||||
EXPECT_EQ(prev.base(), p);
|
EXPECT_EQ(prev.base(), p);
|
||||||
EXPECT_EQ(it.base(), p + 1);
|
EXPECT_EQ(it.base(), p + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(IteratorTest, TruncatingIteratorDefaultConstruct) {
|
TEST(iterator_test, truncating_iterator_default_construct) {
|
||||||
static_assert(std::is_default_constructible<
|
auto it = fmt::detail::truncating_iterator<char*>();
|
||||||
fmt::detail::truncating_iterator<char*>>::value,
|
|
||||||
"");
|
|
||||||
|
|
||||||
fmt::detail::truncating_iterator<char*> it;
|
|
||||||
EXPECT_EQ(nullptr, it.base());
|
EXPECT_EQ(nullptr, it.base());
|
||||||
EXPECT_EQ(std::size_t{0}, it.count());
|
EXPECT_EQ(std::size_t{0}, it.count());
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __cpp_lib_ranges
|
#ifdef __cpp_lib_ranges
|
||||||
TEST(IteratorTest, TruncatingIteratorOutputIterator) {
|
TEST(iterator_test, truncating_iterator_is_output_iterator) {
|
||||||
static_assert(
|
static_assert(
|
||||||
std::output_iterator<fmt::detail::truncating_iterator<char*>, char>);
|
std::output_iterator<fmt::detail::truncating_iterator<char*>, char>);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TEST(IteratorTest, TruncatingBackInserter) {
|
TEST(iterator_test, truncating_back_inserter) {
|
||||||
std::string buffer;
|
auto buffer = std::string();
|
||||||
auto bi = std::back_inserter(buffer);
|
auto bi = std::back_inserter(buffer);
|
||||||
fmt::detail::truncating_iterator<decltype(bi)> it(bi, 2);
|
auto it = fmt::detail::truncating_iterator<decltype(bi)>(bi, 2);
|
||||||
*it++ = '4';
|
*it++ = '4';
|
||||||
*it++ = '2';
|
*it++ = '2';
|
||||||
*it++ = '1';
|
*it++ = '1';
|
||||||
@ -55,37 +44,6 @@ TEST(IteratorTest, TruncatingBackInserter) {
|
|||||||
EXPECT_EQ(buffer, "42");
|
EXPECT_EQ(buffer, "42");
|
||||||
}
|
}
|
||||||
|
|
||||||
// compiletime_prepared_parts_type_provider is useful only with relaxed
|
|
||||||
// constexpr.
|
|
||||||
#if FMT_USE_CONSTEXPR
|
|
||||||
template <unsigned EXPECTED_PARTS_COUNT, typename Format>
|
|
||||||
void check_prepared_parts_type(Format format) {
|
|
||||||
typedef fmt::detail::compiled_format_base<decltype(format)> provider;
|
|
||||||
typedef fmt::detail::format_part<char>
|
|
||||||
expected_parts_type[EXPECTED_PARTS_COUNT];
|
|
||||||
static_assert(std::is_same<typename provider::parts_container,
|
|
||||||
expected_parts_type>::value,
|
|
||||||
"CompileTimePreparedPartsTypeProvider test failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CompileTest, CompileTimePreparedPartsTypeProvider) {
|
|
||||||
check_prepared_parts_type<1u>(FMT_STRING("text"));
|
|
||||||
check_prepared_parts_type<1u>(FMT_STRING("{}"));
|
|
||||||
check_prepared_parts_type<2u>(FMT_STRING("text{}"));
|
|
||||||
check_prepared_parts_type<2u>(FMT_STRING("{}text"));
|
|
||||||
check_prepared_parts_type<3u>(FMT_STRING("text{}text"));
|
|
||||||
check_prepared_parts_type<3u>(FMT_STRING("{:{}.{}} {:{}}"));
|
|
||||||
|
|
||||||
check_prepared_parts_type<3u>(FMT_STRING("{{{}}}")); // '{', 'argument', '}'
|
|
||||||
check_prepared_parts_type<2u>(FMT_STRING("text{{")); // 'text', '{'
|
|
||||||
check_prepared_parts_type<3u>(FMT_STRING("text{{ ")); // 'text', '{', ' '
|
|
||||||
check_prepared_parts_type<2u>(FMT_STRING("}}text")); // '}', text
|
|
||||||
check_prepared_parts_type<2u>(FMT_STRING("text}}text")); // 'text}', 'text'
|
|
||||||
check_prepared_parts_type<4u>(
|
|
||||||
FMT_STRING("text{{}}text")); // 'text', '{', '}', 'text'
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct test_formattable {};
|
struct test_formattable {};
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
@ -97,14 +55,14 @@ template <> struct formatter<test_formattable> : formatter<const char*> {
|
|||||||
};
|
};
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
TEST(CompileTest, CompileFallback) {
|
TEST(compile_test, compile_fallback) {
|
||||||
// FMT_COMPILE should fallback on runtime formatting when `if constexpr` is
|
// FMT_COMPILE should fallback on runtime formatting when `if constexpr` is
|
||||||
// not available.
|
// not available.
|
||||||
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42));
|
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __cpp_if_constexpr
|
#ifdef __cpp_if_constexpr
|
||||||
TEST(CompileTest, FormatDefault) {
|
TEST(compile_test, format_default) {
|
||||||
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42));
|
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42));
|
||||||
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42u));
|
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42u));
|
||||||
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42ll));
|
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42ll));
|
||||||
@ -120,18 +78,18 @@ TEST(CompileTest, FormatDefault) {
|
|||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTest, FormatWideString) {
|
TEST(compile_test, format_wide_string) {
|
||||||
EXPECT_EQ(L"42", fmt::format(FMT_COMPILE(L"{}"), 42));
|
EXPECT_EQ(L"42", fmt::format(FMT_COMPILE(L"{}"), 42));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTest, FormatSpecs) {
|
TEST(compile_test, format_specs) {
|
||||||
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{:x}"), 0x42));
|
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{:x}"), 0x42));
|
||||||
EXPECT_EQ("1.2 ms ",
|
EXPECT_EQ("1.2 ms ",
|
||||||
fmt::format(FMT_COMPILE("{:7.1%Q %q}"),
|
fmt::format(FMT_COMPILE("{:7.1%Q %q}"),
|
||||||
std::chrono::duration<double, std::milli>(1.234)));
|
std::chrono::duration<double, std::milli>(1.234)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTest, DynamicFormatSpecs) {
|
TEST(compile_test, dynamic_format_specs) {
|
||||||
EXPECT_EQ("foo ", fmt::format(FMT_COMPILE("{:{}}"), "foo", 5));
|
EXPECT_EQ("foo ", fmt::format(FMT_COMPILE("{:{}}"), "foo", 5));
|
||||||
EXPECT_EQ(" 3.14", fmt::format(FMT_COMPILE("{:{}.{}f}"), 3.141592, 6, 2));
|
EXPECT_EQ(" 3.14", fmt::format(FMT_COMPILE("{:{}.{}f}"), 3.141592, 6, 2));
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
@ -140,7 +98,7 @@ TEST(CompileTest, DynamicFormatSpecs) {
|
|||||||
std::chrono::duration<double, std::milli>(1.234), 9, 3));
|
std::chrono::duration<double, std::milli>(1.234), 9, 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTest, ManualOrdering) {
|
TEST(compile_test, manual_ordering) {
|
||||||
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{0}"), 42));
|
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{0}"), 42));
|
||||||
EXPECT_EQ(" -42", fmt::format(FMT_COMPILE("{0:4}"), -42));
|
EXPECT_EQ(" -42", fmt::format(FMT_COMPILE("{0:4}"), -42));
|
||||||
EXPECT_EQ("41 43", fmt::format(FMT_COMPILE("{0} {1}"), 41, 43));
|
EXPECT_EQ("41 43", fmt::format(FMT_COMPILE("{0} {1}"), 41, 43));
|
||||||
@ -157,7 +115,7 @@ TEST(CompileTest, ManualOrdering) {
|
|||||||
EXPECT_EQ(L"42", fmt::format(FMT_COMPILE(L"{0}"), 42));
|
EXPECT_EQ(L"42", fmt::format(FMT_COMPILE(L"{0}"), 42));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTest, Named) {
|
TEST(compile_test, named) {
|
||||||
auto runtime_named_field_compiled =
|
auto runtime_named_field_compiled =
|
||||||
fmt::detail::compile<decltype(fmt::arg("arg", 42))>(FMT_COMPILE("{arg}"));
|
fmt::detail::compile<decltype(fmt::arg("arg", 42))>(FMT_COMPILE("{arg}"));
|
||||||
static_assert(std::is_same_v<decltype(runtime_named_field_compiled),
|
static_assert(std::is_same_v<decltype(runtime_named_field_compiled),
|
||||||
@ -205,7 +163,7 @@ TEST(CompileTest, Named) {
|
|||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTest, FormatTo) {
|
TEST(compile_test, format_to) {
|
||||||
char buf[8];
|
char buf[8];
|
||||||
auto end = fmt::format_to(buf, FMT_COMPILE("{}"), 42);
|
auto end = fmt::format_to(buf, FMT_COMPILE("{}"), 42);
|
||||||
*end = '\0';
|
*end = '\0';
|
||||||
@ -215,7 +173,7 @@ TEST(CompileTest, FormatTo) {
|
|||||||
EXPECT_STREQ("2a", buf);
|
EXPECT_STREQ("2a", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTest, FormatToNWithCompileMacro) {
|
TEST(compile_test, format_to_n) {
|
||||||
constexpr auto buffer_size = 8;
|
constexpr auto buffer_size = 8;
|
||||||
char buffer[buffer_size];
|
char buffer[buffer_size];
|
||||||
auto res = fmt::format_to_n(buffer, buffer_size, FMT_COMPILE("{}"), 42);
|
auto res = fmt::format_to_n(buffer, buffer_size, FMT_COMPILE("{}"), 42);
|
||||||
@ -226,17 +184,17 @@ TEST(CompileTest, FormatToNWithCompileMacro) {
|
|||||||
EXPECT_STREQ("2a", buffer);
|
EXPECT_STREQ("2a", buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTest, FormattedSizeWithCompileMacro) {
|
TEST(compile_test, formatted_size) {
|
||||||
EXPECT_EQ(2, fmt::formatted_size(FMT_COMPILE("{0}"), 42));
|
EXPECT_EQ(2, fmt::formatted_size(FMT_COMPILE("{0}"), 42));
|
||||||
EXPECT_EQ(5, fmt::formatted_size(FMT_COMPILE("{0:<4.2f}"), 42.0));
|
EXPECT_EQ(5, fmt::formatted_size(FMT_COMPILE("{0:<4.2f}"), 42.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTest, TextAndArg) {
|
TEST(compile_test, text_and_arg) {
|
||||||
EXPECT_EQ(">>>42<<<", fmt::format(FMT_COMPILE(">>>{}<<<"), 42));
|
EXPECT_EQ(">>>42<<<", fmt::format(FMT_COMPILE(">>>{}<<<"), 42));
|
||||||
EXPECT_EQ("42!", fmt::format(FMT_COMPILE("{}!"), 42));
|
EXPECT_EQ("42!", fmt::format(FMT_COMPILE("{}!"), 42));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTest, UnknownFormatFallback) {
|
TEST(compile_test, unknown_format_fallback) {
|
||||||
EXPECT_EQ(" 42 ",
|
EXPECT_EQ(" 42 ",
|
||||||
fmt::format(FMT_COMPILE("{name:^4}"), fmt::arg("name", 42)));
|
fmt::format(FMT_COMPILE("{name:^4}"), fmt::arg("name", 42)));
|
||||||
|
|
||||||
@ -253,7 +211,7 @@ TEST(CompileTest, UnknownFormatFallback) {
|
|||||||
EXPECT_EQ(" 42 ", fmt::string_view(buffer, 4));
|
EXPECT_EQ(" 42 ", fmt::string_view(buffer, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTest, Empty) { EXPECT_EQ("", fmt::format(FMT_COMPILE(""))); }
|
TEST(compile_test, empty) { EXPECT_EQ("", fmt::format(FMT_COMPILE(""))); }
|
||||||
|
|
||||||
struct to_stringable {
|
struct to_stringable {
|
||||||
friend fmt::string_view to_string_view(to_stringable) { return {}; }
|
friend fmt::string_view to_string_view(to_stringable) { return {}; }
|
||||||
@ -272,13 +230,13 @@ template <> struct formatter<to_stringable> {
|
|||||||
};
|
};
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
TEST(CompileTest, ToStringAndFormatter) {
|
TEST(compile_test, to_string_and_formatter) {
|
||||||
fmt::format(FMT_COMPILE("{}"), to_stringable());
|
fmt::format(FMT_COMPILE("{}"), to_stringable());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||||
TEST(CompileTest, CompileFormatStringLiteral) {
|
TEST(compile_test, compile_format_string_literal) {
|
||||||
using namespace fmt::literals;
|
using namespace fmt::literals;
|
||||||
EXPECT_EQ("", fmt::format(""_cf));
|
EXPECT_EQ("", fmt::format(""_cf));
|
||||||
EXPECT_EQ("42", fmt::format("{}"_cf, 42));
|
EXPECT_EQ("42", fmt::format("{}"_cf, 42));
|
||||||
@ -302,14 +260,14 @@ consteval auto test_format(auto format, const Args&... args) {
|
|||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTimeFormattingTest, Bool) {
|
TEST(compile_time_formatting_test, bool) {
|
||||||
EXPECT_EQ("true", test_format<5>(FMT_COMPILE("{}"), true));
|
EXPECT_EQ("true", test_format<5>(FMT_COMPILE("{}"), true));
|
||||||
EXPECT_EQ("false", test_format<6>(FMT_COMPILE("{}"), false));
|
EXPECT_EQ("false", test_format<6>(FMT_COMPILE("{}"), false));
|
||||||
EXPECT_EQ("true ", test_format<6>(FMT_COMPILE("{:5}"), true));
|
EXPECT_EQ("true ", test_format<6>(FMT_COMPILE("{:5}"), true));
|
||||||
EXPECT_EQ("1", test_format<2>(FMT_COMPILE("{:d}"), true));
|
EXPECT_EQ("1", test_format<2>(FMT_COMPILE("{:d}"), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTimeFormattingTest, Integer) {
|
TEST(compile_time_formatting_test, integer) {
|
||||||
EXPECT_EQ("42", test_format<3>(FMT_COMPILE("{}"), 42));
|
EXPECT_EQ("42", test_format<3>(FMT_COMPILE("{}"), 42));
|
||||||
EXPECT_EQ("420", test_format<4>(FMT_COMPILE("{}"), 420));
|
EXPECT_EQ("420", test_format<4>(FMT_COMPILE("{}"), 420));
|
||||||
EXPECT_EQ("42 42", test_format<6>(FMT_COMPILE("{} {}"), 42, 42));
|
EXPECT_EQ("42 42", test_format<6>(FMT_COMPILE("{} {}"), 42, 42));
|
||||||
@ -339,14 +297,14 @@ TEST(CompileTimeFormattingTest, Integer) {
|
|||||||
EXPECT_EQ("**-42", test_format<6>(FMT_COMPILE("{:*>5}"), -42));
|
EXPECT_EQ("**-42", test_format<6>(FMT_COMPILE("{:*>5}"), -42));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTimeFormattingTest, Char) {
|
TEST(compile_time_formatting_test, char) {
|
||||||
EXPECT_EQ("c", test_format<2>(FMT_COMPILE("{}"), 'c'));
|
EXPECT_EQ("c", test_format<2>(FMT_COMPILE("{}"), 'c'));
|
||||||
|
|
||||||
EXPECT_EQ("c ", test_format<4>(FMT_COMPILE("{:3}"), 'c'));
|
EXPECT_EQ("c ", test_format<4>(FMT_COMPILE("{:3}"), 'c'));
|
||||||
EXPECT_EQ("99", test_format<3>(FMT_COMPILE("{:d}"), 'c'));
|
EXPECT_EQ("99", test_format<3>(FMT_COMPILE("{:d}"), 'c'));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTimeFormattingTest, String) {
|
TEST(compile_time_formatting_test, string) {
|
||||||
EXPECT_EQ("42", test_format<3>(FMT_COMPILE("{}"), "42"));
|
EXPECT_EQ("42", test_format<3>(FMT_COMPILE("{}"), "42"));
|
||||||
EXPECT_EQ("The answer is 42",
|
EXPECT_EQ("The answer is 42",
|
||||||
test_format<17>(FMT_COMPILE("{} is {}"), "The answer", "42"));
|
test_format<17>(FMT_COMPILE("{} is {}"), "The answer", "42"));
|
||||||
@ -355,14 +313,14 @@ TEST(CompileTimeFormattingTest, String) {
|
|||||||
EXPECT_EQ("**🤡**", test_format<9>(FMT_COMPILE("{:*^6}"), "🤡"));
|
EXPECT_EQ("**🤡**", test_format<9>(FMT_COMPILE("{:*^6}"), "🤡"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTimeFormattingTest, Combination) {
|
TEST(compile_time_formatting_test, combination) {
|
||||||
EXPECT_EQ("420, true, answer",
|
EXPECT_EQ("420, true, answer",
|
||||||
test_format<18>(FMT_COMPILE("{}, {}, {}"), 420, true, "answer"));
|
test_format<18>(FMT_COMPILE("{}, {}, {}"), 420, true, "answer"));
|
||||||
|
|
||||||
EXPECT_EQ(" -42", test_format<5>(FMT_COMPILE("{:{}}"), -42, 4));
|
EXPECT_EQ(" -42", test_format<5>(FMT_COMPILE("{:{}}"), -42, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CompileTimeFormattingTest, MultiByteFill) {
|
TEST(compile_time_formatting_test, multibyte_fill) {
|
||||||
EXPECT_EQ("жж42", test_format<8>(FMT_COMPILE("{:ж>4}"), 42));
|
EXPECT_EQ("жж42", test_format<8>(FMT_COMPILE("{:ж>4}"), 42));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user