diff --git a/fmt/format.h b/fmt/format.h index c2244390..3d573282 100644 --- a/fmt/format.h +++ b/fmt/format.h @@ -1043,20 +1043,6 @@ FMT_API void format_windows_error(fmt::buffer &out, int error_code, template struct null {}; -// A helper class template to enable or disable overloads taking wide -// characters and strings in value's constructor. -template -struct wchar_helper { - typedef null supported; - typedef T unsupported; -}; - -template -struct wchar_helper { - typedef T supported; - typedef null unsupported; -}; - typedef char yes[1]; typedef char no[2]; @@ -1192,10 +1178,26 @@ template <> constexpr type get_type() { return POINTER; } template <> constexpr type get_type() { return POINTER; } template <> constexpr type get_type() { return POINTER; } +// Formatting of wide characters and strings into a narrow output is disallowed: +// fmt::format("{}", L"test"); // error +// To fix this, use a wide format string: +// fmt::format(L"{}", L"test"); +template +inline void require_wchar() { + static_assert( + std::is_same::value, + "formatting of wide characters into a narrow output is disallowed"); +} + +template +inline const T *as_const(T *p) { return p; } + // A formatting argument value. template class value { public: + using char_type = typename Context::char_type; + union { int int_value; unsigned uint_value; @@ -1207,38 +1209,122 @@ class value { string_value string; string_value sstring; string_value ustring; - string_value tstring; - custom_value custom; + string_value tstring; + custom_value custom; }; - using char_type = typename Context::char_type; + value() {} + value(bool val) { set(int_value, val); } + value(short val) { set(int_value, val); } + value(unsigned short val) { set(uint_value, val); } + value(int val) { set(int_value, val); } + value(unsigned val) { set(uint_value, val); } + + value(long val) { + // 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(val) == sizeof(int))) + int_value = static_cast(val); + else + long_long_value = val; + } + + value(unsigned long val) { + if (const_check(sizeof(val) == sizeof(unsigned))) + uint_value = static_cast(val); + else + ulong_long_value = val; + } + + value(long long val) { set(long_long_value, val); } + value(unsigned long long val) { set(ulong_long_value, val); } + value(float val) { set(double_value, val); } + value(double val) { set(double_value, val); } + value(long double val) { set(long_double_value, val); } + value(signed char val) { set(int_value, val); } + value(unsigned char val) { set(uint_value, val); } + value(char val) { set(int_value, val); } + +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) + value(wchar_t value) { + require_wchar(); + set(int_value, value); + } +#endif + + value(char *s) { set(string.value, s); } + value(const char *s) { set(string.value, s); } + value(signed char *s) { set(sstring.value, s); } + value(const signed char *s) { set(sstring.value, s); } + value(unsigned char *s) { set(ustring.value, s); } + value(const unsigned char *s) { set(ustring.value, s); } + value(string_view s) { set_string(string, s); } + value(const std::string &s) { set_string(string, s); } + +#define FMT_MAKE_WSTR_VALUE(Type, TYPE) \ + value(Type value) { \ + require_wchar(); \ + static_assert(get_type() == TYPE, "invalid type"); \ + set_string(value); \ + } + + FMT_MAKE_WSTR_VALUE(wchar_t *, TSTRING) + FMT_MAKE_WSTR_VALUE(const wchar_t *, TSTRING) + FMT_MAKE_WSTR_VALUE(wstring_view, TSTRING) + FMT_MAKE_WSTR_VALUE(const std::wstring &, TSTRING) + + // Formatting of arbitrary pointers is disallowed. If you want to output a + // pointer cast it to "void *" or "const void *". In particular, this forbids + // formatting of "[const] volatile char *" which is printed as bool by + // iostreams. + template + value(const T *p) { + static_assert(std::is_same::value, + "formatting of non-void pointers is disallowed"); + set(pointer, p); + } + + template + value(T *p) : value(as_const(p)) {} + + value(std::nullptr_t) { pointer = nullptr; } + + template + value(const T &value, + typename std::enable_if::value, int>::type = 0) { + static_assert(get_type() == INT, "invalid type"); + int_value = value; + } + + template + value(const T &value, + typename std::enable_if::value, int>::type = 0) { + static_assert(get_type() == CUSTOM, "invalid type"); + custom.value = &value; + custom.format = &format_custom_arg; + } + + // Additional template param `Char` is needed here because get_type always + // uses char. + template + value(const named_arg &value) { + static_assert( + get_type &>() == NAMED_ARG, "invalid type"); + pointer = &value; + } private: - // The following two methods are private to disallow formatting of - // arbitrary pointers. If you want to output a pointer cast it to - // "void *" or "const void *". In particular, this forbids formatting - // of "[const] volatile char *" which is printed as bool by iostreams. - // Do not implement! - template - value(const T *value); - template - value(T *value); + template + void set(T &field, const U &value) { + static_assert(get_type() == TYPE, "invalid type"); + field = value; + } - // The following methods are private to disallow formatting of wide - // characters and strings into narrow strings as in - // fmt::format("{}", L"test"); - // To fix this, use a wide format string: fmt::format(L"{}", L"test"). -#if !FMT_MSC_VER || defined(_NATIVE_WCHAR_T_DEFINED) - value(typename wchar_helper::unsupported); -#endif - value(typename wchar_helper::unsupported); - value(typename wchar_helper::unsupported); - value(typename wchar_helper::unsupported); - value(typename wchar_helper::unsupported); - - void set_string(string_view str) { - this->string.value = str.data(); - this->string.size = str.size(); + template + void set_string(T &field, const U &value) { + static_assert(get_type() == TYPE, "invalid type"); + field.value = value.data(); + field.size = value.size(); } void set_string(wstring_view str) { @@ -1260,111 +1346,6 @@ class value { format.remove_prefix(it - begin(format)); f.format(buffer, *static_cast(arg), ctx); } - - public: - value() {} - -#define FMT_MAKE_VALUE_(Type, field, TYPE, rhs) \ - value(Type value) { \ - static_assert(get_type() == internal::TYPE, "invalid type"); \ - this->field = rhs; \ - } - -#define FMT_MAKE_VALUE(Type, field, TYPE) \ - FMT_MAKE_VALUE_(Type, field, TYPE, value) - - FMT_MAKE_VALUE(bool, int_value, BOOL) - FMT_MAKE_VALUE(short, int_value, INT) - FMT_MAKE_VALUE(unsigned short, uint_value, UINT) - FMT_MAKE_VALUE(int, int_value, INT) - FMT_MAKE_VALUE(unsigned, uint_value, UINT) - - value(long value) { - // 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))) - this->int_value = static_cast(value); - else - this->long_long_value = value; - } - - value(unsigned long value) { - if (const_check(sizeof(unsigned long) == sizeof(unsigned))) - this->uint_value = static_cast(value); - else - this->ulong_long_value = value; - } - - FMT_MAKE_VALUE(long long, long_long_value, LONG_LONG) - FMT_MAKE_VALUE(unsigned long long, ulong_long_value, ULONG_LONG) - FMT_MAKE_VALUE(float, double_value, DOUBLE) - FMT_MAKE_VALUE(double, double_value, DOUBLE) - FMT_MAKE_VALUE(long double, long_double_value, LONG_DOUBLE) - FMT_MAKE_VALUE(signed char, int_value, INT) - FMT_MAKE_VALUE(unsigned char, uint_value, UINT) - FMT_MAKE_VALUE(char, int_value, CHAR) - -#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) - typedef typename wchar_helper::supported WChar; - value(WChar value) { - static_assert(get_type() == CHAR, "invalid type"); - this->int_value = value; - } -#endif - -#define FMT_MAKE_STR_VALUE(Type, TYPE) \ - value(Type value) { \ - static_assert(get_type() == TYPE, "invalid type"); \ - set_string(value); \ - } - - FMT_MAKE_VALUE(char *, string.value, CSTRING) - FMT_MAKE_VALUE(const char *, string.value, CSTRING) - FMT_MAKE_VALUE(signed char *, sstring.value, CSTRING) - FMT_MAKE_VALUE(const signed char *, sstring.value, CSTRING) - FMT_MAKE_VALUE(unsigned char *, ustring.value, CSTRING) - FMT_MAKE_VALUE(const unsigned char *, ustring.value, CSTRING) - FMT_MAKE_STR_VALUE(const std::string &, STRING) - FMT_MAKE_STR_VALUE(string_view, STRING) - -#define FMT_MAKE_WSTR_VALUE(Type, TYPE) \ - value(typename wchar_helper::supported value) { \ - static_assert(get_type() == TYPE, "invalid type"); \ - set_string(value); \ - } - - FMT_MAKE_WSTR_VALUE(wchar_t *, TSTRING) - FMT_MAKE_WSTR_VALUE(const wchar_t *, TSTRING) - FMT_MAKE_WSTR_VALUE(const std::wstring &, TSTRING) - FMT_MAKE_WSTR_VALUE(wstring_view, TSTRING) - - FMT_MAKE_VALUE(void *, pointer, POINTER) - FMT_MAKE_VALUE(const void *, pointer, POINTER) - value(std::nullptr_t) { pointer = nullptr; } - - template - value(const T &value, - typename std::enable_if::value, int>::type = 0) { - static_assert(get_type() == CUSTOM, "invalid type"); - this->custom.value = &value; - this->custom.format = &format_custom_arg; - } - - template - value(const T &value, - typename std::enable_if::value, int>::type = 0) { - static_assert(get_type() == INT, "invalid type"); - this->int_value = value; - } - - // Additional template param `Char_` is needed here because make_type always - // uses char. - template - value(const named_arg &value) { - static_assert( - get_type &>() == NAMED_ARG, "invalid type"); - this->pointer = &value; - } }; template @@ -2224,14 +2205,6 @@ class basic_writer { template void write_str(basic_string_view str, const format_specs &spec); - // This following methods are private to disallow writing wide characters - // and strings to a char buffer. If you want to print a wide string as a - // pointer as std::ostream does, cast it to const void*. - // Do not implement! - void operator<<(typename internal::wchar_helper::unsupported); - void operator<<( - typename internal::wchar_helper::unsupported); - // Appends floating-point length specifier to the format string. // The second argument is only used for overload resolution. void append_float_length(Char *&format_ptr, long double) { @@ -2330,7 +2303,8 @@ class basic_writer { buffer_.push_back(value); } - void write(typename internal::wchar_helper::supported value) { + void write(wchar_t value) { + internal::require_wchar(); buffer_.push_back(value); } @@ -2339,14 +2313,14 @@ class basic_writer { Writes *value* to the buffer. \endrst */ - void write(basic_string_view value) { - const Char *str = value.data(); + void write(string_view value) { + const char *str = value.data(); buffer_.append(str, str + value.size()); } - void write( - typename internal::wchar_helper::supported value) { - const char *str = value.data(); + void write(basic_string_view value) { + internal::require_wchar(); + const wchar_t *str = value.data(); buffer_.append(str, str + value.size()); }