Implemented fmt::prepare()

Implementation of fmt::prepare() function and features around it.
This commit is contained in:
stryku 2018-11-27 11:52:00 +01:00
parent f5cc77cea0
commit 8c99ec0738
11 changed files with 3301 additions and 1161 deletions

View File

@ -140,7 +140,7 @@ endfunction()
# Define the fmt library, its includes and the needed defines.
add_headers(FMT_HEADERS chrono.h color.h core.h format.h format-inl.h locale.h
ostream.h printf.h time.h ranges.h)
ostream.h prepare.h printf.h time.h ranges.h)
set(FMT_SOURCES src/format.cc)
if (HAVE_OPEN)
add_headers(FMT_HEADERS posix.h)

View File

@ -372,15 +372,15 @@ template <typename Rep, typename Period, typename Char>
struct formatter<std::chrono::duration<Rep, Period>, Char> {
private:
align_spec spec;
internal::arg_ref<Char> width_ref;
typedef internal::arg_ref<Char> arg_ref_type;
arg_ref_type width_ref;
mutable basic_string_view<Char> format_str;
typedef std::chrono::duration<Rep, Period> duration;
struct spec_handler {
formatter &f;
basic_parse_context<Char> &context;
typedef internal::arg_ref<Char> arg_ref_type;
basic_string_view<Char> format_str;
template <typename Id>
FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
@ -388,6 +388,12 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
return arg_ref_type(arg_id);
}
FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<Char> arg_id) {
context.check_arg_id(arg_id);
const auto str_val = internal::string_view_metadata(format_str, arg_id);
return arg_ref_type(str_val);
}
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
return arg_ref_type(context.next_arg_id());
}
@ -410,7 +416,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
-> decltype(ctx.begin()) {
auto begin = ctx.begin(), end = ctx.end();
if (begin == end) return begin;
spec_handler handler{*this, ctx};
spec_handler handler{*this, ctx, format_str};
begin = internal::parse_align(begin, end, handler);
if (begin == end) return begin;
begin = internal::parse_width(begin, end, handler);

View File

@ -221,14 +221,13 @@ enum class emphasis : uint8_t {
// We use rgb as name because some editors will show it as color direct in the
// editor.
struct rgb {
FMT_CONSTEXPR_DECL rgb() : r(0), g(0), b(0) {}
FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_)
: r(r_), g(g_), b(b_) {}
FMT_CONSTEXPR_DECL rgb(uint32_t hex)
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b((hex) & 0xFF) {}
FMT_CONSTEXPR_DECL rgb(color hex)
: r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF),
b(uint32_t(hex) & 0xFF) {}
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
FMT_CONSTEXPR rgb(uint32_t hex)
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b((hex)&0xFF) {}
FMT_CONSTEXPR rgb(color hex)
: r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF),
b(uint32_t(hex) & 0xFF) {}
uint8_t r;
uint8_t g;
uint8_t b;

View File

