diff --git a/fmt/format.h b/fmt/format.h index 6a58f338..91c4cee9 100644 --- a/fmt/format.h +++ b/fmt/format.h @@ -989,146 +989,27 @@ FMT_API void format_windows_error(fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT; #endif -enum Type { - NONE, NAMED_ARG, - // Integer types should go first, - INT, UINT, LONG_LONG, ULONG_LONG, BOOL, CHAR, LAST_INTEGER_TYPE = CHAR, - // followed by floating-point types. - DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE, - CSTRING, STRING, TSTRING, POINTER, CUSTOM -}; +template +struct EnableIf {}; -template -struct StringValue { - const Char *value; - std::size_t size; -}; +template +struct EnableIf { typedef T type; }; -typedef void (*FormatFunc)(void *writer, const void *arg, void *ctx); +template +struct Conditional { typedef T type; }; -struct CustomValue { - const void *value; - FormatFunc format; -}; +template +struct Conditional { typedef F type; }; -// A formatting argument value. -template -struct Value { - union { - int int_value; - unsigned uint_value; - LongLong long_long_value; - ULongLong ulong_long_value; - double double_value; - long double long_double_value; - const void *pointer; - StringValue string; - StringValue sstring; - StringValue ustring; - StringValue tstring; - CustomValue custom; - }; -}; +// For bcc32 which doesn't understand ! in template arguments. +template +struct Not { enum { value = 0 }; }; -template -class ArgMap; -} // namespace internal +template <> +struct Not { enum { value = 1 }; }; -template -class basic_format_args; - -// A formatting argument. It is a trivially copyable/constructible type to -// allow storage in internal::MemoryBuffer. -template -class basic_format_arg : public internal::Value { - protected: - internal::Type type_; - - 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_ > internal::NAMED_ARG, "invalid argument type"); - } - - public: - explicit operator bool() const noexcept { return type_ != internal::NONE; } - - bool is_integral() const { - check_type(); - return type_ <= internal::LAST_INTEGER_TYPE; - } - - bool is_numeric() const { - check_type(); - return type_ <= internal::LAST_NUMERIC_TYPE; - } - - bool is_pointer() const { - check_type(); - return type_ == internal::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 internal::NONE: - case internal::NAMED_ARG: - FMT_ASSERT(false, "invalid argument type"); - break; - case internal::INT: - return vis(arg.int_value); - case internal::UINT: - return vis(arg.uint_value); - case internal::LONG_LONG: - return vis(arg.long_long_value); - case internal::ULONG_LONG: - return vis(arg.ulong_long_value); - case internal::BOOL: - return vis(arg.int_value != 0); - case internal::CHAR: - return vis(static_cast(arg.int_value)); - case internal::DOUBLE: - return vis(arg.double_value); - case internal::LONG_DOUBLE: - return vis(arg.long_double_value); - case internal::CSTRING: - return vis(arg.string.value); - case internal::STRING: - return vis(StringRef(arg.string.value, arg.string.size)); - case internal::TSTRING: - return vis(BasicStringRef(arg.tstring.value, arg.tstring.size)); - case internal::POINTER: - return vis(arg.pointer); - case internal::CUSTOM: - return vis(arg.custom); - } - return typename std::result_of::type(); -} - -namespace internal { - -template -struct NamedArg; +template +struct False { enum { value = 0 }; }; template struct Null {}; @@ -1190,71 +1071,49 @@ FMT_DISABLE_CONVERSION_TO_INT(float); FMT_DISABLE_CONVERSION_TO_INT(double); FMT_DISABLE_CONVERSION_TO_INT(long double); -template -struct EnableIf {}; - -template -struct EnableIf { typedef T type; }; - -template -struct Conditional { typedef T type; }; - -template -struct Conditional { typedef F type; }; - -// For bcc32 which doesn't understand ! in template arguments. -template -struct Not { enum { value = 0 }; }; - -template <> -struct Not { enum { value = 1 }; }; - -template -struct False { enum { value = 0 }; }; - -template struct LConvCheck { - LConvCheck(int) {} +enum Type { + NONE, NAMED_ARG, + // Integer types should go first, + INT, UINT, LONG_LONG, ULONG_LONG, BOOL, CHAR, LAST_INTEGER_TYPE = CHAR, + // followed by floating-point types. + DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE, + CSTRING, STRING, TSTRING, POINTER, CUSTOM }; -// Returns the thousands separator for the current locale. -// We check if ``lconv`` contains ``thousands_sep`` because on Android -// ``lconv`` is stubbed as an empty struct. -template -inline StringRef thousands_sep( - LConv *lc, LConvCheck = 0) { - return lc->thousands_sep; -} +template +struct StringValue { + const Char *value; + std::size_t size; +}; -inline fmt::StringRef thousands_sep(...) { return ""; } +typedef void (*FormatFunc)(void *writer, const void *arg, void *ctx); -#define FMT_CONCAT(a, b) a##b +struct CustomValue { + const void *value; + FormatFunc format; +}; -#if FMT_GCC_VERSION >= 407 -# define FMT_UNUSED __attribute__((unused)) -#else -# define FMT_UNUSED -#endif +// A formatting argument value. +template +struct Value { + union { + int int_value; + unsigned uint_value; + LongLong long_long_value; + ULongLong ulong_long_value; + double double_value; + long double long_double_value; + const void *pointer; + StringValue string; + StringValue sstring; + StringValue ustring; + StringValue tstring; + CustomValue custom; + }; +}; -#ifndef FMT_USE_STATIC_ASSERT -# define FMT_USE_STATIC_ASSERT 0 -#endif - -#if FMT_USE_STATIC_ASSERT || FMT_HAS_FEATURE(cxx_static_assert) || \ - (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600 -# define FMT_STATIC_ASSERT(cond, message) static_assert(cond, message) -#else -# define FMT_CONCAT_(a, b) FMT_CONCAT(a, b) -# define FMT_STATIC_ASSERT(cond, message) \ - typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED -#endif - -template -void format_value(BasicWriter &, const T &, Formatter &, const Char *) { - FMT_STATIC_ASSERT(False::value, - "Cannot format argument. To enable the use of ostream " - "operator<< include fmt/ostream.h. Otherwise provide " - "an overload of format_arg."); -} +template +struct NamedArg; template struct IsNamedArg : std::false_type {}; @@ -1264,81 +1123,57 @@ struct IsNamedArg< NamedArg > : std::true_type {}; template constexpr Type gettype() { - typedef format_arg Arg; return IsNamedArg::value ? - internal::NAMED_ARG : - (ConvertToInt::value ? internal::INT : internal::CUSTOM); + NAMED_ARG : (ConvertToInt::value ? INT : CUSTOM); } -template <> constexpr Type gettype() { return internal::BOOL; } -template <> constexpr Type gettype() { return internal::INT; } -template <> constexpr Type gettype() { - return internal::UINT; -} -template <> constexpr Type gettype() { return internal::INT; } -template <> constexpr Type gettype() { return internal::UINT; } +template <> constexpr Type gettype() { return BOOL; } +template <> constexpr Type gettype() { return INT; } +template <> constexpr Type gettype() { return UINT; } +template <> constexpr Type gettype() { return INT; } +template <> constexpr Type gettype() { return UINT; } template <> constexpr Type gettype() { - return sizeof(long) == sizeof(int) ? internal::INT : internal::LONG_LONG; + return sizeof(long) == sizeof(int) ? INT : LONG_LONG; } template <> constexpr Type gettype() { return sizeof(unsigned long) == sizeof(unsigned) ? - internal::UINT : internal::ULONG_LONG; + UINT : ULONG_LONG; } -template <> constexpr Type gettype() { return internal::LONG_LONG; } -template <> constexpr Type gettype() { - return internal::ULONG_LONG; -} -template <> constexpr Type gettype() { return internal::DOUBLE; } -template <> constexpr Type gettype() { return internal::DOUBLE; } -template <> constexpr Type gettype() { - return internal::LONG_DOUBLE; -} -template <> constexpr Type gettype() { return internal::INT; } -template <> constexpr Type gettype() { return internal::UINT; } -template <> constexpr Type gettype() { return internal::CHAR; } +template <> constexpr Type gettype() { return LONG_LONG; } +template <> constexpr Type gettype() { return ULONG_LONG; } +template <> constexpr Type gettype() { return DOUBLE; } +template <> constexpr Type gettype() { return DOUBLE; } +template <> constexpr Type gettype() { return LONG_DOUBLE; } +template <> constexpr Type gettype() { return INT; } +template <> constexpr Type gettype() { return UINT; } +template <> constexpr Type gettype() { return CHAR; } #if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) -template <> constexpr Type gettype() { return internal::CHAR; } +template <> constexpr Type gettype() { return CHAR; } #endif -template <> constexpr Type gettype() { return internal::CSTRING; } -template <> constexpr Type gettype() { - return internal::CSTRING; -} -template <> constexpr Type gettype() { - return internal::CSTRING; -} -template <> constexpr Type gettype() { - return internal::CSTRING; -} -template <> constexpr Type gettype() { - return internal::CSTRING; -} -template <> constexpr Type gettype() { - return internal::CSTRING; -} -template <> constexpr Type gettype() { return internal::STRING; } -template <> constexpr Type gettype() { return internal::STRING; } -template <> constexpr Type gettype() { return internal::CSTRING; } -template <> constexpr Type gettype() { return internal::TSTRING; } -template <> constexpr Type gettype() { - return internal::TSTRING; -} -template <> constexpr Type gettype() { - return internal::TSTRING; -} -template <> constexpr Type gettype() { return internal::TSTRING; } -template <> constexpr Type gettype() { return internal::POINTER; } -template <> constexpr Type gettype() { - return internal::POINTER; -} +template <> constexpr Type gettype() { return CSTRING; } +template <> constexpr Type gettype() { return CSTRING; } +template <> constexpr Type gettype() { return CSTRING; } +template <> constexpr Type gettype() { return CSTRING; } +template <> constexpr Type gettype() { return CSTRING; } +template <> constexpr Type gettype() { return CSTRING; } +template <> constexpr Type gettype() { return STRING; } +template <> constexpr Type gettype() { return STRING; } +template <> constexpr Type gettype() { return CSTRING; } +template <> constexpr Type gettype() { return TSTRING; } +template <> constexpr Type gettype() { return TSTRING; } +template <> constexpr Type gettype() { return TSTRING; } +template <> constexpr Type gettype() { return TSTRING; } +template <> constexpr Type gettype() { return POINTER; } +template <> constexpr Type gettype() { return POINTER; } template constexpr Type type() { return gettype::type>(); } // Makes a format_arg object from any type. template -class MakeValue : public basic_format_arg { +class MakeValue : public Value { public: typedef typename Context::char_type Char; @@ -1492,20 +1327,165 @@ class MakeValue : public basic_format_arg { } }; +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. +template +class basic_format_arg { + protected: + internal::Value value_; + internal::Type type_; + + 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_ > internal::NAMED_ARG, "invalid argument type"); + } + + public: + basic_format_arg() : type_(internal::NONE) {} + + explicit operator bool() const noexcept { return type_ != internal::NONE; } + + bool is_integral() const { + check_type(); + return type_ <= internal::LAST_INTEGER_TYPE; + } + + bool is_numeric() const { + check_type(); + return type_ <= internal::LAST_NUMERIC_TYPE; + } + + bool is_pointer() const { + check_type(); + return type_ == internal::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 internal::NONE: + case internal::NAMED_ARG: + FMT_ASSERT(false, "invalid argument type"); + break; + case internal::INT: + return vis(arg.value_.int_value); + case internal::UINT: + return vis(arg.value_.uint_value); + case internal::LONG_LONG: + return vis(arg.value_.long_long_value); + case internal::ULONG_LONG: + return vis(arg.value_.ulong_long_value); + case internal::BOOL: + return vis(arg.value_.int_value != 0); + case internal::CHAR: + return vis(static_cast(arg.value_.int_value)); + case internal::DOUBLE: + return vis(arg.value_.double_value); + case internal::LONG_DOUBLE: + return vis(arg.value_.long_double_value); + case internal::CSTRING: + return vis(arg.value_.string.value); + case internal::STRING: + return vis(StringRef(arg.value_.string.value, arg.value_.string.size)); + case internal::TSTRING: + return vis(BasicStringRef( + arg.value_.tstring.value, arg.value_.tstring.size)); + case internal::POINTER: + return vis(arg.value_.pointer); + case internal::CUSTOM: + return vis(arg.value_.custom); + } + return typename std::result_of::type(); +} + +namespace internal { + template class MakeArg : public basic_format_arg { -public: + public: MakeArg() { this->type_ = internal::NONE; } template - MakeArg(const T &value) - : basic_format_arg(MakeValue(value)) { + MakeArg(const T &value) { + this->value_ = internal::MakeValue(value); this->type_ = internal::type(); } }; +template struct LConvCheck { + LConvCheck(int) {} +}; + +// Returns the thousands separator for the current locale. +// We check if ``lconv`` contains ``thousands_sep`` because on Android +// ``lconv`` is stubbed as an empty struct. +template +inline StringRef thousands_sep( + LConv *lc, LConvCheck = 0) { + return lc->thousands_sep; +} + +inline fmt::StringRef thousands_sep(...) { return ""; } + +#define FMT_CONCAT(a, b) a##b + +#if FMT_GCC_VERSION >= 407 +# define FMT_UNUSED __attribute__((unused)) +#else +# define FMT_UNUSED +#endif + +#ifndef FMT_USE_STATIC_ASSERT +# define FMT_USE_STATIC_ASSERT 0 +#endif + +#if FMT_USE_STATIC_ASSERT || FMT_HAS_FEATURE(cxx_static_assert) || \ + (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600 +# define FMT_STATIC_ASSERT(cond, message) static_assert(cond, message) +#else +# define FMT_CONCAT_(a, b) FMT_CONCAT(a, b) +# define FMT_STATIC_ASSERT(cond, message) \ + typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED +#endif + +template +void format_value(BasicWriter &, const T &, Formatter &, const Char *) { + FMT_STATIC_ASSERT(False::value, + "Cannot format argument. To enable the use of ostream " + "operator<< include fmt/ostream.h. Otherwise provide " + "an overload of format_arg."); +} + template struct NamedArg : basic_format_arg { BasicStringRef name; @@ -1532,27 +1512,39 @@ constexpr uint64_t make_type() { return 0; } // Maximum number of arguments with packed types. enum { MAX_PACKED_ARGS = 16 }; + +template +inline typename std::enable_if>::type + make_arg(const T& value) { + return MakeValue(value); +} + +template +inline typename std::enable_if::type + make_arg(const T& value) { + return MakeArg(value); +} } // namespace internal template class format_arg_store { private: static const size_t NUM_ARGS = sizeof...(Args); - static const bool IS_PACKED = NUM_ARGS < internal::MAX_PACKED_ARGS; + static const bool PACKED = NUM_ARGS < internal::MAX_PACKED_ARGS; typedef typename Context::char_type char_type; - typedef typename std::conditional, basic_format_arg>::type value_type; // If the arguments are not packed, add one more element to mark the end. - std::array data_; + std::array data_; public: static const uint64_t TYPES = internal::make_type(); format_arg_store(const Args &... args) - : data_{{internal::MakeArg(args)...}} {} + : data_{{internal::make_arg(args)...}} {} const value_type *data() const { return data_.data(); } }; @@ -1607,9 +1599,9 @@ class basic_format_args { bool use_values = type(internal::MAX_PACKED_ARGS - 1) == internal::NONE; if (index < internal::MAX_PACKED_ARGS) { typename internal::Type arg_type = type(index); - internal::Value &val = arg; + internal::Value &val = arg.value_; if (arg_type != internal::NONE) - val = use_values ? values_[index] : args_[index]; + val = use_values ? values_[index] : args_[index].value_; arg.type_ = arg_type; return arg; } @@ -1639,7 +1631,7 @@ class basic_format_args { format_arg operator[](size_type index) const { format_arg arg = get(index); return arg.type_ == internal::NAMED_ARG ? - *static_cast(arg.pointer) : arg; + *static_cast(arg.value_.pointer) : arg; } }; @@ -1922,7 +1914,7 @@ void ArgMap::init(const basic_format_args &args) { for (unsigned i = 0; i != MAX_PACKED_ARGS; ++i) { internal::Type arg_type = args.type(i); if (arg_type == internal::NAMED_ARG) { - named_arg = static_cast(args.args_[i].pointer); + named_arg = static_cast(args.args_[i].value_.pointer); map_.push_back(Pair(named_arg->name, *named_arg)); } } @@ -1931,7 +1923,7 @@ void ArgMap::init(const basic_format_args &args) { case internal::NONE: return; case internal::NAMED_ARG: - named_arg = static_cast(args.args_[i].pointer); + named_arg = static_cast(args.args_[i].value_.pointer); map_.push_back(Pair(named_arg->name, *named_arg)); break; default: @@ -3497,7 +3489,7 @@ void do_format_arg(BasicWriter &writer, const basic_format_arg &arg, spec.fill_ = c; } else ++s; if (spec.align_ == ALIGN_NUMERIC) - require_numeric_argument(arg, '='); + internal::require_numeric_argument(arg, '='); break; } } while (--p >= s); @@ -3506,28 +3498,28 @@ void do_format_arg(BasicWriter &writer, const basic_format_arg &arg, // Parse sign. switch (*s) { case '+': - check_sign(s, arg); + internal::check_sign(s, arg); spec.flags_ |= SIGN_FLAG | PLUS_FLAG; break; case '-': - check_sign(s, arg); + internal::check_sign(s, arg); spec.flags_ |= MINUS_FLAG; break; case ' ': - check_sign(s, arg); + internal::check_sign(s, arg); spec.flags_ |= SIGN_FLAG; break; } if (*s == '#') { - require_numeric_argument(arg, '#'); + internal::require_numeric_argument(arg, '#'); spec.flags_ |= HASH_FLAG; ++s; } // Parse zero flag. if (*s == '0') { - require_numeric_argument(arg, '0'); + internal::require_numeric_argument(arg, '0'); spec.align_ = ALIGN_NUMERIC; spec.fill_ = '0'; ++s; diff --git a/test/util-test.cc b/test/util-test.cc index ec98ef5a..b20fe54f 100644 --- a/test/util-test.cc +++ b/test/util-test.cc @@ -424,7 +424,7 @@ void format_value(fmt::Writer &, const Test &, CustomFormatter &ctx) { TEST(UtilTest, MakeValueWithCustomFormatter) { ::Test t; - format_arg arg = fmt::internal::MakeValue(t); + fmt::internal::Value arg = fmt::internal::MakeValue(t); CustomFormatter ctx = {false}; fmt::MemoryWriter w; arg.custom.format(&w, &t, &ctx);