Add wchar.h for wide char overloads

This commit is contained in:
Victor Zverovich 2021-05-17 20:47:38 -07:00
parent ce14eafc24
commit 0dd91e20d5
8 changed files with 105 additions and 47 deletions

View File

@ -187,7 +187,8 @@ endfunction()
# Define the fmt library, its includes and the needed defines. # Define the fmt library, its includes and the needed defines.
add_headers(FMT_HEADERS args.h chrono.h color.h compile.h core.h format.h add_headers(FMT_HEADERS args.h chrono.h color.h compile.h core.h format.h
format-inl.h locale.h os.h ostream.h printf.h ranges.h) format-inl.h locale.h os.h ostream.h printf.h ranges.h
wchar.h)
if (FMT_OS) if (FMT_OS)
set(FMT_SOURCES src/format.cc src/os.cc) set(FMT_SOURCES src/format.cc src/os.cc)
else() else()

View File

@ -392,8 +392,8 @@ FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char micro[] = "\u00B5"; FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char micro[] = "\u00B5";
template <typename Char> constexpr bool is_unicode() { constexpr bool is_utf8() {
return FMT_UNICODE || sizeof(Char) != 1 || return FMT_UNICODE ||
(sizeof(micro) == 3 && micro[0] == 0xC2 && micro[1] == 0xB5); (sizeof(micro) == 3 && micro[0] == 0xC2 && micro[1] == 0xB5);
} }
FMT_END_DETAIL_NAMESPACE FMT_END_DETAIL_NAMESPACE
@ -2895,12 +2895,12 @@ FMT_INLINE auto vformat(
return detail::vformat(to_string_view(format_str), args); return detail::vformat(to_string_view(format_str), args);
} }
#if FMT_COMPILE_TIME_CHECKS
template <typename Char, typename... Args> class basic_format_string { template <typename Char, typename... Args> class basic_format_string {
private: private:
basic_string_view<Char> str; basic_string_view<Char> str;
public: public:
#if FMT_COMPILE_TIME_CHECKS
template <size_t N> template <size_t N>
consteval basic_format_string(const char (&s)[N]) : str(s) { consteval basic_format_string(const char (&s)[N]) : str(s) {
if constexpr (detail::count_named_args<Args...>() == 0) { if constexpr (detail::count_named_args<Args...>() == 0) {
@ -2909,21 +2909,29 @@ template <typename Char, typename... Args> class basic_format_string {
detail::parse_format_string<true>(string_view(s, N), checker(s, {})); detail::parse_format_string<true>(string_view(s, N), checker(s, {}));
} }
} }
#endif
template <typename T, template <typename T,
FMT_ENABLE_IF(std::is_constructible_v<string_view, const T&>)> FMT_ENABLE_IF(std::is_constructible<basic_string_view<Char>,
basic_format_string(const T& s) : str(s) {} const T&>::value)>
basic_format_string(const T& s) : str(s) {
static_assert(
detail::count<
(std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
std::is_reference<Args>::value)...>() == 0,
"passing views as lvalues is disallowed");
detail::check_format_string<Args...>(s);
}
operator basic_string_view<Char>() const { return str; } FMT_INLINE operator basic_string_view<Char>() const { return str; }
}; };
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround broken conversion on older gcc.
template <typename... Args> using format_string = string_view;
#else
template <typename... Args> template <typename... Args>
using format_string = basic_format_string<char, std::type_identity_t<Args>...>; using format_string = basic_format_string<char, type_identity_t<Args>...>;
template <typename... T>
FMT_INLINE auto format(format_string<T...> str, T&&... args) -> std::string {
return detail::vformat(str, make_format_args(args...));
}
#endif #endif
/** /**
@ -2936,15 +2944,9 @@ FMT_INLINE auto format(format_string<T...> str, T&&... args) -> std::string {
std::string message = fmt::format("The answer is {}", 42); std::string message = fmt::format("The answer is {}", 42);
\endrst \endrst
*/ */
// Pass char_t as a default template parameter instead of using template <typename... T>
// std::basic_string<char_t<S>> to reduce the symbol size. FMT_INLINE auto format(format_string<T...> str, T&&... args) -> std::string {
template <typename S, typename... Args, typename Char = char_t<S>, return detail::vformat(str, make_format_args(args...));
FMT_ENABLE_IF(!FMT_COMPILE_TIME_CHECKS ||
!std::is_same<Char, char>::value)>
FMT_INLINE auto format(const S& format_str, Args&&... args)
-> std::basic_string<Char> {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return detail::vformat(to_string_view(format_str), vargs);
} }
FMT_API void vprint(string_view, format_args); FMT_API void vprint(string_view, format_args);
@ -2952,8 +2954,8 @@ FMT_API void vprint(std::FILE*, string_view, format_args);
/** /**
\rst \rst
Formats ``args`` according to specifications in ``format_str`` and writes the Formats ``args`` according to specifications in ``str`` and writes the
output to the file ``f``. Strings are assumed to be Unicode-encoded unless the output to the file ``f``. Strings are assumed to be in UTF-8 unless the
``FMT_UNICODE`` macro is set to 0. ``FMT_UNICODE`` macro is set to 0.
**Example**:: **Example**::
@ -2961,32 +2963,29 @@ FMT_API void vprint(std::FILE*, string_view, format_args);
fmt::print(stderr, "Don't {}!", "panic"); fmt::print(stderr, "Don't {}!", "panic");
\endrst \endrst
*/ */
template <typename S, typename... Args, typename Char = char_t<S>> template <typename... T>
inline void print(std::FILE* f, const S& format_str, Args&&... args) { inline void print(std::FILE* f, format_string<T...> str, T&&... args) {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); const auto& vargs = make_format_args(args...);
return detail::is_unicode<Char>() return detail::is_utf8() ? vprint(f, str, vargs)
? vprint(f, to_string_view(format_str), vargs) : detail::vprint_mojibake(f, str, vargs);
: detail::vprint_mojibake(f, to_string_view(format_str), vargs);
} }
/** /**
\rst \rst
Formats ``args`` according to specifications in ``format_str`` and writes Formats ``args`` according to specifications in ``str`` and writes the output
the output to ``stdout``. Strings are assumed to be Unicode-encoded unless to ``stdout``. Strings are assumed to be in UTF-8 unless the ``FMT_UNICODE``
the ``FMT_UNICODE`` macro is set to 0. macro is set to 0.
**Example**:: **Example**::
fmt::print("Elapsed time: {0:.2f} seconds", 1.23); fmt::print("Elapsed time: {0:.2f} seconds", 1.23);
\endrst \endrst
*/ */
template <typename S, typename... Args, typename Char = char_t<S>> template <typename... T>
inline void print(const S& format_str, Args&&... args) { inline void print(format_string<T...> str, T&&... args) {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); const auto& vargs = make_format_args(args...);
return detail::is_unicode<Char>() return detail::is_utf8() ? vprint(str, vargs)
? vprint(to_string_view(format_str), vargs) : detail::vprint_mojibake(stdout, str, vargs);
: detail::vprint_mojibake(stdout, to_string_view(format_str),
vargs);
} }
FMT_MODULE_EXPORT_END FMT_MODULE_EXPORT_END