@ -454,9 +454,11 @@ template <typename Char>
inline basic_string_view<Char>
to_string_view(basic_string_view<Char> s) { return s; }
template <typename Char>
template <typename Char, typename Traits, typename Allocator>
inline basic_string_view<Char>
to_string_view(const std::basic_string<Char> &s) { return s; }
to_string_view(const std::basic_string<Char, Traits, Allocator> &s) {
return {s.data(), s.size()};
}
template <typename Char>
inline basic_string_view<Char> to_string_view(const Char *s) { return s; }
@ -504,6 +506,8 @@ template <typename T, typename Char, typename Enable = void>
struct convert_to_int: std::integral_constant<
bool, !std::is_arithmetic<T>::value && std::is_convertible<T, int>::value> {};
template <typename Char> struct basic_format_specs;
namespace internal {
struct dummy_string_view { typedef void char_type; };
@ -877,16 +881,18 @@ FMT_CONSTEXPR typename internal::result_of<Visitor(int)>::type
template <typename Char, typename ErrorHandler = internal::error_handler>
class basic_parse_context : private ErrorHandler {
private:
basic_string_view<Char> format_str_;
int next_arg_id_;
basic_string_view<Char> primary_format_str_;
basic_string_view<Char> format_str_;
int next_arg_id_;
public:
typedef Char char_type;
typedef typename basic_string_view<Char>::iterator iterator;
explicit FMT_CONSTEXPR basic_parse_context(
basic_string_view<Char> format_str, ErrorHandler eh = ErrorHandler())
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {}
explicit FMT_CONSTEXPR basic_parse_context(basic_string_view<Char> format_str,
ErrorHandler eh = ErrorHandler())
: ErrorHandler(eh), primary_format_str_(format_str),
format_str_(format_str), next_arg_id_(0) {}
// Returns an iterator to the beginning of the format string range being
// parsed.
@ -913,13 +919,17 @@ class basic_parse_context : private ErrorHandler {
next_arg_id_ = -1;
return true;
}
void check_arg_id(basic_string_view<Char>) {}
FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
FMT_CONSTEXPR void on_error(const char *message) {
ErrorHandler::on_error(message);
}
FMT_CONSTEXPR ErrorHandler error_handler() const { return *this; }
FMT_CONSTEXPR basic_string_view<Char> primary_format() const {
return primary_format_str_;
}
};
typedef basic_parse_context<char> format_parse_context;
@ -1038,6 +1048,10 @@ class context_base {
void advance_to(iterator it) { out_ = it; }
locale_ref locale() { return loc_; }
basic_string_view<Char> primary_format() const {
return parse_context_.primary_format();
}
};
template <typename Context, typename T>

View File

@ -81,6 +81,16 @@
# define FMT_SECURE_SCL 0
#endif
// Check whether we can use unrestricted unions (e.g. unions with non-PODs
// members). If no, use struct.
#ifndef FMT_UNRESTRICTED_UNION
#if (FMT_MSC_VER >= 1900 || FMT_GCC_VERSION >= 406 || FMT_CLANG_VERSION >= 303)
#define FMT_UNRESTRICTED_UNION union
#else
#define FMT_UNRESTRICTED_UNION struct
#endif
#endif
#if FMT_SECURE_SCL
# include <iterator>
#endif
@ -1104,6 +1114,7 @@ struct core_format_specs {
FMT_CONSTEXPR core_format_specs() : precision(-1), flags(0), type(0) {}
FMT_CONSTEXPR bool has(unsigned f) const { return (flags & f) != 0; }
FMT_CONSTEXPR bool has_precision() const { return precision != -1; }
};
// Format specifiers.
@ -1585,68 +1596,79 @@ class specs_setter {
basic_format_specs<Char> &specs_;
};
template <typename ErrorHandler> class numeric_specs_checker {
public:
FMT_CONSTEXPR numeric_specs_checker(ErrorHandler &eh, internal::type arg_type)
: error_handler_(eh), arg_type_(arg_type) {}
FMT_CONSTEXPR void require_numeric_argument() {
if (!is_arithmetic(arg_type_)) {
error_handler_.on_error("format specifier requires numeric argument");
}
}
FMT_CONSTEXPR void check_sign() {
require_numeric_argument();
if (is_integral(arg_type_) && arg_type_ != int_type &&
arg_type_ != long_long_type && arg_type_ != internal::char_type) {
error_handler_.on_error("format specifier requires signed argument");
}
}
FMT_CONSTEXPR void check_precision() {
if (is_integral(arg_type_) || arg_type_ == internal::pointer_type) {
error_handler_.on_error("precision not allowed for this argument type");
}
}
private:
ErrorHandler &error_handler_;
internal::type arg_type_;
};
// A format specifier handler that checks if specifiers are consistent with the
// argument type.
template <typename Handler>
class specs_checker : public Handler {
public:
FMT_CONSTEXPR specs_checker(const Handler& handler, internal::type arg_type)
: Handler(handler), arg_type_(arg_type) {}
FMT_CONSTEXPR specs_checker(const Handler &handler, internal::type arg_type)
: Handler(handler), checker_(*this, arg_type) {}
FMT_CONSTEXPR specs_checker(const specs_checker &other)
: Handler(other), arg_type_(other.arg_type_) {}
FMT_CONSTEXPR specs_checker(const specs_checker &other)
: Handler(other), checker_(*this, other.arg_type_) {}
FMT_CONSTEXPR void on_align(alignment align) {
if (align == ALIGN_NUMERIC)
require_numeric_argument();
Handler::on_align(align);
FMT_CONSTEXPR void on_align(alignment align) {
if (align == ALIGN_NUMERIC)
checker_.require_numeric_argument();
Handler::on_align(align);
}
FMT_CONSTEXPR void on_plus() {
check_sign();
checker_.check_sign();
Handler::on_plus();
}
FMT_CONSTEXPR void on_minus() {
check_sign();
checker_.check_sign();
Handler::on_minus();
}
FMT_CONSTEXPR void on_space() {
check_sign();
checker_.check_sign();
Handler::on_space();
}
FMT_CONSTEXPR void on_hash() {
require_numeric_argument();
checker_.require_numeric_argument();
Handler::on_hash();
}
FMT_CONSTEXPR void on_zero() {
require_numeric_argument();
checker_.require_numeric_argument();
Handler::on_zero();
}
FMT_CONSTEXPR void end_precision() {
if (is_integral(arg_type_) || arg_type_ == pointer_type)
this->on_error("precision not allowed for this argument type");
}
FMT_CONSTEXPR void end_precision() { checker_.check_precision(); }
private:
FMT_CONSTEXPR void require_numeric_argument() {
if (!is_arithmetic(arg_type_))
this->on_error("format specifier requires numeric argument");
}
FMT_CONSTEXPR void check_sign() {
require_numeric_argument();
if (is_integral(arg_type_) && arg_type_ != int_type &&
arg_type_ != long_long_type && arg_type_ != internal::char_type) {
this->on_error("format specifier requires signed argument");
}
}
internal::type arg_type_;
private:
numeric_specs_checker<Handler> checker_;
};
template <template <typename> class Handler, typename T,
@ -1702,28 +1724,53 @@ class specs_handler: public specs_setter<typename Context::char_type> {
Context &context_;
};
struct string_view_metadata {
FMT_CONSTEXPR string_view_metadata() : offset_(0u), size_(0u) {}
template <typename Char>
FMT_CONSTEXPR string_view_metadata(basic_string_view<Char> primary_string,
basic_string_view<Char> view)
: offset_(view.data() - primary_string.data()), size_(view.size()) {}
FMT_CONSTEXPR string_view_metadata(std::size_t offset, std::size_t size)
: offset_(offset), size_(size) {}
template <typename S>
FMT_CONSTEXPR typename std::enable_if<internal::is_string<S>::value,
basic_string_view<FMT_CHAR(S)>>::type
to_view(S &&str) const {
const auto view = to_string_view(str);
return basic_string_view<FMT_CHAR(S)>(view.data() + offset_, size_);
}
std::size_t offset_;
std::size_t size_;
};
// An argument reference.
template <typename Char>
struct arg_ref {
enum Kind { NONE, INDEX, NAME };
typedef Char char_type;
FMT_CONSTEXPR arg_ref() : kind(NONE), index(0) {}
FMT_CONSTEXPR explicit arg_ref(unsigned index) : kind(INDEX), index(index) {}
explicit arg_ref(basic_string_view<Char> nm) : kind(NAME) {
name = {nm.data(), nm.size()};
}
FMT_CONSTEXPR arg_ref() : kind(NONE), val() {}
FMT_CONSTEXPR explicit arg_ref(unsigned index) : kind(INDEX), val(index) {}
FMT_CONSTEXPR explicit arg_ref(string_view_metadata name)
: kind(NAME), val(name) {}
FMT_CONSTEXPR arg_ref &operator=(unsigned idx) {
kind = INDEX;
index = idx;
val.index = idx;
return *this;
}
Kind kind;
union {
FMT_UNRESTRICTED_UNION value {
FMT_CONSTEXPR value() : index(0u) {}
FMT_CONSTEXPR value(unsigned id) : index(id) {}
FMT_CONSTEXPR value(string_view_metadata n) : name(n) {}
unsigned index;
string_value<Char> name; // This is not string_view because of gcc 4.4.
};
string_view_metadata name;
}
val;
};
// Format specifiers with width and precision resolved at formatting rather
@ -1737,19 +1784,19 @@ struct dynamic_format_specs : basic_format_specs<Char> {
// Format spec handler that saves references to arguments representing dynamic
// width and precision to be resolved at formatting time.
template <typename ParseContext>
class dynamic_specs_handler :
public specs_setter<typename ParseContext::char_type> {
public:
template <typename Specs, typename ParseContext>
class dynamic_specs_handler
: public specs_setter<typename ParseContext::char_type> {
public:
typedef typename ParseContext::char_type char_type;
FMT_CONSTEXPR dynamic_specs_handler(
dynamic_format_specs<char_type> &specs, ParseContext &ctx)
: specs_setter<char_type>(specs), specs_(specs), context_(ctx) {}
private:
typedef arg_ref<typename ParseContext::char_type> arg_ref_type;
FMT_CONSTEXPR dynamic_specs_handler(const dynamic_specs_handler &other)
: specs_setter<char_type>(other),
specs_(other.specs_), context_(other.context_) {}
public:
FMT_CONSTEXPR dynamic_specs_handler(Specs &specs, ParseContext &ctx)
: specs_setter<char_type>(specs), specs_(specs), context_(ctx) {}
template <typename Id>
FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
@ -1766,19 +1813,24 @@ class dynamic_specs_handler :
}
private:
typedef arg_ref<char_type> arg_ref_type;
template <typename Id>
FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
context_.check_arg_id(arg_id);
return arg_ref_type(arg_id);
FMT_CONSTEXPR arg_ref_type make_arg_ref(unsigned arg_id) {
context_.check_arg_id(arg_id);
return arg_ref_type(arg_id);
}
FMT_CONSTEXPR arg_ref_type make_arg_ref(auto_id) {
return arg_ref_type(context_.next_arg_id());
}
dynamic_format_specs<char_type> &specs_;
FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<char_type> arg_id) {
context_.check_arg_id(arg_id);
const auto id_metadata =
string_view_metadata(context_.primary_format(), arg_id);
return arg_ref_type(id_metadata);
}
private:
Specs &specs_;
ParseContext &context_;
};
@ -2131,21 +2183,22 @@ struct format_type :
std::integral_constant<bool, get_type<Context, T>::value != custom_type> {};
template <template <typename> class Handler, typename Spec, typename Context>
void handle_dynamic_spec(
Spec &value, arg_ref<typename Context::char_type> ref, Context &ctx) {
void handle_dynamic_spec(Spec &value, arg_ref<typename Context::char_type> ref,
Context &ctx) {
typedef typename Context::char_type char_type;
typedef arg_ref<char_type> arg_ref_type;
switch (ref.kind) {
case arg_ref<char_type>::NONE:
case arg_ref_type::NONE:
break;
case arg_ref<char_type>::INDEX:
internal::set_dynamic_spec<Handler>(
value, ctx.get_arg(ref.index), ctx.error_handler());
break;
case arg_ref<char_type>::NAME:
internal::set_dynamic_spec<Handler>(
value, ctx.get_arg({ref.name.value, ref.name.size}),
ctx.error_handler());
case arg_ref_type::INDEX:
internal::set_dynamic_spec<Handler>(value, ctx.get_arg(ref.val.index),
ctx.error_handler());
break;
case arg_ref_type::NAME: {
const auto arg_id = ref.val.name.to_view(ctx.primary_format());
internal::set_dynamic_spec<Handler>(value, ctx.get_arg(arg_id),
ctx.error_handler());
} break;
}
}
} // namespace internal
@ -2925,7 +2978,9 @@ struct formatter<
// terminating '}'.
template <typename ParseContext>
FMT_CONSTEXPR typename ParseContext::iterator parse(ParseContext &ctx) {
typedef internal::dynamic_specs_handler<ParseContext> handler_type;
typedef internal::dynamic_specs_handler<
internal::dynamic_format_specs<Char>, ParseContext>
handler_type;
auto type = internal::get_type<
typename buffer_context<Char>::type, T>::value;
internal::specs_checker<handler_type>
@ -3011,33 +3066,42 @@ class dynamic_formatter {
void on_hash() {}
};
public:
typedef internal::dynamic_format_specs<Char> dynamic_specs;
public:
template <typename ParseContext>
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
typedef internal::dynamic_specs_handler<
internal::dynamic_format_specs<Char>, ParseContext>
handler_type;
handler_type handler(specs_, ctx);
// Checks are deferred to formatting time when the argument type is known.
internal::dynamic_specs_handler<ParseContext> handler(specs_, ctx);
return parse_format_specs(ctx.begin(), ctx.end(), handler);
}
template <typename T, typename FormatContext>
auto format(const T &val, FormatContext &ctx) -> decltype(ctx.out()) {
basic_format_specs<Char> specs;
handle_specs(ctx);
internal::specs_checker<null_handler>
checker(null_handler(), internal::get_type<FormatContext, T>::value);
checker.on_align(specs_.align());
if (specs_.flags == 0); // Do nothing.
else if (specs_.has(SIGN_FLAG))
specs_.has(PLUS_FLAG) ? checker.on_plus() : checker.on_space();
else if (specs_.has(MINUS_FLAG))
specs = specs_;
internal::specs_checker<null_handler> checker(
null_handler(), internal::get_type<FormatContext, T>::value);
checker.on_align(specs.align());
if (specs.flags == 0)
; // Do nothing.
else if (specs.has(SIGN_FLAG))
specs.has(PLUS_FLAG) ? checker.on_plus() : checker.on_space();
else if (specs.has(MINUS_FLAG))
checker.on_minus();
else if (specs_.has(HASH_FLAG))
else if (specs.has(HASH_FLAG))
checker.on_hash();
if (specs_.precision != -1)
if (specs.precision != -1)
checker.end_precision();
typedef output_range<typename FormatContext::iterator,
typename FormatContext::char_type> range;
visit_format_arg(arg_formatter<range>(ctx, &specs_),
internal::make_arg<FormatContext>(val));
visit_format_arg(arg_formatter<range>(ctx, &specs),
internal::make_arg<FormatContext>(val));
return ctx.out();
}
@ -3050,7 +3114,7 @@ class dynamic_formatter {
specs_.precision, specs_.precision_ref, ctx);
}
internal::dynamic_format_specs<Char> specs_;
dynamic_specs specs_;
};
template <typename Range, typename Char>
@ -3510,16 +3574,16 @@ operator"" _a(const wchar_t *s, std::size_t) { return {s}; }
#endif // FMT_USE_USER_DEFINED_LITERALS
FMT_END_NAMESPACE
#define FMT_STRING(s) [] { \
typedef typename std::remove_cv<std::remove_pointer< \
typename std::decay<decltype(s)>::type>::type>::type ct; \
struct str : fmt::compile_string { \
typedef ct char_type; \
FMT_CONSTEXPR operator fmt::basic_string_view<ct>() const { \
return {s, sizeof(s) / sizeof(ct) - 1}; \
} \
}; \
return str{}; \
#define FMT_STRING(s) \
[] { \
struct str : fmt::compile_string { \
typedef typename std::remove_cv<std::remove_pointer< \
typename std::decay<decltype(s)>::type>::type>::type char_type; \
FMT_CONSTEXPR operator fmt::basic_string_view<char_type>() const { \
return {s, sizeof(s) / sizeof(char_type) - 1}; \
} \
}; \
return str{}; \
}()
#if defined(FMT_STRING_ALIAS) && FMT_STRING_ALIAS

