diff --git a/fmt/format.cc b/fmt/format.cc index 499e8382..3e2a6eec 100644 --- a/fmt/format.cc +++ b/fmt/format.cc @@ -443,8 +443,7 @@ template void printf(BasicWriter &w, BasicCStringRef format, format_args args); -FMT_FUNC int vfprintf(std::FILE *f, CStringRef format, - basic_format_args> args) { +FMT_FUNC int vfprintf(std::FILE *f, CStringRef format, printf_args args) { MemoryWriter w; printf(w, format, args); std::size_t size = w.size(); @@ -475,7 +474,7 @@ template int internal::CharTraits::format_float( template void internal::FixedBuffer::grow(std::size_t); -template void internal::ArgMap::init(const format_args &args); +template void internal::ArgMap::init(const wformat_args &args); template void printf_context::format(WWriter &writer); diff --git a/fmt/format.h b/fmt/format.h index e03c66aa..ed706466 100644 --- a/fmt/format.h +++ b/fmt/format.h @@ -414,6 +414,8 @@ class BasicStringRef { std::size_t size_; public: + BasicStringRef() : data_(0), size_(0) {} + /** Constructs a string reference object from a C string and a size. */ BasicStringRef(const Char *s, std::size_t size) : data_(s), size_(size) {} @@ -1027,16 +1029,102 @@ struct Value { CSTRING, STRING, WSTRING, POINTER, CUSTOM }; }; + +template +class ArgMap; } // namespace internal +template +class basic_format_args; + // A formatting argument. It is a trivially copyable/constructible type to // allow storage in internal::MemoryBuffer. -struct format_arg : internal::Value { - Type type; +template +class basic_format_arg : public internal::Value { + protected: + Type type_; - explicit operator bool() const noexcept { return type != NONE; } + template + friend typename std::result_of::type + visit(Visitor &&vis, basic_format_arg arg); + + template + friend class basic_format_args; + + template + friend class internal::ArgMap; + + void check_type() const { + FMT_ASSERT(type_ > NAMED_ARG, "invalid argument type"); + } + + public: + explicit operator bool() const noexcept { return type_ != NONE; } + + bool is_integral() const { + check_type(); + return type_ <= LAST_INTEGER_TYPE; + } + + bool is_numeric() const { + check_type(); + return type_ <= LAST_NUMERIC_TYPE; + } + + bool is_pointer() const { + check_type(); + return type_ == POINTER; + } }; +typedef basic_format_arg format_arg; +typedef basic_format_arg wformat_arg; + +/** + \rst + Visits an argument dispatching to the appropriate visit method based on + the argument type. For example, if the argument type is ``double`` then + ``vis(value)`` will be called with the value of type ``double``. + \endrst + */ +template +typename std::result_of::type + visit(Visitor &&vis, basic_format_arg arg) { + switch (arg.type_) { + case format_arg::NONE: + case format_arg::NAMED_ARG: + FMT_ASSERT(false, "invalid argument type"); + break; + case format_arg::INT: + return vis(arg.int_value); + case format_arg::UINT: + return vis(arg.uint_value); + case format_arg::LONG_LONG: + return vis(arg.long_long_value); + case format_arg::ULONG_LONG: + return vis(arg.ulong_long_value); + case format_arg::BOOL: + return vis(arg.int_value != 0); + case format_arg::CHAR: + return vis(static_cast(arg.int_value)); + case format_arg::DOUBLE: + return vis(arg.double_value); + case format_arg::LONG_DOUBLE: + return vis(arg.long_double_value); + case format_arg::CSTRING: + return vis(arg.string.value); + case format_arg::STRING: + return vis(arg.string); + case format_arg::WSTRING: + return vis(arg.wstring); + case format_arg::POINTER: + return vis(arg.pointer); + case format_arg::CUSTOM: + return vis(arg.custom); + } + return typename std::result_of::type(); +} + namespace internal { template @@ -1251,7 +1339,7 @@ constexpr Type type() { return gettype::type>(); } // Makes a format_arg object from any type. template -class MakeValue : public format_arg { +class MakeValue : public basic_format_arg { public: typedef typename Context::char_type Char; @@ -1279,13 +1367,13 @@ class MakeValue : public format_arg { MakeValue(typename WCharHelper::Unsupported); void set_string(StringRef str) { - string.value = str.data(); - string.size = str.size(); + this->string.value = str.data(); + this->string.size = str.size(); } void set_string(WStringRef str) { - wstring.value = str.data(); - wstring.size = str.size(); + this->wstring.value = str.data(); + this->wstring.size = str.size(); } // Formats an argument of a custom type, such as a user-defined class. @@ -1302,8 +1390,8 @@ class MakeValue : public format_arg { #define FMT_MAKE_VALUE_(Type, field, TYPE, rhs) \ MakeValue(Type value) { \ - static_assert(internal::type() == TYPE, "invalid type"); \ - field = rhs; \ + static_assert(internal::type() == MakeValue::TYPE, "invalid type"); \ + this->field = rhs; \ } #define FMT_MAKE_VALUE(Type, field, TYPE) \ @@ -1319,16 +1407,16 @@ class MakeValue : public format_arg { // To minimize the number of types we need to deal with, long is // translated either to int or to long long depending on its size. if (const_check(sizeof(long) == sizeof(int))) - int_value = static_cast(value); + this->int_value = static_cast(value); else - long_long_value = value; + this->long_long_value = value; } MakeValue(unsigned long value) { if (const_check(sizeof(unsigned long) == sizeof(unsigned))) - uint_value = static_cast(value); + this->uint_value = static_cast(value); else - ulong_long_value = value; + this->ulong_long_value = value; } FMT_MAKE_VALUE(LongLong, long_long_value, LONG_LONG) @@ -1343,14 +1431,14 @@ class MakeValue : public format_arg { #if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) typedef typename WCharHelper::Supported WChar; MakeValue(WChar value) { - static_assert(internal::type() == CHAR, "invalid type"); - int_value = value; + static_assert(internal::type() == MakeValue::CHAR, "invalid type"); + this->int_value = value; } #endif #define FMT_MAKE_STR_VALUE(Type, TYPE) \ MakeValue(Type value) { \ - static_assert(internal::type() == TYPE, "invalid type"); \ + static_assert(internal::type() == MakeValue::TYPE, "invalid type"); \ set_string(value); \ } @@ -1366,7 +1454,7 @@ class MakeValue : public format_arg { #define FMT_MAKE_WSTR_VALUE(Type, TYPE) \ MakeValue(typename WCharHelper::Supported value) { \ - static_assert(internal::type() == TYPE, "invalid type"); \ + static_assert(internal::type() == MakeValue::TYPE, "invalid type"); \ set_string(value); \ } @@ -1382,49 +1470,51 @@ class MakeValue : public format_arg { MakeValue(const T &value, typename EnableIf::value>::value, int>::type = 0) { - static_assert(internal::type() == CUSTOM, "invalid type"); - custom.value = &value; - custom.format = &format_custom_arg; + static_assert(internal::type() == MakeValue::CUSTOM, "invalid type"); + this->custom.value = &value; + this->custom.format = &format_custom_arg; } template MakeValue(const T &value, typename EnableIf::value, int>::type = 0) { - static_assert(internal::type() == INT, "invalid type"); - int_value = value; + static_assert(internal::type() == MakeValue::INT, "invalid type"); + this->int_value = value; } // Additional template param `Char_` is needed here because make_type always // uses char. template MakeValue(const NamedArg &value) { - static_assert(internal::type &>() == NAMED_ARG, - "invalid type"); - pointer = &value; + static_assert( + internal::type &>() == MakeValue::NAMED_ARG, + "invalid type"); + this->pointer = &value; } }; template -class MakeArg : public format_arg { + class MakeArg : public basic_format_arg { public: MakeArg() { - type = format_arg::NONE; + this->type_ = format_arg::NONE; } template MakeArg(const T &value) - : format_arg(MakeValue(value)) { - type = internal::type(); + : basic_format_arg(MakeValue(value)) { + this->type_ = internal::type(); } }; template -struct NamedArg : format_arg { +struct NamedArg : basic_format_arg { BasicStringRef name; template NamedArg(BasicStringRef argname, const T &value) - : format_arg(MakeArg< basic_format_context >(value)), name(argname) {} + : basic_format_arg(MakeArg< basic_format_context >(value)), + name(argname) {} }; class RuntimeError : public std::runtime_error { @@ -1433,9 +1523,6 @@ class RuntimeError : public std::runtime_error { ~RuntimeError() throw(); }; -template -class ArgMap; - template constexpr uint64_t make_type() { return type() | (make_type() << 4); @@ -1482,8 +1569,12 @@ inline format_arg_store } /** Formatting arguments. */ -template +template class basic_format_args { + public: + typedef unsigned size_type; + typedef basic_format_arg format_arg; + private: // To reduce compiled code size per formatting function call, types of first // MAX_PACKED_ARGS arguments are passed in the types_ field. @@ -1498,21 +1589,43 @@ class basic_format_args { const format_arg *args_; }; - format_arg::Type type(unsigned index) const { + typename format_arg::Type type(unsigned index) const { unsigned shift = index * 4; uint64_t mask = 0xf; - return static_cast((types_ & (mask << shift)) >> shift); + return static_cast( + (types_ & (mask << shift)) >> shift); } - template - friend class internal::ArgMap; + friend class internal::ArgMap; void set_data(const internal::Value *values) { values_ = values; } void set_data(const format_arg *args) { args_ = args; } - public: - typedef unsigned size_type; + format_arg get(size_type index) const { + format_arg arg; + bool use_values = type(internal::MAX_PACKED_ARGS - 1) == format_arg::NONE; + if (index < internal::MAX_PACKED_ARGS) { + typename format_arg::Type arg_type = type(index); + internal::Value &val = arg; + if (arg_type != format_arg::NONE) + val = use_values ? values_[index] : args_[index]; + arg.type_ = arg_type; + return arg; + } + if (use_values) { + // The index is greater than the number of arguments that can be stored + // in values, so return a "none" argument. + arg.type_ = format_arg::NONE; + return arg; + } + for (unsigned i = internal::MAX_PACKED_ARGS; i <= index; ++i) { + if (args_[i].type_ == format_arg::NONE) + return args_[i]; + } + return args_[index]; + } + public: basic_format_args() : types_(0) {} template @@ -1523,77 +1636,14 @@ class basic_format_args { /** Returns the argument at specified index. */ format_arg operator[](size_type index) const { - format_arg arg; - bool use_values = type(internal::MAX_PACKED_ARGS - 1) == format_arg::NONE; - if (index < internal::MAX_PACKED_ARGS) { - format_arg::Type arg_type = type(index); - internal::Value &val = arg; - if (arg_type != format_arg::NONE) - val = use_values ? values_[index] : args_[index]; - arg.type = arg_type; - return arg; - } - if (use_values) { - // The index is greater than the number of arguments that can be stored - // in values, so return a "none" argument. - arg.type = format_arg::NONE; - return arg; - } - for (unsigned i = internal::MAX_PACKED_ARGS; i <= index; ++i) { - if (args_[i].type == format_arg::NONE) - return args_[i]; - } - return args_[index]; + format_arg arg = get(index); + return arg.type_ == format_arg::NAMED_ARG ? + *static_cast(arg.pointer) : arg; } }; -typedef basic_format_args> format_args; -typedef basic_format_args> wformat_args; - -/** - \rst - Visits an argument dispatching to the appropriate visit method based on - the argument type. For example, if the argument type is ``double`` then - ``vis(value)`` will be called with the value of type ``double``. - \endrst - */ -template -typename std::result_of::type visit(Visitor &&vis, - format_arg arg) { - switch (arg.type) { - case format_arg::NONE: - case format_arg::NAMED_ARG: - FMT_ASSERT(false, "invalid argument type"); - break; - case format_arg::INT: - return vis(arg.int_value); - case format_arg::UINT: - return vis(arg.uint_value); - case format_arg::LONG_LONG: - return vis(arg.long_long_value); - case format_arg::ULONG_LONG: - return vis(arg.ulong_long_value); - case format_arg::BOOL: - return vis(arg.int_value != 0); - case format_arg::CHAR: - return vis(static_cast(arg.int_value)); - case format_arg::DOUBLE: - return vis(arg.double_value); - case format_arg::LONG_DOUBLE: - return vis(arg.long_double_value); - case format_arg::CSTRING: - return vis(arg.string.value); - case format_arg::STRING: - return vis(arg.string); - case format_arg::WSTRING: - return vis(arg.wstring); - case format_arg::POINTER: - return vis(arg.pointer); - case format_arg::CUSTOM: - return vis(arg.custom); - } - return typename std::result_of::type(); -} +typedef basic_format_args, char> format_args; +typedef basic_format_args, wchar_t> wformat_args; enum Alignment { ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC @@ -1822,16 +1872,17 @@ template class ArgMap { private: typedef std::vector< - std::pair, format_arg> > MapType; + std::pair, basic_format_arg > > MapType; typedef typename MapType::value_type Pair; MapType map_; public: - template - void init(const basic_format_args &args); + template + void init(const basic_format_args &args); - const format_arg *find(const fmt::BasicStringRef &name) const { + const basic_format_arg + *find(const fmt::BasicStringRef &name) const { // The list is unsorted, so just return the first matching name. for (typename MapType::const_iterator it = map_.begin(), end = map_.end(); it != end; ++it) { @@ -1843,8 +1894,8 @@ class ArgMap { }; template -template -void ArgMap::init(const basic_format_args &args) { +template +void ArgMap::init(const basic_format_args &args) { if (!map_.empty()) return; typedef internal::NamedArg NamedArg; @@ -1874,8 +1925,8 @@ void ArgMap::init(const basic_format_args &args) { map_.push_back(Pair(named_arg->name, *named_arg)); } } - for (unsigned i = MAX_PACKED_ARGS;/*nothing*/; ++i) { - switch (args.args_[i].type) { + for (unsigned i = MAX_PACKED_ARGS; ; ++i) { + switch (args.args_[i].type_) { case format_arg::NONE: return; case format_arg::NAMED_ARG: @@ -1955,7 +2006,7 @@ class ArgFormatterBase { write(value); } - void operator()(wchar_t value) { + void operator()(Char value) { if (spec_.type_ && spec_.type_ != 'c') { spec_.flags_ |= CHAR_FLAG; writer_.write_int(value, spec_); @@ -2016,29 +2067,24 @@ template class format_context_base { private: const Char *ptr_; - basic_format_args args_; + basic_format_args args_; int next_arg_index_; protected: - format_context_base(const Char *format_str, basic_format_args args) + typedef basic_format_arg format_arg; + + format_context_base(const Char *format_str, + basic_format_args args) : ptr_(format_str), args_(args), next_arg_index_(0) {} ~format_context_base() {} - basic_format_args args() const { return args_; } + basic_format_args args() const { return args_; } // Returns the argument with specified index. format_arg do_get_arg(unsigned arg_index, const char *&error) { format_arg arg = args_[arg_index]; - switch (arg.type) { - case format_arg::NONE: - error = "argument index out of range"; - break; - case format_arg::NAMED_ARG: - arg = *static_cast(arg.pointer); - break; - default: - /*nothing*/; - } + if (!arg) + error = "argument index out of range"; return arg; } @@ -2107,13 +2153,14 @@ class basic_format_context : FMT_DISALLOW_COPY_AND_ASSIGN(basic_format_context); - typedef internal::format_context_base Base; + typedef internal::format_context_base> Base; + using typename Base::format_arg; using Base::get_arg; // Checks if manual indexing is used and returns the argument with // specified name. - format_arg get_arg(BasicStringRef name, const char *&error); + basic_format_arg get_arg(BasicStringRef name, const char *&error); public: /** The character type for the output. */ @@ -2126,11 +2173,11 @@ class basic_format_context : \endrst */ basic_format_context(const Char *format_str, - basic_format_args args) + basic_format_args args) : Base(format_str, args) {} // Parses argument id and returns corresponding argument. - format_arg parse_arg_id(); + basic_format_arg parse_arg_id(); using Base::ptr; }; @@ -2364,7 +2411,7 @@ class BasicWriter { } void vwrite(BasicCStringRef format, - basic_format_args> args); + basic_format_args, Char> args); /** \rst Writes formatted data. @@ -3281,45 +3328,121 @@ unsigned parse_nonnegative_int(const Char *&s) { return value; } -inline void require_numeric_argument(const format_arg &arg, char spec) { - if (arg.type > format_arg::LAST_NUMERIC_TYPE) { - std::string message = - fmt::format("format specifier '{}' requires numeric argument", spec); - FMT_THROW(fmt::format_error(message)); +template +inline void require_numeric_argument( + const basic_format_arg &arg, char spec) { + if (!arg.is_numeric()) { + FMT_THROW(fmt::format_error( + fmt::format("format specifier '{}' requires numeric argument", spec))); } } +struct IsUnsigned { + template + typename std::enable_if::value, bool>::type + operator()(T value) { + return true; + } + + template + typename std::enable_if::value, bool>::type + operator()(T value) { + return false; + } +}; + template -void check_sign(const Char *&s, const format_arg &arg) { +void check_sign(const Char *&s, const basic_format_arg &arg) { char sign = static_cast(*s); require_numeric_argument(arg, sign); - if (arg.type == format_arg::UINT || arg.type == format_arg::ULONG_LONG) { + if (visit(IsUnsigned(), arg)) { FMT_THROW(format_error(fmt::format( "format specifier '{}' requires signed argument", sign))); } ++s; } + +template +class CustomFormatter { + private: + BasicWriter &writer_; + Context &ctx_; + + public: + CustomFormatter(BasicWriter &writer, Context &ctx) + : writer_(writer), ctx_(ctx) {} + + bool operator()(format_arg::CustomValue custom) { + custom.format(&writer_, custom.value, &ctx_); + return true; + } + + template + bool operator()(T) { return false; } +}; + +template +struct IsInteger { + enum { + value = std::is_integral::value && !std::is_same::value && + !std::is_same::value && !std::is_same::value + }; +}; + +struct WidthHandler { + template + typename std::enable_if::value, ULongLong>::type + operator()(T value) { + if (is_negative(value)) + FMT_THROW(format_error("negative width")); + return value; + } + + template + typename std::enable_if::value, ULongLong>::type + operator()(T value) { + FMT_THROW(format_error("width is not integer")); + return 0; + } +}; + +struct PrecisionHandler { + template + typename std::enable_if::value, ULongLong>::type + operator()(T value) { + if (is_negative(value)) + FMT_THROW(format_error("negative precision")); + return value; + } + + template + typename std::enable_if::value, ULongLong>::type + operator()(T value) { + FMT_THROW(format_error("precision is not integer")); + return 0; + } +}; } // namespace internal template -inline format_arg basic_format_context::get_arg( +inline basic_format_arg basic_format_context::get_arg( BasicStringRef name, const char *&error) { if (this->check_no_auto_index(error)) { map_.init(this->args()); - const format_arg *arg = map_.find(name); + const basic_format_arg *arg = map_.find(name); if (arg) return *arg; error = "argument not found"; } - return format_arg(); + return basic_format_arg(); } template -inline format_arg basic_format_context::parse_arg_id() { +inline basic_format_arg basic_format_context::parse_arg_id() { const Char *&s = this->ptr(); if (!internal::is_name_start(*s)) { const char *error = 0; - format_arg arg = *s < '0' || *s > '9' ? + basic_format_arg arg = *s < '0' || *s > '9' ? this->next_arg(error) : get_arg(internal::parse_nonnegative_int(s), error); if (error) { FMT_THROW(format_error( @@ -3333,7 +3456,8 @@ inline format_arg basic_format_context::parse_arg_id() { c = *++s; } while (internal::is_name_start(c) || ('0' <= c && c <= '9')); const char *error = 0; - format_arg arg = get_arg(BasicStringRef(start, s - start), error); + basic_format_arg arg = + get_arg(BasicStringRef(start, s - start), error); if (error) FMT_THROW(format_error(error)); return arg; @@ -3341,15 +3465,13 @@ inline format_arg basic_format_context::parse_arg_id() { // Formats a single argument. template -void do_format_arg(BasicWriter &writer, const format_arg &arg, +void do_format_arg(BasicWriter &writer, const basic_format_arg &arg, Context &ctx) { const Char *&s = ctx.ptr(); FormatSpec spec; if (*s == ':') { - if (arg.type == format_arg::CUSTOM) { - arg.custom.format(&writer, arg.custom.value, &ctx); + if (visit(internal::CustomFormatter(writer, ctx), arg)) return; - } ++s; // Parse fill and alignment. if (Char c = *s) { @@ -3420,33 +3542,13 @@ void do_format_arg(BasicWriter &writer, const format_arg &arg, spec.width_ = internal::parse_nonnegative_int(s); } else if (*s == '{') { ++s; - format_arg width_arg = ctx.parse_arg_id(); + auto width_arg = ctx.parse_arg_id(); if (*s++ != '}') FMT_THROW(format_error("invalid format string")); - ULongLong value = 0; - switch (width_arg.type) { - case format_arg::INT: - if (width_arg.int_value < 0) - FMT_THROW(format_error("negative width")); - value = width_arg.int_value; - break; - case format_arg::UINT: - value = width_arg.uint_value; - break; - case format_arg::LONG_LONG: - if (width_arg.long_long_value < 0) - FMT_THROW(format_error("negative width")); - value = width_arg.long_long_value; - break; - case format_arg::ULONG_LONG: - value = width_arg.ulong_long_value; - break; - default: - FMT_THROW(format_error("width is not integer")); - } - if (value > (std::numeric_limits::max)()) + ULongLong width = visit(internal::WidthHandler(), width_arg); + if (width > (std::numeric_limits::max)()) FMT_THROW(format_error("number is too big")); - spec.width_ = static_cast(value); + spec.width_ = static_cast(width); } // Parse precision. @@ -3457,41 +3559,21 @@ void do_format_arg(BasicWriter &writer, const format_arg &arg, spec.precision_ = internal::parse_nonnegative_int(s); } else if (*s == '{') { ++s; - format_arg precision_arg = ctx.parse_arg_id(); + auto precision_arg = ctx.parse_arg_id(); if (*s++ != '}') FMT_THROW(format_error("invalid format string")); - ULongLong value = 0; - switch (precision_arg.type) { - case format_arg::INT: - if (precision_arg.int_value < 0) - FMT_THROW(format_error("negative precision")); - value = precision_arg.int_value; - break; - case format_arg::UINT: - value = precision_arg.uint_value; - break; - case format_arg::LONG_LONG: - if (precision_arg.long_long_value < 0) - FMT_THROW(format_error("negative precision")); - value = precision_arg.long_long_value; - break; - case format_arg::ULONG_LONG: - value = precision_arg.ulong_long_value; - break; - default: - FMT_THROW(format_error("precision is not integer")); - } - if (value > (std::numeric_limits::max)()) + ULongLong precision = + visit(internal::PrecisionHandler(), precision_arg); + if (precision > (std::numeric_limits::max)()) FMT_THROW(format_error("number is too big")); - spec.precision_ = static_cast(value); + spec.precision_ = static_cast(precision); } else { FMT_THROW(format_error("missing precision specifier")); } - if (arg.type <= format_arg::LAST_INTEGER_TYPE || - arg.type == format_arg::POINTER) { + if (arg.is_integral() || arg.is_pointer()) { FMT_THROW(format_error( fmt::format("precision not allowed in {} format specifier", - arg.type == format_arg::POINTER ? "pointer" : "integer"))); + arg.is_pointer() ? "pointer" : "integer"))); } } @@ -3510,7 +3592,7 @@ void do_format_arg(BasicWriter &writer, const format_arg &arg, /** Formats arguments and writes the output to the writer. */ template void vformat(BasicWriter &writer, BasicCStringRef format_str, - basic_format_args args) { + basic_format_args args) { basic_format_context ctx(format_str.c_str(), args); const Char *&s = ctx.ptr(); const Char *start = s; @@ -3536,7 +3618,7 @@ void vformat(BasicWriter &writer, BasicCStringRef format_str, template inline void BasicWriter::vwrite( BasicCStringRef format, - basic_format_args> args) { + basic_format_args, Char> args) { vformat>(*this, format, args); } } // namespace fmt diff --git a/fmt/printf.h b/fmt/printf.h index 386acf98..c6700185 100644 --- a/fmt/printf.h +++ b/fmt/printf.h @@ -40,7 +40,7 @@ struct IntChecker { static bool fits_in_int(int) { return true; } }; -class PrecisionHandler { +class PrintfPrecisionHandler { public: template typename std::enable_if::value, int>::type @@ -80,14 +80,14 @@ struct is_same { enum { value = 1 }; }; -template +template class ArgConverter { private: - format_arg &arg_; - wchar_t type_; + basic_format_arg &arg_; + Char type_; public: - ArgConverter(format_arg &arg, wchar_t type) + ArgConverter(basic_format_arg &arg, Char type) : arg_(arg), type_(type) {} void operator()(bool value) { @@ -101,27 +101,27 @@ class ArgConverter { bool is_signed = type_ == 'd' || type_ == 'i'; typedef typename internal::Conditional< is_same::value, U, T>::type TargetType; + typedef basic_format_context format_context; if (sizeof(TargetType) <= sizeof(int)) { // Extra casts are used to silence warnings. if (is_signed) { - arg_.type = format_arg::INT; - arg_.int_value = static_cast(static_cast(value)); + arg_ = internal::MakeArg( + static_cast(static_cast(value))); } else { - arg_.type = format_arg::UINT; typedef typename internal::MakeUnsigned::Type Unsigned; - arg_.uint_value = static_cast(static_cast(value)); + arg_ = internal::MakeArg( + static_cast(static_cast(value))); } } else { if (is_signed) { - arg_.type = format_arg::LONG_LONG; // glibc's printf doesn't sign extend arguments of smaller types: // std::printf("%lld", -42); // prints "4294967254" // but we don't have to do the same because it's a UB. - arg_.long_long_value = static_cast(value); + arg_ = internal::MakeArg( + static_cast(value)); } else { - arg_.type = format_arg::ULONG_LONG; - arg_.ulong_long_value = - static_cast::Type>(value); + arg_ = internal::MakeArg( + static_cast::Type>(value)); } } } @@ -137,26 +137,27 @@ class ArgConverter { // If T is void, the argument is converted to corresponding signed or unsigned // type depending on the type specifier: 'd' and 'i' - signed, other - // unsigned). -template -void convert_arg(format_arg &arg, wchar_t type) { - visit(ArgConverter(arg, type), arg); +template +void convert_arg(basic_format_arg &arg, Char type) { + visit(ArgConverter(arg, type), arg); } // Converts an integer argument to char for printf. +template class CharConverter { private: - format_arg &arg_; + basic_format_arg &arg_; FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); public: - explicit CharConverter(format_arg &arg) : arg_(arg) {} + explicit CharConverter(basic_format_arg &arg) : arg_(arg) {} template typename std::enable_if::value>::type operator()(T value) { - arg_.type = format_arg::CHAR; - arg_.int_value = static_cast(value); + arg_ = + internal::MakeArg>(static_cast(value)); } template @@ -168,14 +169,14 @@ class CharConverter { // Checks if an argument is a valid printf width specifier and sets // left alignment if it is negative. -class WidthHandler { +class PrintfWidthHandler { private: FormatSpec &spec_; - FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler); + FMT_DISALLOW_COPY_AND_ASSIGN(PrintfWidthHandler); public: - explicit WidthHandler(FormatSpec &spec) : spec_(spec) {} + explicit PrintfWidthHandler(FormatSpec &spec) : spec_(spec) {} template typename std::enable_if::value, unsigned>::type @@ -239,7 +240,7 @@ class PrintfArgFormatter : public internal::ArgFormatterBase { } /** Formats a character. */ - void operator()(wchar_t value) { + void operator()(Char value) { const FormatSpec &fmt_spec = this->spec(); BasicWriter &w = this->writer(); if (fmt_spec.type_ && fmt_spec.type_ != 'c') @@ -282,7 +283,7 @@ class PrintfArgFormatter : public internal::ArgFormatterBase { /** Formats an argument of a custom (user-defined) type. */ void operator()(format_arg::CustomValue c) { const Char format_str[] = {'}', '\0'}; - auto args = basic_format_args>(); + auto args = basic_format_args, Char>(); basic_format_context ctx(format_str, args); c.format(&this->writer(), c.value, &ctx); } @@ -305,7 +306,7 @@ class printf_context : // Returns the argument with specified index or, if arg_index is equal // to the maximum unsigned value, the next argument. - format_arg get_arg( + basic_format_arg get_arg( const Char *s, unsigned arg_index = (std::numeric_limits::max)()); @@ -321,7 +322,7 @@ class printf_context : \endrst */ explicit printf_context(BasicCStringRef format_str, - basic_format_args args) + basic_format_args args) : Base(format_str.c_str(), args) {} /** Formats stored arguments and writes the output to the writer. */ @@ -355,11 +356,12 @@ void printf_context::parse_flags(FormatSpec &spec, const Char *&s) { } template -format_arg printf_context::get_arg(const Char *s, - unsigned arg_index) { +basic_format_arg printf_context::get_arg( + const Char *s, unsigned arg_index) { (void)s; const char *error = 0; - format_arg arg = arg_index == std::numeric_limits::max() ? + basic_format_arg arg = + arg_index == std::numeric_limits::max() ? this->next_arg(error) : Base::get_arg(arg_index - 1, error); if (error) FMT_THROW(format_error(!*s ? "invalid format string" : error)); @@ -395,7 +397,7 @@ unsigned printf_context::parse_header( spec.width_ = internal::parse_nonnegative_int(s); } else if (*s == '*') { ++s; - spec.width_ = visit(internal::WidthHandler(spec), get_arg(s)); + spec.width_ = visit(internal::PrintfWidthHandler(spec), get_arg(s)); } return arg_index; } @@ -427,15 +429,15 @@ void printf_context::format(BasicWriter &writer) { spec.precision_ = static_cast(internal::parse_nonnegative_int(s)); } else if (*s == '*') { ++s; - spec.precision_ = visit(internal::PrecisionHandler(), get_arg(s)); + spec.precision_ = visit(internal::PrintfPrecisionHandler(), get_arg(s)); } } - format_arg arg = get_arg(s, arg_index); + basic_format_arg arg = get_arg(s, arg_index); if (spec.flag(HASH_FLAG) && visit(internal::IsZeroInt(), arg)) spec.flags_ &= ~internal::to_unsigned(HASH_FLAG); if (spec.fill_ == '0') { - if (arg.type <= format_arg::LAST_NUMERIC_TYPE) + if (arg.is_numeric()) spec.align_ = ALIGN_NUMERIC; else spec.fill_ = ' '; // Ignore '0' flag for non-numeric types. @@ -478,7 +480,7 @@ void printf_context::format(BasicWriter &writer) { if (!*s) FMT_THROW(format_error("invalid format string")); spec.type_ = static_cast(*s++); - if (arg.type <= format_arg::LAST_INTEGER_TYPE) { + if (arg.is_integral()) { // Normalize type. switch (spec.type_) { case 'i': case 'u': @@ -486,7 +488,7 @@ void printf_context::format(BasicWriter &writer) { break; case 'c': // TODO: handle wchar_t - visit(internal::CharConverter(arg), arg); + visit(internal::CharConverter(arg), arg); break; } } @@ -509,12 +511,13 @@ void format_value(BasicWriter &w, const T &value, template void printf(BasicWriter &w, BasicCStringRef format, - basic_format_args> args) { + basic_format_args, Char> args) { printf_context(format, args).format(w); } -inline std::string vsprintf(CStringRef format, - basic_format_args> args) { +typedef basic_format_args, char> printf_args; + +inline std::string vsprintf(CStringRef format, printf_args args) { MemoryWriter w; printf(w, format, args); return w.str(); @@ -534,8 +537,9 @@ inline std::string sprintf(CStringRef format_str, const Args & ... args) { return vsprintf(format_str, make_xformat_args>(args...)); } -inline std::wstring vsprintf(WCStringRef format, - basic_format_args> args) { +inline std::wstring vsprintf( + WCStringRef format, + basic_format_args, wchar_t> args) { WMemoryWriter w; printf(w, format, args); return w.str(); @@ -547,8 +551,7 @@ inline std::wstring sprintf(WCStringRef format_str, const Args & ... args) { return vsprintf(format_str, vargs); } -FMT_API int vfprintf(std::FILE *f, CStringRef format, - basic_format_args> args); +FMT_API int vfprintf(std::FILE *f, CStringRef format, printf_args args); /** \rst @@ -565,8 +568,7 @@ inline int fprintf(std::FILE *f, CStringRef format_str, const Args & ... args) { return vfprintf(f, format_str, vargs); } -inline int vprintf(CStringRef format, - basic_format_args> args) { +inline int vprintf(CStringRef format, printf_args args) { return vfprintf(stdout, format, args); } @@ -584,8 +586,7 @@ inline int printf(CStringRef format_str, const Args & ... args) { return vprintf(format_str, make_xformat_args>(args...)); } -inline int vfprintf(std::ostream &os, CStringRef format_str, - basic_format_args> args) { +inline int vfprintf(std::ostream &os, CStringRef format_str, printf_args args) { MemoryWriter w; printf(w, format_str, args); internal::write(os, w); diff --git a/test/custom-formatter-test.cc b/test/custom-formatter-test.cc index 7601aeb5..51bed768 100644 --- a/test/custom-formatter-test.cc +++ b/test/custom-formatter-test.cc @@ -63,7 +63,7 @@ typedef fmt::printf_context std::string custom_vsprintf( const char* format_str, - fmt::basic_format_args args) { + fmt::basic_format_args args) { fmt::MemoryWriter writer; CustomPrintfFormatter formatter(format_str, args); formatter.format(writer); diff --git a/test/format-impl-test.cc b/test/format-impl-test.cc index c63b6b97..ff702a53 100644 --- a/test/format-impl-test.cc +++ b/test/format-impl-test.cc @@ -41,13 +41,25 @@ #undef min #undef max +template +struct ValueExtractor { + T operator()(T value) { + return value; + } + + template + T operator()(U) { + throw std::runtime_error(fmt::format("invalid type {}", typeid(U).name())); + return T(); + } +}; + TEST(FormatTest, ArgConverter) { using fmt::format_arg; - format_arg arg = format_arg(); - arg.type = format_arg::LONG_LONG; - arg.long_long_value = std::numeric_limits::max(); - visit(fmt::internal::ArgConverter(arg, 'd'), arg); - EXPECT_EQ(format_arg::LONG_LONG, arg.type); + fmt::LongLong value = std::numeric_limits::max(); + format_arg arg = fmt::internal::MakeArg(value); + visit(fmt::internal::ArgConverter(arg, 'd'), arg); + EXPECT_EQ(value, visit(ValueExtractor(), arg)); } TEST(FormatTest, FormatNegativeNaN) { diff --git a/test/util-test.cc b/test/util-test.cc index 3371ef20..c39b0abf 100644 --- a/test/util-test.cc +++ b/test/util-test.cc @@ -49,13 +49,17 @@ #include "fmt/format.h" +#undef min #undef max +using fmt::basic_format_arg; using fmt::format_arg; using fmt::Buffer; using fmt::StringRef; using fmt::internal::MemoryBuffer; +using fmt::internal::Value; +using testing::_; using testing::Return; using testing::StrictMock; @@ -70,11 +74,9 @@ void format_value(fmt::BasicWriter &w, Test, } template -format_arg make_arg(const T &value) { - typedef fmt::internal::MakeValue< fmt::basic_format_context > MakeValue; - format_arg arg = MakeValue(value); - arg.type = fmt::internal::type(); - return arg; +basic_format_arg make_arg(const T &value) { + typedef fmt::internal::MakeArg< fmt::basic_format_context > MakeArg; + return MakeArg(value); } } // namespace @@ -406,175 +408,9 @@ TEST(UtilTest, Increment) { EXPECT_STREQ("200", s); } -template -struct ArgInfo; - -#define ARG_INFO(type_code, Type, field) \ - template <> \ - struct ArgInfo { \ - static Type get(const format_arg &arg) { return arg.field; } \ - } - -ARG_INFO(INT, int, int_value); -ARG_INFO(UINT, unsigned, uint_value); -ARG_INFO(LONG_LONG, fmt::LongLong, long_long_value); -ARG_INFO(ULONG_LONG, fmt::ULongLong, ulong_long_value); -ARG_INFO(BOOL, int, int_value); -ARG_INFO(CHAR, int, int_value); -ARG_INFO(DOUBLE, double, double_value); -ARG_INFO(LONG_DOUBLE, long double, long_double_value); -ARG_INFO(CSTRING, const char *, string.value); -ARG_INFO(STRING, const char *, string.value); -ARG_INFO(WSTRING, const wchar_t *, wstring.value); -ARG_INFO(POINTER, const void *, pointer); -ARG_INFO(CUSTOM, format_arg::CustomValue, custom); - -#define CHECK_ARG_INFO(Type, field, value) { \ - format_arg arg = format_arg(); \ - arg.field = value; \ - EXPECT_EQ(value, ArgInfo::get(arg)); \ -} - -TEST(ArgTest, ArgInfo) { - CHECK_ARG_INFO(INT, int_value, 42); - CHECK_ARG_INFO(UINT, uint_value, 42u); - CHECK_ARG_INFO(LONG_LONG, long_long_value, 42); - CHECK_ARG_INFO(ULONG_LONG, ulong_long_value, 42u); - CHECK_ARG_INFO(DOUBLE, double_value, 4.2); - CHECK_ARG_INFO(LONG_DOUBLE, long_double_value, 4.2); - CHECK_ARG_INFO(CHAR, int_value, 'x'); - const char STR[] = "abc"; - CHECK_ARG_INFO(CSTRING, string.value, STR); - const wchar_t WSTR[] = L"abc"; - CHECK_ARG_INFO(WSTRING, wstring.value, WSTR); - int p = 0; - CHECK_ARG_INFO(POINTER, pointer, &p); - format_arg arg = format_arg(); - arg.custom.value = &p; - EXPECT_EQ(&p, ArgInfo::get(arg).value); -} - -#define EXPECT_ARG_(Char, type_code, MakeArgType, ExpectedType, value) { \ - MakeArgType input = static_cast(value); \ - format_arg arg = make_arg(input); \ - EXPECT_EQ(format_arg::type_code, arg.type); \ - ExpectedType expected_value = static_cast(value); \ - EXPECT_EQ(expected_value, ArgInfo::get(arg)); \ -} - -#define EXPECT_ARG(type_code, Type, value) \ - EXPECT_ARG_(char, type_code, Type, Type, value) - -#define EXPECT_ARGW(type_code, Type, value) \ - EXPECT_ARG_(wchar_t, type_code, Type, Type, value) - -TEST(ArgTest, MakeArg) { - // Test bool. - EXPECT_ARG_(char, BOOL, bool, int, true); - EXPECT_ARG_(wchar_t, BOOL, bool, int, true); - - // Test char. - EXPECT_ARG(CHAR, char, 'a'); - EXPECT_ARG(CHAR, char, CHAR_MIN); - EXPECT_ARG(CHAR, char, CHAR_MAX); - - // Test wchar_t. - EXPECT_ARGW(CHAR, wchar_t, L'a'); - EXPECT_ARGW(CHAR, wchar_t, WCHAR_MIN); - EXPECT_ARGW(CHAR, wchar_t, WCHAR_MAX); - - // Test signed/unsigned char. - EXPECT_ARG(INT, signed char, 42); - EXPECT_ARG(INT, signed char, SCHAR_MIN); - EXPECT_ARG(INT, signed char, SCHAR_MAX); - EXPECT_ARG(UINT, unsigned char, 42); - EXPECT_ARG(UINT, unsigned char, UCHAR_MAX ); - - // Test short. - EXPECT_ARG(INT, short, 42); - EXPECT_ARG(INT, short, SHRT_MIN); - EXPECT_ARG(INT, short, SHRT_MAX); - EXPECT_ARG(UINT, unsigned short, 42); - EXPECT_ARG(UINT, unsigned short, USHRT_MAX); - - // Test int. - EXPECT_ARG(INT, int, 42); - EXPECT_ARG(INT, int, INT_MIN); - EXPECT_ARG(INT, int, INT_MAX); - EXPECT_ARG(UINT, unsigned, 42); - EXPECT_ARG(UINT, unsigned, UINT_MAX); - - // Test long. -#if LONG_MAX == INT_MAX -# define LONG INT -# define ULONG UINT -# define long_value int_value -# define ulong_value uint_value -#else -# define LONG LONG_LONG -# define ULONG ULONG_LONG -# define long_value long_long_value -# define ulong_value ulong_long_value -#endif - EXPECT_ARG(LONG, long, 42); - EXPECT_ARG(LONG, long, LONG_MIN); - EXPECT_ARG(LONG, long, LONG_MAX); - EXPECT_ARG(ULONG, unsigned long, 42); - EXPECT_ARG(ULONG, unsigned long, ULONG_MAX); - - // Test long long. - EXPECT_ARG(LONG_LONG, fmt::LongLong, 42); - EXPECT_ARG(LONG_LONG, fmt::LongLong, LLONG_MIN); - EXPECT_ARG(LONG_LONG, fmt::LongLong, LLONG_MAX); - EXPECT_ARG(ULONG_LONG, fmt::ULongLong, 42); - EXPECT_ARG(ULONG_LONG, fmt::ULongLong, ULLONG_MAX); - - // Test float. - EXPECT_ARG(DOUBLE, float, 4.2); - EXPECT_ARG(DOUBLE, float, FLT_MIN); - EXPECT_ARG(DOUBLE, float, FLT_MAX); - - // Test double. - EXPECT_ARG(DOUBLE, double, 4.2); - EXPECT_ARG(DOUBLE, double, DBL_MIN); - EXPECT_ARG(DOUBLE, double, DBL_MAX); - - // Test long double. - EXPECT_ARG(LONG_DOUBLE, long double, 4.2); - EXPECT_ARG(LONG_DOUBLE, long double, LDBL_MIN); - EXPECT_ARG(LONG_DOUBLE, long double, LDBL_MAX); - - // Test string. - char STR[] = "test"; - EXPECT_ARG(CSTRING, char*, STR); - EXPECT_ARG(CSTRING, const char*, STR); - EXPECT_ARG(STRING, std::string, STR); - EXPECT_ARG(STRING, fmt::StringRef, STR); - - // Test wide string. - wchar_t WSTR[] = L"test"; - EXPECT_ARGW(WSTRING, wchar_t*, WSTR); - EXPECT_ARGW(WSTRING, const wchar_t*, WSTR); - EXPECT_ARGW(WSTRING, std::wstring, WSTR); - EXPECT_ARGW(WSTRING, fmt::WStringRef, WSTR); - - int n = 42; - EXPECT_ARG(POINTER, void*, &n); - EXPECT_ARG(POINTER, const void*, &n); - - ::Test t; - format_arg arg = make_arg(t); - EXPECT_EQ(format_arg::CUSTOM, arg.type); - EXPECT_EQ(&t, arg.custom.value); - fmt::MemoryWriter w; - fmt::format_context ctx("}", fmt::format_args()); - arg.custom.format(&w, &t, &ctx); - EXPECT_EQ("test", w.str()); -} - TEST(UtilTest, FormatArgs) { fmt::format_args args; - EXPECT_EQ(format_arg::NONE, args[1].type); + EXPECT_FALSE(args[1]); } struct CustomFormatter { @@ -595,73 +431,163 @@ TEST(UtilTest, MakeValueWithCustomFormatter) { EXPECT_TRUE(ctx.called); } -struct Result { - format_arg arg; +namespace fmt { +namespace internal { - Result() : arg(make_arg(0xdeadbeef)) {} - - template - Result(const T& value) : arg(make_arg(value)) {} - Result(const wchar_t *s) : arg(make_arg(s)) {} -}; - -struct TestVisitor { - Result operator()(int value) { return value; } - Result operator()(unsigned value) { return value; } - Result operator()(fmt::LongLong value) { return value; } - Result operator()(fmt::ULongLong value) { return value; } - Result operator()(double value) { return value; } - Result operator()(long double value) { return value; } - Result operator()(wchar_t value) { return static_cast(value); } - Result operator()(const char *s) { return s; } - Result operator()(fmt::format_arg::StringValue s) { - return s.value; - } - Result operator()(fmt::format_arg::StringValue s) { - return s.value; - } - Result operator()(const void *p) { return p; } - Result operator()(fmt::format_arg::CustomValue c) { - return *static_cast(c.value); - } -}; - -#define EXPECT_RESULT_(Char, type_code, value) { \ - format_arg arg = make_arg(value); \ - Result result = fmt::visit(TestVisitor(), arg); \ - EXPECT_EQ(format_arg::type_code, result.arg.type); \ - EXPECT_EQ(value, ArgInfo::get(result.arg)); \ +bool operator==(Value::CustomValue lhs, Value::CustomValue rhs) { + return lhs.value == rhs.value; } -#define EXPECT_RESULT(type_code, value) \ - EXPECT_RESULT_(char, type_code, value) -#define EXPECT_RESULTW(type_code, value) \ - EXPECT_RESULT_(wchar_t, type_code, value) +template +bool operator==(Value::StringValue lhs, Value::StringValue rhs) { + return std::basic_string(lhs.value, lhs.size) == + std::basic_string(rhs.value, rhs.size); +} +} +} -TEST(ArgVisitorTest, VisitAll) { - EXPECT_RESULT(INT, 42); - EXPECT_RESULT(UINT, 42u); - EXPECT_RESULT(LONG_LONG, 42ll); - EXPECT_RESULT(ULONG_LONG, 42ull); - EXPECT_RESULT(DOUBLE, 4.2); - EXPECT_RESULT(LONG_DOUBLE, 4.2l); - EXPECT_RESULT(CHAR, 'x'); - const char STR[] = "abc"; - EXPECT_RESULT(CSTRING, STR); - const wchar_t WSTR[] = L"abc"; - EXPECT_RESULTW(WSTRING, WSTR); - const void *p = STR; - EXPECT_RESULT(POINTER, p); - ::Test t; - Result result = visit(TestVisitor(), make_arg(t)); - EXPECT_EQ(format_arg::CUSTOM, result.arg.type); - EXPECT_EQ(&t, result.arg.custom.value); +template +struct MockVisitor { + // Use a unique result type to make sure that there are no undesirable + // conversions. + struct Result {}; + + MockVisitor() { + ON_CALL(*this, visit(_)).WillByDefault(Return(Result())); + } + + MOCK_METHOD1_T(visit, Result (T value)); + MOCK_METHOD0_T(unexpected, void ()); + + Result operator()(T value) { return visit(value); } + + template + Result operator()(U value) { + unexpected(); + return Result(); + } +}; + +template +struct VisitType { typedef T Type; }; + +#define VISIT_TYPE(Type_, VisitType_) \ + template <> \ + struct VisitType { typedef VisitType_ Type; } + +VISIT_TYPE(signed char, int); +VISIT_TYPE(unsigned char, unsigned); +VISIT_TYPE(short, int); +VISIT_TYPE(unsigned short, unsigned); + +#if LONG_MAX == INT_MAX +VISIT_TYPE(long, int); +VISIT_TYPE(unsigned long, unsigned); +#else +VISIT_TYPE(long, fmt::LongLong); +VISIT_TYPE(unsigned long, fmt::ULongLong); +#endif + +VISIT_TYPE(float, double); + +#define CHECK_ARG_(Char, expected, value) { \ + testing::StrictMock> visitor; \ + EXPECT_CALL(visitor, visit(expected)); \ + fmt::visit(visitor, make_arg(value)); \ +} + +#define CHECK_ARG(value) { \ + typename VisitType::Type expected = value; \ + CHECK_ARG_(char, expected, value) \ + CHECK_ARG_(wchar_t, expected, value) \ +} + +template +class NumericArgTest : public testing::Test {}; + +typedef ::testing::Types< + bool, signed char, unsigned char, signed, unsigned short, + int, unsigned, long, unsigned long, fmt::LongLong, fmt::ULongLong, + float, double, long double> Types; +TYPED_TEST_CASE(NumericArgTest, Types); + +template +typename std::enable_if::value, T>::type test_value() { + return static_cast(42); +} + +template +typename std::enable_if::value, T>::type + test_value() { + return static_cast(4.2); +} + +TYPED_TEST(NumericArgTest, MakeAndVisit) { + CHECK_ARG(test_value()); + CHECK_ARG(std::numeric_limits::min()); + CHECK_ARG(std::numeric_limits::max()); +} + +TEST(UtilTest, CharArg) { + CHECK_ARG_(char, 'a', 'a'); + CHECK_ARG_(wchar_t, L'a', 'a'); + CHECK_ARG_(wchar_t, L'a', L'a'); +} + +TEST(UtilTest, StringArg) { + char str_data[] = "test"; + char *str = str_data; + const char *cstr = str; + CHECK_ARG_(char, cstr, str); + CHECK_ARG_(wchar_t, cstr, str); + CHECK_ARG(cstr); + + Value::StringValue strval = {str, 4}; + CHECK_ARG_(char, strval, std::string(str)); + CHECK_ARG_(wchar_t, strval, std::string(str)); + CHECK_ARG_(char, strval, fmt::StringRef(str)); + CHECK_ARG_(wchar_t, strval, fmt::StringRef(str)); +} + +TEST(UtilTest, WStringArg) { + wchar_t str_data[] = L"test"; + wchar_t *str = str_data; + const wchar_t *cstr = str; + + Value::StringValue strval = {str, 4}; + CHECK_ARG_(wchar_t, strval, str); + CHECK_ARG_(wchar_t, strval, cstr); + CHECK_ARG_(wchar_t, strval, std::wstring(str)); + CHECK_ARG_(wchar_t, strval, fmt::WStringRef(str)); +} + +TEST(UtilTest, PointerArg) { + void *p = 0; + const void *cp = 0; + CHECK_ARG_(char, cp, p); + CHECK_ARG_(wchar_t, cp, p); + CHECK_ARG(cp); +} + +TEST(UtilTest, CustomArg) { + ::Test test; + typedef MockVisitor Visitor; + testing::StrictMock visitor; + EXPECT_CALL(visitor, visit(_)).WillOnce( + testing::Invoke([&](Value::CustomValue custom) { + EXPECT_EQ(&test, custom.value); + fmt::MemoryWriter w; + fmt::format_context ctx("}", fmt::format_args()); + custom.format(&w, &test, &ctx); + EXPECT_EQ("test", w.str()); + return Visitor::Result(); + })); + fmt::visit(visitor, make_arg(test)); } TEST(ArgVisitorTest, VisitInvalidArg) { format_arg arg = format_arg(); - arg.type = static_cast(format_arg::NONE); - EXPECT_ASSERT(visit(TestVisitor(), arg), "invalid argument type"); + EXPECT_ASSERT(visit(MockVisitor(), arg), "invalid argument type"); } // Tests fmt::internal::count_digits for integer type Int.