View File

@ -2658,6 +2658,16 @@ auto detail::vformat(
return to_string(buffer); return to_string(buffer);
} }
// Pass char_t as a default template parameter instead of using
// std::basic_string<char_t<S>> to reduce the symbol size.
template <typename S, typename... Args, typename Char = char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
FMT_INLINE auto format(const S& format_str, Args&&... args)
-> std::basic_string<Char> {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return detail::vformat(to_string_view(format_str), vargs);
}
template <typename Char, FMT_ENABLE_IF(std::is_same<Char, wchar_t>::value)> template <typename Char, FMT_ENABLE_IF(std::is_same<Char, wchar_t>::value)>
void vprint(std::FILE* f, basic_string_view<Char> format_str, void vprint(std::FILE* f, basic_string_view<Char> format_str,
wformat_args args) { wformat_args args) {

39
include/fmt/wchar.h Normal file
View File

@ -0,0 +1,39 @@
// Formatting library for C++ - experimental wchar_t support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_WCHAR_H_
#define FMT_WCHAR_H_
#include "format.h"
namespace fmt {
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround broken conversion on older gcc.
template <typename... Args> using wformat_string = wstring_view;
#else
template <typename... Args>
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
#endif
template <typename... Args>
constexpr format_arg_store<wformat_context, Args...> make_wformat_args(
const Args&... args) {
return {args...};
}
template <typename... T>
void print(std::FILE* f, wformat_string<T...> str, T&&... args) {
return vprint(f, wstring_view(str), make_wformat_args(args...));
}
template <typename... T> void print(wformat_string<T...> str, T&&... args) {
return vprint(wstring_view(str), make_wformat_args(args...));
}
} // namespace fmt
#endif // FMT_WCHAR_H_

View File

@ -72,6 +72,7 @@ add_fmt_test(compile-test)
add_fmt_test(printf-test) add_fmt_test(printf-test)
add_fmt_test(ranges-test) add_fmt_test(ranges-test)
add_fmt_test(scan-test) add_fmt_test(scan-test)
add_fmt_test(wchar-test)
if (NOT MSVC) if (NOT MSVC)
# FMT_ENFORCE_COMPILE_STRING is not supported under MSVC due to compiler bugs. # FMT_ENFORCE_COMPILE_STRING is not supported under MSVC due to compiler bugs.

View File

@ -590,11 +590,6 @@ TEST(core_test, to_string_view_foreign_strings) {
EXPECT_EQ(type, fmt::detail::type::string_type); EXPECT_EQ(type, fmt::detail::type::string_type);
} }
TEST(core_test, format_foreign_strings) {
using namespace test_ns;
EXPECT_EQ(fmt::format(test_string<char>("{}"), 42), "42");
}
struct implicitly_convertible_to_string { struct implicitly_convertible_to_string {
operator std::string() const { return "foo"; } operator std::string() const { return "foo"; }
}; };

View File

@ -1580,8 +1580,6 @@ TEST(format_test, print) {
EXPECT_WRITE(stdout, fmt::print("Don't {}!", "panic"), "Don't panic!"); EXPECT_WRITE(stdout, fmt::print("Don't {}!", "panic"), "Don't panic!");
EXPECT_WRITE(stderr, fmt::print(stderr, "Don't {}!", "panic"), EXPECT_WRITE(stderr, fmt::print(stderr, "Don't {}!", "panic"),
"Don't panic!"); "Don't panic!");
// Check that the wide print overload compiles.
if (fmt::detail::const_check(false)) fmt::print(L"test");
} }
TEST(format_test, variadic) { TEST(format_test, variadic) {

15
test/wchar-test.cc Normal file
View File

@ -0,0 +1,15 @@
// Formatting library for C++ - formatting library tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/wchar.h"
#include "gtest/gtest.h"
TEST(wchar_test, print) {
// Check that the wide print overload compiles.
if (fmt::detail::const_check(false)) fmt::print(L"test");
}