814
include/fmt/prepare.h Normal file
View File

@ -0,0 +1,814 @@
/*
Formatting library for C++
Copyright (c) 2012 - present, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FMT_PREPARE_H_
#define FMT_PREPARE_H_
#ifndef FMT_HAS_CONSTRUCTIBLE_TRAITS
#define FMT_HAS_CONSTRUCTIBLE_TRAITS \
(FMT_GCC_VERSION >= 407 || FMT_CLANG_VERSION || FMT_MSC_VER)
#endif
#include "format.h"
#include <vector>
FMT_BEGIN_NAMESPACE
template <typename Char> struct format_part {
public:
struct named_argument_id {
FMT_CONSTEXPR named_argument_id(internal::string_view_metadata id)
: id(id) {}
internal::string_view_metadata id;
};
struct argument_id {
FMT_CONSTEXPR argument_id() : argument_id(0u) {}
FMT_CONSTEXPR argument_id(unsigned id)
: which(which_arg_id::index), val(id) {}
FMT_CONSTEXPR argument_id(internal::string_view_metadata id)
: which(which_arg_id::named_index), val(id) {}
enum class which_arg_id { index, named_index };
which_arg_id which;
FMT_UNRESTRICTED_UNION value {
FMT_CONSTEXPR value() : index(0u) {}
FMT_CONSTEXPR value(unsigned id) : index(id) {}
FMT_CONSTEXPR value(internal::string_view_metadata id)
: named_index(id) {}
unsigned index;
internal::string_view_metadata named_index;
}
val;
};
struct specification {
FMT_CONSTEXPR specification() : arg_id(0u) {}
FMT_CONSTEXPR specification(unsigned id) : arg_id(id) {}
FMT_CONSTEXPR specification(internal::string_view_metadata id)
: arg_id(id) {}
argument_id arg_id;
internal::dynamic_format_specs<Char> parsed_specs;
};
FMT_CONSTEXPR format_part()
: which(which_value::argument_id), end_of_argument_id(0u), val(0u) {}
FMT_CONSTEXPR format_part(internal::string_view_metadata text)
: which(which_value::text), end_of_argument_id(0u), val(text) {}
FMT_CONSTEXPR format_part(unsigned id)
: which(which_value::argument_id), end_of_argument_id(0u), val(id) {}
FMT_CONSTEXPR format_part(named_argument_id arg_id)
: which(which_value::named_argument_id), end_of_argument_id(0u),
val(arg_id) {}
FMT_CONSTEXPR format_part(specification spec)
: which(which_value::specification), end_of_argument_id(0u), val(spec) {}
enum class which_value {
argument_id,
named_argument_id,
text,
specification
};
which_value which;
std::size_t end_of_argument_id;
FMT_UNRESTRICTED_UNION value {
FMT_CONSTEXPR value() : arg_id(0u) {}
FMT_CONSTEXPR value(unsigned id) : arg_id(id) {}
FMT_CONSTEXPR value(named_argument_id named_id)
: named_arg_id(named_id.id) {}
FMT_CONSTEXPR value(internal::string_view_metadata t) : text(t) {}
FMT_CONSTEXPR value(specification s) : spec(s) {}
unsigned arg_id;
internal::string_view_metadata named_arg_id;
internal::string_view_metadata text;
specification spec;
}
val;
};
namespace internal {
template <typename Char, typename PartsContainer>
class format_preparation_handler : public internal::error_handler {
private:
typedef format_part<Char> part;
public:
typedef internal::null_terminating_iterator<Char> iterator;
FMT_CONSTEXPR format_preparation_handler(basic_string_view<Char> format,
PartsContainer &parts)
: parts_(parts), format_(format), parse_context_(format) {}
FMT_CONSTEXPR void on_text(const Char *begin, const Char *end) {
if (begin == end) {
return;
}
const auto offset = begin - format_.data();
const auto size = end - begin;
parts_.add(part(string_view_metadata(offset, size)));
}
FMT_CONSTEXPR void on_arg_id() {
parts_.add(part(parse_context_.next_arg_id()));
}
FMT_CONSTEXPR void on_arg_id(unsigned id) {
parse_context_.check_arg_id(id);
parts_.add(part(id));
}
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
const auto view = string_view_metadata(format_, id);
const auto arg_id = typename part::named_argument_id(view);
parts_.add(part(arg_id));
}
FMT_CONSTEXPR void on_replacement_field(const Char *ptr) {
auto last_part = parts_.last();
last_part.end_of_argument_id = ptr - format_.begin();
parts_.substitute_last(last_part);
}
FMT_CONSTEXPR const Char *on_format_specs(const Char *begin,
const Char *end) {
const auto specs_offset = to_unsigned(begin - format_.begin());
typedef internal::dynamic_format_specs<Char> prepared_specs;
typedef basic_parse_context<Char> parse_context;
prepared_specs parsed_specs;
dynamic_specs_handler<prepared_specs, parse_context> handler(
parsed_specs, parse_context_);
begin = parse_format_specs(begin, end, handler);
if (*begin != '}') {
on_error("missing '}' in format string");
}
const auto last_part = parts_.last();
auto specs = last_part.which == part::which_value::argument_id
? typename part::specification(last_part.val.arg_id)
: typename part::specification(last_part.val.named_arg_id);
specs.parsed_specs = parsed_specs;
auto new_part = part(specs);
new_part.end_of_argument_id = specs_offset;
parts_.substitute_last(new_part);
return begin;
}
private:
PartsContainer &parts_;
basic_string_view<Char> format_;
basic_parse_context<Char> parse_context_;
};
template <typename ErrorHandler> class parsed_specs_checker {
public:
FMT_CONSTEXPR parsed_specs_checker(ErrorHandler &error_handler,
internal::type arg_type)
: checker_(error_handler, arg_type) {}
template <typename Char>
FMT_CONSTEXPR void check(const basic_format_specs<Char> &specs) {
if (specs.align_ == ALIGN_NUMERIC) {
checker_.require_numeric_argument();
}
if (specs.has(PLUS_FLAG | MINUS_FLAG | SIGN_FLAG)) {
checker_.check_sign();
}
if (specs.has(HASH_FLAG)) {
checker_.require_numeric_argument();
}
if (specs.has_precision()) {
checker_.check_precision();
}
}
private:
numeric_specs_checker<ErrorHandler> checker_;
};
template <typename Format, typename PreparedPartsProvider, typename... Args>
class prepared_format {
public:
typedef FMT_CHAR(Format) char_type;
typedef format_part<char_type> format_part_t;
typedef internal::checked_args<Format, Args...> checked_args;
prepared_format(Format f)
: format_(std::move(f)), parts_provider_(to_string_view(format_)) {}
prepared_format() = delete;
std::size_t formatted_size(const Args &... args) const {
const auto it = this->format_to(counting_iterator<char_type>(), args...);
return it.count();
}
template <typename OutputIt>
inline typename std::enable_if<internal::is_output_iterator<OutputIt>::value,
format_to_n_result<OutputIt>>::type
format_to_n(OutputIt out, unsigned n, const Args &... args) const {
format_arg_store<typename format_to_n_context<OutputIt, char_type>::type,
Args...>
as(args...);
typedef truncating_iterator<OutputIt> trunc_it;
typedef output_range<trunc_it, char_type> range;
range r(trunc_it(out, n));
auto it = this->vformat_to(
r, typename format_to_n_args<OutputIt, char_type>::type(as));
return {it.base(), it.count()};
}
std::basic_string<char_type> format(const Args &... args) const {
basic_memory_buffer<char_type> buffer;
typedef back_insert_range<internal::basic_buffer<char_type>> range;
this->vformat_to(range(buffer), *checked_args(format_, args...));
return to_string(buffer);
}
template <typename Container>
inline typename std::enable_if<is_contiguous<Container>::value,
std::back_insert_iterator<Container>>::type
format_to(std::back_insert_iterator<Container> out,
const Args &... args) const {
internal::container_buffer<Container> buffer(internal::get_container(out));
typedef back_insert_range<internal::basic_buffer<char_type>> range;
this->vformat_to(range(buffer), *checked_args(format_, args...));
return out;
}
template <typename OutputIt>
inline OutputIt format_to(OutputIt out, const Args &... args) const {
typedef typename format_context_t<OutputIt, char_type>::type context;
typedef output_range<OutputIt, char_type> range;
format_arg_store<context, Args...> as(args...);
return this->vformat_to(range(out), basic_format_args<context>(as));
}
template <std::size_t SIZE = inline_buffer_size>
inline typename buffer_context<char_type>::type::iterator
format_to(basic_memory_buffer<char_type, SIZE> &buf,
const Args &... args) const {
typedef back_insert_range<internal::basic_buffer<char_type>> range;
return this->vformat_to(range(buf), *checked_args(format_, args...));
}
private:
template <typename Range, typename Context>
typename Context::iterator vformat_to(Range out,
basic_format_args<Context> args) const {
const auto format_view = internal::to_string_view(format_);
Context ctx(out.begin(), format_view, args);
const auto &parts = parts_provider_.parts();
for (auto part_it = parts.begin(); part_it != parts.end(); ++part_it) {
const auto &part = *part_it;
const auto &value = part.val;
switch (part.which) {
case format_part_t::which_value::text: {
const auto text = value.text.to_view(format_view);
auto out = ctx.out();
auto &&it = internal::reserve(out, text.size());
it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(out);
} break;
case format_part_t::which_value::argument_id: {
advance_parse_context_to_specification(ctx, part);
format_arg<Range>(ctx, value.arg_id);
} break;
case format_part_t::which_value::named_argument_id: {
advance_parse_context_to_specification(ctx, part);
const auto named_arg_id = value.named_arg_id.to_view(format_view);
format_arg<Range>(ctx, named_arg_id);
} break;
case format_part_t::which_value::specification: {
const auto &arg_id_value = value.spec.arg_id.val;
const auto arg =
value.spec.arg_id.which ==
format_part_t::argument_id::which_arg_id::index
? ctx.get_arg(arg_id_value.index)
: ctx.get_arg(arg_id_value.named_index.to_view(format_));
auto specs = value.spec.parsed_specs;
handle_dynamic_spec<internal::width_checker>(specs.width_,
specs.width_ref, ctx);
handle_dynamic_spec<internal::precision_checker>(
specs.precision, specs.precision_ref, ctx);
internal::error_handler h;
parsed_specs_checker<internal::error_handler> checker(h, arg.type());
checker.check(specs);
advance_parse_context_to_specification(ctx, part);
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, &specs), arg));
} break;
}
}
return ctx.out();
}
template <typename Context>
void advance_parse_context_to_specification(Context &ctx,
const format_part_t &part) const {
const auto view = to_string_view(format_);
const auto specification_begin = view.data() + part.end_of_argument_id;
ctx.parse_context().advance_to(specification_begin);
}
template <typename Range, typename Context, typename Id>
void format_arg(Context &ctx, Id arg_id) const {
ctx.parse_context().check_arg_id(arg_id);
const auto stopped_at =
visit_format_arg(arg_formatter<Range>(ctx), ctx.get_arg(arg_id));
ctx.advance_to(stopped_at);
}
private:
Format format_;
PreparedPartsProvider parts_provider_;
};
template <typename Format> class compiletime_prepared_parts_type_provider {
private:
typedef FMT_CHAR(Format) char_type;
class count_handler {
private:
typedef internal::null_terminating_iterator<char_type> iterator;
public:
FMT_CONSTEXPR count_handler() : counter_(0u) {}
FMT_CONSTEXPR void on_text(const char_type *begin, const char_type *end) {
if (begin != end) {
++counter_;
}
}
FMT_CONSTEXPR void on_arg_id() { ++counter_; }
FMT_CONSTEXPR void on_arg_id(unsigned) { ++counter_; }
FMT_CONSTEXPR void on_arg_id(basic_string_view<char_type>) { ++counter_; }
FMT_CONSTEXPR void on_replacement_field(const char_type *) {}
FMT_CONSTEXPR const char_type *on_format_specs(const char_type *begin,
const char_type *end) {
return find_matching_brace(begin, end);
}
FMT_CONSTEXPR void on_error(const char *) {}
FMT_CONSTEXPR unsigned result() const { return counter_; }
private:
FMT_CONSTEXPR const char_type *find_matching_brace(const char_type *begin,
const char_type *end) {
FMT_CONSTEXPR_DECL const basic_string_view<char_type> text = Format{};
unsigned braces_counter{0u};
for (; begin != end; ++begin) {
if (*begin == '{') {
++braces_counter;
} else if (*begin == '}') {
if (braces_counter == 0u) {
break;
}
--braces_counter;
}
}
return begin;
}
private:
unsigned counter_;
};
static FMT_CONSTEXPR unsigned count_parts() {
FMT_CONSTEXPR_DECL const auto text = to_string_view(Format{});
count_handler handler;
internal::parse_format_string</*IS_CONSTEXPR=*/true>(text, handler);
return handler.result();
}
// Workaround for old compilers. Compiletime parts preparation will not be
// performed with them anyway.
#if FMT_USE_CONSTEXPR
static FMT_CONSTEXPR_DECL const unsigned number_of_format_parts =
compiletime_prepared_parts_type_provider::count_parts();
#else
static const unsigned number_of_format_parts = 0u;
#endif
public:
template <unsigned N> struct format_parts_array {
typedef format_part<char_type> value_type;
FMT_CONSTEXPR format_parts_array() : arr{} {}
FMT_CONSTEXPR value_type &operator[](unsigned ind) { return arr[ind]; }
FMT_CONSTEXPR const value_type *begin() const { return arr; }
FMT_CONSTEXPR const value_type *end() const { return begin() + N; }
private:
value_type arr[N];
};
struct empty {
// Parts preparator will search for it
typedef format_part<char_type> value_type;
};
typedef typename std::conditional<static_cast<bool>(number_of_format_parts),
format_parts_array<number_of_format_parts>,
empty>::type type;
};
template <typename Parts> class compiletime_prepared_parts_collector {
private:
typedef typename Parts::value_type format_part;
public:
FMT_CONSTEXPR explicit compiletime_prepared_parts_collector(Parts &parts)
: parts_{parts}, counter_{0u} {}
FMT_CONSTEXPR void add(format_part part) { parts_[counter_++] = part; }
FMT_CONSTEXPR void substitute_last(format_part part) {
parts_[counter_ - 1] = part;
}
FMT_CONSTEXPR format_part last() { return parts_[counter_ - 1]; }
private:
Parts &parts_;
unsigned counter_;
};
template <typename PartsContainer, typename Char>
FMT_CONSTEXPR PartsContainer prepare_parts(basic_string_view<Char> format) {
PartsContainer parts;
internal::parse_format_string</*IS_CONSTEXPR=*/false>(
format, format_preparation_handler<Char, PartsContainer>(format, parts));
return parts;
}
template <typename PartsContainer, typename Char>
FMT_CONSTEXPR PartsContainer
prepare_compiletime_parts(basic_string_view<Char> format) {
typedef compiletime_prepared_parts_collector<PartsContainer> collector;
PartsContainer parts;
collector c(parts);
internal::parse_format_string</*IS_CONSTEXPR=*/true>(
format, format_preparation_handler<Char, collector>(format, c));
return parts;
}
template <typename PartsContainer> class runtime_parts_provider {
public:
runtime_parts_provider() = delete;
template <typename Char>
runtime_parts_provider(basic_string_view<Char> format)
: parts_(prepare_parts<PartsContainer>(format)) {}
const PartsContainer &parts() const { return parts_; }
private:
PartsContainer parts_;
};
template <typename Format, typename PartsContainer>
struct compiletime_parts_provider {
compiletime_parts_provider() = delete;
template <typename Char>
FMT_CONSTEXPR compiletime_parts_provider(basic_string_view<Char>) {}
const PartsContainer &parts() const {
static FMT_CONSTEXPR_DECL const PartsContainer prepared_parts =
prepare_compiletime_parts<PartsContainer>(
internal::to_string_view(Format{}));
return prepared_parts;
}
};
template <typename PartsContainer>
struct parts_container_concept_check : std::true_type {
#if FMT_HAS_CONSTRUCTIBLE_TRAITS
static_assert(std::is_copy_constructible<PartsContainer>::value,
"PartsContainer is not copy constructible");
static_assert(std::is_move_constructible<PartsContainer>::value,
"PartsContainer is not move constructible");
#endif
template <typename T, typename = void>
struct has_format_part_type : std::false_type {};
template <typename T>
struct has_format_part_type<
T, typename void_<typename T::format_part_type>::type> : std::true_type {
};
static_assert(has_format_part_type<PartsContainer>::value,
"PartsContainer doesn't provide format_part_type typedef");
struct check_second {};
struct check_first : check_second {};
template <typename T> static std::false_type has_add_check(check_second);
template <typename T>
static decltype(declval<T>().add(declval<typename T::format_part_type>()),
std::true_type()) has_add_check(check_first);
typedef decltype(has_add_check<PartsContainer>(check_first())) has_add;
static_assert(has_add::value, "PartsContainer doesn't provide add() method");
template <typename T> static std::false_type has_last_check(check_second);
template <typename T>
static decltype(declval<T>().last(),
std::true_type()) has_last_check(check_first);
typedef decltype(has_last_check<PartsContainer>(check_first())) has_last;
static_assert(has_last::value,
"PartsContainer doesn't provide last() method");
template <typename T>
static std::false_type has_substitute_last_check(check_second);
template <typename T>
static decltype(
declval<T>().substitute_last(declval<typename T::format_part_type>()),
std::true_type()) has_substitute_last_check(check_first);
typedef decltype(has_substitute_last_check<PartsContainer>(
check_first())) has_substitute_last;
static_assert(has_substitute_last::value,
"PartsContainer doesn't provide substitute_last() method");
template <typename T> static std::false_type has_begin_check(check_second);
template <typename T>
static decltype(declval<T>().begin(),
std::true_type()) has_begin_check(check_first);
typedef decltype(has_begin_check<PartsContainer>(check_first())) has_begin;
static_assert(has_begin::value,
"PartsContainer doesn't provide begin() method");
template <typename T> static std::false_type has_end_check(check_second);
template <typename T>
static decltype(declval<T>().end(),
std::true_type()) has_end_check(check_first);
typedef decltype(has_end_check<PartsContainer>(check_first())) has_end;
static_assert(has_end::value, "PartsContainer doesn't provide end() method");
};
template <bool IS_CONSTEXPR, typename Format, typename /*PartsContainer*/>
struct parts_provider_type {
typedef compiletime_parts_provider<
Format, typename compiletime_prepared_parts_type_provider<Format>::type>
type;
};
template <typename Format, typename PartsContainer>
struct parts_provider_type</*IS_CONSTEXPR=*/false, Format, PartsContainer> {
static_assert(parts_container_concept_check<PartsContainer>::value,
"Parts container doesn't meet the concept");
typedef runtime_parts_provider<PartsContainer> type;
};
template <typename Format, typename PreparedPartsContainer, typename... Args>
struct basic_prepared_format {
typedef internal::prepared_format<Format,
typename internal::parts_provider_type<
is_compile_string<Format>::value,
Format, PreparedPartsContainer>::type,
Args...>
type;
};
template <typename Char>
std::basic_string<Char> to_runtime_format(basic_string_view<Char> format) {
return std::basic_string<Char>(format.begin(), format.size());
}
template <typename Char>
std::basic_string<Char> to_runtime_format(const Char *format) {
return std::basic_string<Char>(format);
}
template <typename Char, typename Container = std::vector<format_part<Char>>>
class parts_container {
public:
typedef format_part<Char> format_part_type;
void add(format_part_type part) { parts_.push_back(std::move(part)); }
void substitute_last(format_part_type part) {
parts_.back() = std::move(part);
}
format_part_type last() { return parts_.back(); }
auto begin() -> decltype(internal::declval<Container>().begin()) {
return parts_.begin();
}
auto begin() const -> decltype(internal::declval<const Container>().begin()) {
return parts_.begin();
}
auto end() -> decltype(internal::declval<Container>().end()) {
return parts_.end();
}
auto end() const -> decltype(internal::declval<const Container>().end()) {
return parts_.end();
}
private:
Container parts_;
};
// Delegate preparing to preparator, to take advantage of a partial
// specialization.
template <typename Format, typename... Args> struct preparator {
typedef parts_container<FMT_CHAR(Format)> container;
typedef typename basic_prepared_format<Format, container, Args...>::type
prepared_format_type;
static auto prepare(Format format) -> prepared_format_type {
return prepared_format_type(std::move(format));
}
};
template <typename PassedFormat, typename PreparedFormatFormat,
typename PartsContainer, typename... Args>
struct preparator<PassedFormat, prepared_format<PreparedFormatFormat,
PartsContainer, Args...>> {
typedef prepared_format<PreparedFormatFormat, PartsContainer, Args...>
prepared_format_type;
static auto prepare(PassedFormat format) -> prepared_format_type {
return prepared_format_type(std::move(format));
}
};
struct compiletime_format_tag {};
struct runtime_format_tag {};
template <typename Format> struct format_tag {
typedef typename std::conditional<is_compile_string<Format>::value,
compiletime_format_tag,
runtime_format_tag>::type type;
};
#if FMT_USE_CONSTEXPR
template <typename Format, typename... Args>
auto do_prepare(runtime_format_tag, Format format) {
return preparator<Format, Args...>::prepare(std::move(format));
}
template <typename Format, typename... Args>
FMT_CONSTEXPR auto do_prepare(compiletime_format_tag, const Format &format) {
return typename basic_prepared_format<Format, void, Args...>::type(format);
}
#else
template <typename Format, typename... Args>
auto do_prepare(const Format &format)
-> decltype(preparator<Format, Args...>::prepare(format)) {
return preparator<Format, Args...>::prepare(format);
}
#endif
} // namespace internal
template <typename Char, typename Container = std::vector<format_part<Char>>>
struct parts_container {
typedef internal::parts_container<Char, Container> type;
};
template <typename Format, typename PartsContainer, typename... Args>
struct basic_prepared_format {
typedef typename internal::basic_prepared_format<Format, PartsContainer,
Args...>::type type;
};
template <typename... Args> struct prepared_format {
typedef typename basic_prepared_format<
std::string, typename parts_container<char>::type, Args...>::type type;
};
template <typename... Args> struct wprepared_format {
typedef
typename basic_prepared_format<std::wstring,
typename parts_container<wchar_t>::type,
Args...>::type type;
};
#if FMT_USE_ALIAS_TEMPLATES
template <typename Char, typename Container = std::vector<format_part<Char>>>
using parts_container_t = typename parts_container<Char, Container>::type;
template <typename Format, typename PreparedPartsContainer, typename... Args>
using basic_prepared_format_t =
typename basic_prepared_format<Format, PreparedPartsContainer,
Args...>::type;
template <typename... Args>
using prepared_format_t =
basic_prepared_format_t<std::string, parts_container<char>, Args...>;
template <typename... Args>
using wprepared_format_t =
basic_prepared_format_t<std::wstring, parts_container<wchar_t>, Args...>;
#endif
#if FMT_USE_CONSTEXPR
template <typename... Args, typename Format>
FMT_CONSTEXPR auto prepare(Format format) {
return internal::do_prepare<Format, Args...>(
typename internal::format_tag<Format>::type{}, std::move(format));
}
#else
template <typename... Args, typename Format>
auto prepare(Format format) ->
typename internal::preparator<Format, Args...>::prepared_format_type {
return internal::preparator<Format, Args...>::prepare(std::move(format));
}
#endif
template <typename... Args, typename Char>
auto prepare(const Char *format) ->
typename internal::preparator<std::basic_string<Char>,
Args...>::prepared_format_type {
return prepare<Args...>(internal::to_runtime_format(format));
}
template <typename... Args, typename Char, unsigned N>
auto prepare(const Char(format)[N]) ->
typename internal::preparator<std::basic_string<Char>,
Args...>::prepared_format_type {
const auto view = basic_string_view<Char>(format, N);
return prepare<Args...>(internal::to_runtime_format(view));
}
template <typename... Args, typename Char>
auto prepare(basic_string_view<Char> format) ->
typename internal::preparator<std::basic_string<Char>,
Args...>::prepared_format_type {
return prepare<Args...>(internal::to_runtime_format(format));
}
FMT_END_NAMESPACE
#endif // FMT_PREPARE_H_

View File

@ -80,11 +80,16 @@ function(add_fmt_test name)
if (FMT_PEDANTIC)
target_compile_options(${name} PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif ()
if (MSVC)
target_compile_options(${name} PRIVATE "/bigobj")
endif(MSVC)
target_include_directories(${name} SYSTEM PUBLIC gtest gmock)
add_test(NAME ${name} COMMAND ${name})
endfunction()
add_fmt_test(assert-test)
add_fmt_test(common-format-test)
add_fmt_test(chrono-test)
add_fmt_test(core-test)
add_fmt_test(gtest-extra-test)
@ -94,6 +99,7 @@ if (NOT (MSVC AND BUILD_SHARED_LIBS))
endif ()
add_fmt_test(locale-test)
add_fmt_test(ostream-test)
add_fmt_test(prepare-test)
add_fmt_test(printf-test)
add_fmt_test(time-test)
add_fmt_test(custom-formatter-test)

1621
test/common-format-test.cc Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -154,7 +154,7 @@ TEST(OStreamTest, WriteToOStreamMaxSize) {
EXPECT_CALL(streambuf, xsputn(data, static_cast<std::streamsize>(n)))
.WillOnce(testing::Return(max_streamsize));
data += n;
size -= n;
size -= static_cast<std::size_t>(n);
} while (size != 0);
fmt::internal::write(os, buffer);
}

643
test/prepare-test.cc Normal file
View File

@ -0,0 +1,643 @@
// Formatting library for C++ - formatting library tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to prepare.h.
#include <cctype>
#include <cfloat>
#include <climits>
#include <cmath>
#include <cstring>
#include <deque>
#include <list>
#include <memory>
#include <stdint.h>
#include <string>
// Check if fmt/prepare.h compiles with windows.h included before it.
#ifdef _WIN32
#include <windows.h>
#endif
#include "fmt/prepare.h"
#include "gmock.h"
#include "gtest-extra.h"
#include "mock-allocator.h"
#include "util.h"
#undef ERROR
#undef min
#undef max
using testing::Return;
using testing::StrictMock;
class mock_parts_collector {
public:
MOCK_METHOD1(add, void(fmt::format_part<char>));
MOCK_METHOD1(substitute_last, void(fmt::format_part<char>));
MOCK_METHOD0(last, fmt::format_part<char>());
};
FMT_BEGIN_NAMESPACE
bool operator==(const internal::string_view_metadata &lhs,
const internal::string_view_metadata &rhs) {
return std::tie(lhs.offset_, lhs.size_) == std::tie(rhs.offset_, rhs.size_);
}
bool operator!=(const internal::string_view_metadata &lhs,
const internal::string_view_metadata &rhs) {
return !(lhs == rhs);
}
bool operator==(const format_part<char>::specification &lhs,
const format_part<char>::specification &rhs) {
if (lhs.arg_id.which != rhs.arg_id.which) {
return false;
}
typedef format_part<char>::argument_id::which_arg_id which_arg_id;
switch (lhs.arg_id.which) {
case which_arg_id::index: {
if (lhs.arg_id.val.index != rhs.arg_id.val.index) {
return false;
}
} break;
case which_arg_id::named_index: {
if (lhs.arg_id.val.named_index != rhs.arg_id.val.named_index) {
return false;
}
} break;
}
return std::tie(lhs.parsed_specs.width_, lhs.parsed_specs.fill_,
lhs.parsed_specs.align_, lhs.parsed_specs.precision,
lhs.parsed_specs.flags, lhs.parsed_specs.type) ==
std::tie(rhs.parsed_specs.width_, rhs.parsed_specs.fill_,
rhs.parsed_specs.align_, rhs.parsed_specs.precision,
rhs.parsed_specs.flags, rhs.parsed_specs.type);
}
bool operator!=(const format_part<char>::specification &lhs,
const format_part<char>::specification &rhs) {
return !(lhs == rhs);
}
bool operator==(const format_part<char> &lhs,
const fmt::format_part<char> &rhs) {
typedef format_part<char>::which_value which_value;
if (lhs.which != rhs.which ||
lhs.end_of_argument_id != rhs.end_of_argument_id) {
return false;
}
switch (lhs.which) {
case which_value::argument_id: {
return lhs.val.arg_id == rhs.val.arg_id;
}
case which_value::named_argument_id: {
return lhs.val.named_arg_id == rhs.val.named_arg_id;
}
case which_value::text: {
return lhs.val.text == rhs.val.text;
}
case which_value::specification: {
return lhs.val.spec == rhs.val.spec;
}
}
return false;
}
bool operator!=(const fmt::format_part<char> &lhs,
const fmt::format_part<char> &rhs) {
return !(lhs == rhs);
}
FMT_END_NAMESPACE
TEST(PrepareTest, FormatPart_ComparisonOperators) {
typedef fmt::format_part<char> format_part;
typedef fmt::internal::dynamic_format_specs<char> prepared_specs;
{
const auto part = format_part(0u);
const auto other = format_part(0u);
EXPECT_EQ(part, other);
}
{
const auto lhs = format_part(0u);
const auto rhs = format_part(1u);
EXPECT_NE(lhs, rhs);
}
{
const auto lhs = format_part(fmt::internal::string_view_metadata(0, 42));
const auto rhs = format_part(fmt::internal::string_view_metadata(0, 42));
EXPECT_EQ(lhs, rhs);
}
{
const auto lhs = format_part(fmt::internal::string_view_metadata(0, 42));
const auto rhs = format_part(fmt::internal::string_view_metadata(0, 4422));
EXPECT_NE(lhs, rhs);
}
{
auto lhs = format_part(0u);
auto rhs = format_part(fmt::internal::string_view_metadata(0, 42));
EXPECT_NE(lhs, rhs);
rhs = format_part(fmt::internal::string_view_metadata(0, 0));
EXPECT_NE(lhs, rhs);
}
{
auto lhs = format_part(0u);
lhs.end_of_argument_id = 42;
auto rhs = format_part(0u);
rhs.end_of_argument_id = 42;
EXPECT_EQ(lhs, rhs);
rhs.end_of_argument_id = 13;
EXPECT_NE(lhs, rhs);
}
{
const auto specs_argument_id = 0u;
const auto specs_named_argument_id =
fmt::internal::string_view_metadata(0, 42);
auto specs = format_part::specification(0u);
auto lhs = format_part(specs);
auto rhs = format_part(specs);
EXPECT_EQ(lhs, rhs);
const auto specs_text = fmt::string_view("<10");
specs.parsed_specs = prepared_specs();
lhs = format_part(specs);
rhs = format_part(specs);
EXPECT_EQ(lhs, rhs);
specs = format_part::specification(specs_named_argument_id);
lhs = format_part(specs);
rhs = format_part(specs);
EXPECT_EQ(lhs, rhs);
specs.parsed_specs = prepared_specs();
lhs = format_part(specs);
rhs = format_part(specs);
EXPECT_EQ(lhs, rhs);
auto lhs_spec = format_part::specification(specs_argument_id);
auto rhs_spec = format_part::specification(specs_named_argument_id);
lhs = format_part(lhs_spec);
rhs = format_part(rhs_spec);
EXPECT_NE(lhs, rhs);
const auto lhs_specs_text = fmt::string_view("<10");
const auto rhs_specs_text = fmt::string_view("<42");
lhs_spec = format_part::specification(specs_argument_id);
rhs_spec = format_part::specification(specs_argument_id);
lhs_spec.parsed_specs.precision = 1;
rhs_spec.parsed_specs.precision = 2;
lhs = format_part(lhs_spec);
rhs = format_part(rhs_spec);
EXPECT_NE(lhs, rhs);
}
{
const auto specs_text = fmt::string_view{"<10"};
const auto specs_argument_id = 0u;
const auto specs_named_argument_id =
fmt::internal::string_view_metadata(0, 42);
auto specs = format_part::specification(specs_argument_id);
auto lhs = format_part(specs);
auto rhs = format_part(0u);
auto rhs2 = format_part(fmt::internal::string_view_metadata(0, 42));
EXPECT_NE(lhs, rhs);
EXPECT_NE(lhs, rhs2);
specs.parsed_specs = prepared_specs();
lhs = format_part{specs};
EXPECT_NE(lhs, rhs);
EXPECT_NE(lhs, rhs2);
specs = format_part::specification(specs_named_argument_id);
EXPECT_NE(lhs, rhs);
EXPECT_NE(lhs, rhs2);
specs.parsed_specs = prepared_specs();
lhs = format_part(specs);
EXPECT_NE(lhs, rhs);
EXPECT_NE(lhs, rhs2);
}
}
TEST(PrepareTest, FormatPreparationHandler_OnText_AddsPartWithText) {
typedef fmt::format_part<char> format_part;
typedef StrictMock<mock_parts_collector> parts_mock;
parts_mock parts;
const auto format = fmt::internal::to_string_view("text");
fmt::internal::format_preparation_handler<char, parts_mock> handler(format,
parts);
const auto expected_text = fmt::internal::string_view_metadata(
0u, static_cast<unsigned>(format.size()));
EXPECT_CALL(parts, add(format_part(expected_text)));
handler.on_text(format.begin(), format.end());
}
TEST(PrepareTest, FormatPreparationHandler_OnArgId_AddsPartWithIncrementedId) {
typedef fmt::format_part<char> format_part;
typedef StrictMock<mock_parts_collector> parts_mock;
parts_mock parts;
const auto format = fmt::internal::to_string_view("");
fmt::internal::format_preparation_handler<char, parts_mock> handler(format,
parts);
const auto expected_first_arg_id = 0u;
const auto expected_second_arg_id = 1u;
EXPECT_CALL(parts, add(format_part(expected_first_arg_id)));
EXPECT_CALL(parts, add(format_part(expected_second_arg_id)));
handler.on_arg_id();
handler.on_arg_id();
}
TEST(PrepareTest, FormatPreparationHandler_OnArgId_AddsPartWithPassedId) {
typedef fmt::format_part<char> format_part;
typedef StrictMock<mock_parts_collector> parts_mock;
parts_mock parts;
const auto format = fmt::internal::to_string_view("");
fmt::internal::format_preparation_handler<char, parts_mock> handler(format,
parts);
const auto expected_first_arg_id = 2u;
const auto expected_second_arg_id = 0u;
const auto expected_third_arg_id = 1u;
EXPECT_CALL(parts, add(format_part(expected_first_arg_id)));
EXPECT_CALL(parts, add(format_part(expected_second_arg_id)));
EXPECT_CALL(parts, add(format_part(expected_third_arg_id)));
handler.on_arg_id(expected_first_arg_id);
handler.on_arg_id(expected_second_arg_id);
handler.on_arg_id(expected_third_arg_id);
}
TEST(PrepareTest, FormatPreparationHandler_OnArgId_AddsPartWithPassedNamedId) {
typedef fmt::format_part<char> format_part;
typedef format_part::named_argument_id named_argument_id;
typedef StrictMock<mock_parts_collector> parts_mock;
parts_mock parts;
const auto format = fmt::internal::to_string_view("0123456789");
fmt::internal::format_preparation_handler<char, parts_mock> handler(format,
parts);
const auto expected_first_arg_id = fmt::string_view(format.data(), 1);
const auto expected_first_arg_view_metadata =
fmt::internal::string_view_metadata(0, 1);
const auto expected_second_arg_id = fmt::string_view(format.data() + 3, 2);
const auto expected_second_arg_view_metadata =
fmt::internal::string_view_metadata(3, 2);
const auto expected_third_arg_id = fmt::string_view(format.data() + 6, 3);
const auto expected_third_arg_view_metadata =
fmt::internal::string_view_metadata(6, 3);
EXPECT_CALL(parts, add(format_part(
named_argument_id(expected_first_arg_view_metadata))));
EXPECT_CALL(
parts,
add(format_part(named_argument_id(expected_second_arg_view_metadata))));
EXPECT_CALL(parts, add(format_part(
named_argument_id(expected_third_arg_view_metadata))));
handler.on_arg_id(expected_first_arg_id);
handler.on_arg_id(expected_second_arg_id);
handler.on_arg_id(expected_third_arg_id);
}
TEST(PrepareTest,
FormatPreparationHandler_OnReplacementField_SetsEndOfArgumentId) {
typedef fmt::format_part<char> format_part;
typedef StrictMock<mock_parts_collector> parts_mock;
const auto format = fmt::internal::to_string_view("{:<}");
parts_mock parts;
const auto last_part = format_part(0u);
EXPECT_CALL(parts, last()).WillOnce(Return(last_part));
auto expected_substitution_part = last_part;
expected_substitution_part.end_of_argument_id = 1;
EXPECT_CALL(parts, substitute_last(expected_substitution_part));
fmt::internal::format_preparation_handler<char, parts_mock> handler(format,
parts);
handler.on_replacement_field(format.data() + 1);
}
TEST(
PrepareTest,
FormatPreparationHandlerLastPartArgIndex_OnFormatSpecs_UpdatesLastAddedPart) {
typedef fmt::format_part<char> format_part;
typedef StrictMock<mock_parts_collector> parts_mock;
parts_mock parts;
const auto specification_test_text = fmt::internal::to_string_view("{:<10}");
const auto specification_offset = 2u;
const auto specification_begin_it =
specification_test_text.begin() + specification_offset;
fmt::internal::format_preparation_handler<char, parts_mock> handler(
specification_test_text, parts);
const auto last_part = format_part(0u);
format_part::specification expected_specification(0u);
fmt::internal::dynamic_format_specs<char> specs{};
specs.align_ = fmt::alignment::ALIGN_LEFT;
specs.width_ = 10;
expected_specification.parsed_specs = specs;
auto expected_substitution_part = format_part(expected_specification);
expected_substitution_part.end_of_argument_id = specification_offset;
EXPECT_CALL(parts, last()).WillOnce(Return(last_part));
EXPECT_CALL(parts, substitute_last(expected_substitution_part));
handler.on_format_specs(specification_begin_it,
specification_test_text.end());
}
TEST(
PrepareTest,
FormatPreparationHandlerLastPartNamedArgIndex_OnFormatSpecs_UpdatesLastAddedPart) {
typedef fmt::format_part<char> format_part;
typedef StrictMock<mock_parts_collector> parts_mock;
parts_mock parts;
const auto specification_test_text = fmt::internal::to_string_view("{:<10}");
const auto specification_offset = 2u;
const auto specification_begin_it =
specification_test_text.begin() + specification_offset;
fmt::internal::format_preparation_handler<char, parts_mock> handler(
specification_test_text, parts);
const auto arg_id = fmt::internal::string_view_metadata(0, 42);
const auto last_part = format_part(format_part::named_argument_id(arg_id));
format_part::specification expected_specification(arg_id);
fmt::internal::dynamic_format_specs<char> specs{};
specs.align_ = fmt::alignment::ALIGN_LEFT;
specs.width_ = 10;
expected_specification.parsed_specs = specs;
auto expected_substitution_part = format_part(expected_specification);
expected_substitution_part.end_of_argument_id = specification_offset;
EXPECT_CALL(parts, last()).WillOnce(Return(last_part));
EXPECT_CALL(parts, substitute_last(expected_substitution_part));
handler.on_format_specs(specification_begin_it,
specification_test_text.end());
}
// 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::internal::compiletime_prepared_parts_type_provider<decltype(
format)>
provider;
typedef typename provider::template format_parts_array<EXPECTED_PARTS_COUNT>
expected_parts_type;
static_assert(
std::is_same<typename provider::type, expected_parts_type>::value,
"CompileTimePreparedPartsTypeProvider test failed");
}
TEST(PrepareTest, 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
// Use the struct instead of a function to workaround GCC 4.4's 'sorry,
// unimplemented: mangling template_id_expr' issue.
template <typename... Args> struct copied_prepared_format_creator {
static decltype(fmt::prepare<Args...>(fmt::internal::declval<std::string>()))
make(std::string format_str) {
auto prepared_format = fmt::prepare<Args...>(std::move(format_str));
auto copied_prepared_format = prepared_format;
prepared_format = fmt::prepare<Args...>("");
return copied_prepared_format;
}
};
TEST(PrepareTest, CopyPreparedFormat_InternalStringViewsAreNotInvalidated) {
auto prepared = copied_prepared_format_creator<int, std::string>::make(
"before {} middle {} after");
EXPECT_EQ("before 42 middle text after", prepared.format(42, "text"));
prepared = copied_prepared_format_creator<int, std::string>::make(
"before {0} middle {1} after");
EXPECT_EQ("before 42 middle text after", prepared.format(42, "text"));
{
typedef decltype(fmt::arg("first", 42)) argument0;
typedef decltype(fmt::arg("second", "text")) argument1;
auto named_prepared =
copied_prepared_format_creator<argument0, argument1>::make(
"before {first} middle {second} after");
EXPECT_EQ("before 42 middle text after",
named_prepared.format(fmt::arg("first", 42),
fmt::arg("second", "text")));
}
{
typedef decltype(fmt::arg("value", "12345")) argument0;
typedef decltype(fmt::arg("width", 10)) argument1;
auto named_prepared =
copied_prepared_format_creator<argument0, argument1>::make(
">>>{value:>{width}}<<<");
EXPECT_EQ(">>> 12345<<<",
named_prepared.format(fmt::arg("value", "12345"),
fmt::arg("width", 10)));
}
}
TEST(PepareTest, ReusedPreparedFormatType) {
typedef fmt::prepared_format<std::string, int>::type prepared_format;
prepared_format prepared = fmt::prepare<prepared_format>("The {} is {}.");
EXPECT_EQ("The answer is 42.", prepared.format("answer", 42));
prepared = fmt::prepare<prepared_format>("40 {} 2 = {}");
EXPECT_EQ("40 + 2 = 42", prepared.format("+", 42));
}
TEST(PrepareTest, UserProvidedPartsContainerUnderlyingContainer) {
typedef fmt::format_part<char> format_part;
typedef fmt::parts_container<char, std::list<format_part>>::type
parts_container;
typedef fmt::basic_prepared_format<std::string, parts_container, std::string,
int>::type prepared_format;
prepared_format prepared = fmt::prepare<prepared_format>("The {} is {}.");
EXPECT_EQ("The answer is 42.", prepared.format("answer", 42));
prepared = fmt::prepare<prepared_format>("40 {} 2 = {}");
EXPECT_EQ("40 + 2 = 42", prepared.format("+", 42));
}
class custom_parts_container {
public:
typedef fmt::format_part<char> format_part_type;
private:
typedef std::deque<format_part_type> parts;
public:
void add(format_part_type part) { parts_.push_back(std::move(part)); }
void substitute_last(format_part_type part) {
parts_.back() = std::move(part);
}
format_part_type last() { return parts_.back(); }
auto begin() -> decltype(fmt::internal::declval<parts>().begin()) {
return parts_.begin();
}
auto begin() const
-> decltype(fmt::internal::declval<const parts>().begin()) {
return parts_.begin();
}
auto end() -> decltype(fmt::internal::declval<parts>().begin()) {
return parts_.end();
}
auto end() const -> decltype(fmt::internal::declval<const parts>().begin()) {
return parts_.end();
}
private:
parts parts_;
};
TEST(PrepareTest, UserProvidedPartsContainer) {
typedef fmt::basic_prepared_format<std::string, custom_parts_container,
std::string, int>::type prepared_format;
prepared_format prepared = fmt::prepare<prepared_format>("The {} is {}.");
EXPECT_EQ("The answer is 42.", prepared.format("answer", 42));
prepared = fmt::prepare<prepared_format>("40 {} 2 = {}");
EXPECT_EQ("40 + 2 = 42", prepared.format("+", 42));
}
TEST(PrepareTest, PassConstCharPointerFormat) {
const char *c_format = "test {}";
const auto prepared = fmt::prepare<int>(c_format);
EXPECT_EQ("test 42", prepared.format(42));
const wchar_t *wc_format = L"test {}";
const auto wprepared = fmt::prepare<int>(wc_format);
EXPECT_EQ(L"test 42", wprepared.format(42));
}
TEST(PrepareTest, PassCharArrayFormat) {
char c_format[] = "test {}";
const auto prepared = fmt::prepare<int>(c_format);
EXPECT_EQ("test 42", prepared.format(42));
wchar_t wc_format[] = L"test {}";
const auto wprepared = fmt::prepare<int>(wc_format);
EXPECT_EQ(L"test 42", wprepared.format(42));
}
TEST(PrepareTest, PassConstCharArrayFormat) {
const char c_format[] = "test {}";
const auto prepared = fmt::prepare<int>(c_format);
EXPECT_EQ("test 42", prepared.format(42));
const wchar_t wc_format[] = L"test {}";
const auto wprepared = fmt::prepare<int>(wc_format);
EXPECT_EQ(L"test 42", wprepared.format(42));
}
TEST(PrepareTest, PassStringLiteralFormat) {
const auto prepared = fmt::prepare<int>("test {}");
EXPECT_EQ("test 42", prepared.format(42));
const auto wprepared = fmt::prepare<int>(L"test {}");
EXPECT_EQ(L"test 42", wprepared.format(42));
}
TEST(PrepareTest, PassStringViewFormat) {
const auto prepared =
fmt::prepare<int>(fmt::basic_string_view<char>("test {}"));
EXPECT_EQ("test 42", prepared.format(42));
const auto wprepared =
fmt::prepare<int>(fmt::basic_string_view<wchar_t>(L"test {}"));
EXPECT_EQ(L"test 42", wprepared.format(42));
}
TEST(PrepareTest, PassBasicStringFormat) {
const auto prepared = fmt::prepare<int>(std::string("test {}"));
EXPECT_EQ("test 42", prepared.format(42));
const auto wprepared = fmt::prepare<int>(std::wstring(L"test {}"));
EXPECT_EQ(L"test 42", wprepared.format(42));
}
#if FMT_USE_CONSTEXPR
TEST(PrepareTest, PassCompileString) {
const auto prepared = fmt::prepare<int>(FMT_STRING("test {}"));
EXPECT_EQ("test 42", prepared.format(42));
const auto wprepared = fmt::prepare<int>(FMT_STRING(L"test {}"));
EXPECT_EQ(L"test 42", wprepared.format(42));
}
#endif
template <typename T> struct user_allocator {
typedef T value_type;
typedef value_type *pointer;
typedef const value_type *const_pointer;
typedef value_type &reference;
typedef const value_type &const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
template <typename U> struct rebind { typedef user_allocator<U> other; };
user_allocator() = default;
~user_allocator() = default;
template <typename U> user_allocator(const user_allocator<U> &) {}
pointer allocate(size_type cnt,
typename std::allocator<void>::const_pointer = 0) {
return new value_type[cnt];
}
void deallocate(pointer p, size_type cnt) { delete[] p; }
void construct(pointer p, const value_type &val) { new (p) value_type(val); }
void destroy(pointer p) { (*p).~value_type(); }
bool operator==(const user_allocator &other) const { return true; }
bool operator!=(const user_allocator &other) const { return false; }
};
TEST(PrepareTest, PassUserTypeFormat) {
typedef std::basic_string<char, std::char_traits<char>, user_allocator<char>>
user_format;
const auto prepared = fmt::prepare<int>(user_format("test {}"));
EXPECT_EQ("test 42", prepared.format(42));
}