From e05c8fcffe6e0cdde386b50879a7d28d0ebd1136 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 30 Nov 2020 10:45:20 -0800 Subject: [PATCH 01/34] Add test --- test/CMakeLists.txt | 4 + test/enforce-compiletime-test.cc | 709 +++++++++++++++++++++++++++++++ 2 files changed, 713 insertions(+) create mode 100644 test/enforce-compiletime-test.cc diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 45721a6d8408..e6f95733a4a0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -106,6 +106,10 @@ add_fmt_test(printf-test) add_fmt_test(ranges-test) add_fmt_test(scan-test) + +add_fmt_test(enforce-compiletime-test) +target_compile_definitions(enforce-compiletime-test PRIVATE "-DFMT_ENFORCE_COMPILE_STRING") + if (NOT DEFINED MSVC_STATIC_RUNTIME AND MSVC) foreach (flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE diff --git a/test/enforce-compiletime-test.cc b/test/enforce-compiletime-test.cc new file mode 100644 index 000000000000..b39745b09c94 --- /dev/null +++ b/test/enforce-compiletime-test.cc @@ -0,0 +1,709 @@ +// 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 +#include +#include +#include + +#ifdef WIN32 +# define _CRT_SECURE_NO_WARNINGS +#endif + +#include "fmt/format.h" +#include "fmt/locale.h" +#include "fmt/chrono.h" +#include "fmt/color.h" +#include "fmt/ostream.h" + +#undef index + +#include "gmock.h" +#include "gtest-extra.h" +#include "mock-allocator.h" +#include "util.h" + +#undef ERROR + +using fmt::basic_memory_buffer; +using fmt::format; +using fmt::format_error; +using fmt::memory_buffer; +using fmt::string_view; +using fmt::wmemory_buffer; +using fmt::wstring_view; +using fmt::detail::max_value; + +using testing::Return; +using testing::StrictMock; + +class Answer {}; + +FMT_BEGIN_NAMESPACE +template <> struct formatter : formatter { + template + auto format(Answer, FormatContext& ctx) -> decltype(ctx.out()) { + return formatter::format(42, ctx); + } +}; +FMT_END_NAMESPACE + +struct string_like {}; +fmt::string_view to_string_view(string_like) { return "foo"; } + +constexpr char with_null[3] = {'{', '}', '\0'}; +constexpr char no_null[2] = {'{', '}'}; +static FMT_CONSTEXPR_DECL const char static_with_null[3] = {'{', '}', '\0'}; +static FMT_CONSTEXPR_DECL const wchar_t static_with_null_wide[3] = {'{', '}', + '\0'}; +static FMT_CONSTEXPR_DECL const char static_no_null[2] = {'{', '}'}; +static FMT_CONSTEXPR_DECL const wchar_t static_no_null_wide[2] = {'{', '}'}; + +TEST(FormatTest, CompileTimeString) { + EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42)); + EXPECT_EQ(L"42", fmt::format(FMT_STRING(L"{}"), 42)); + EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like())); + + (void)static_with_null; + (void)static_with_null_wide; + (void)static_no_null; + (void)static_no_null_wide; +#if !defined(_MSC_VER) + EXPECT_EQ("42", fmt::format(FMT_STRING(static_with_null), 42)); + EXPECT_EQ(L"42", fmt::format(FMT_STRING(static_with_null_wide), 42)); + EXPECT_EQ("42", fmt::format(FMT_STRING(static_no_null), 42)); + EXPECT_EQ(L"42", fmt::format(FMT_STRING(static_no_null_wide), 42)); +#endif + + (void)with_null; + (void)no_null; +#if __cplusplus >= 201703L + EXPECT_EQ("42", fmt::format(FMT_STRING(with_null), 42)); + EXPECT_EQ("42", fmt::format(FMT_STRING(no_null), 42)); +#endif +#if defined(FMT_USE_STRING_VIEW) && __cplusplus >= 201703L + EXPECT_EQ("42", fmt::format(FMT_STRING(std::string_view("{}")), 42)); + EXPECT_EQ(L"42", fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42)); +#endif +} + +TEST(FormatTest, CustomFormatCompileTimeString) { + EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), Answer())); + Answer answer; + EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), answer)); + char buf[10] = {}; + fmt::format_to(buf, FMT_STRING("{}"), answer); + const Answer const_answer = Answer(); + EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), const_answer)); +} + +#if FMT_USE_USER_DEFINED_LITERALS +// Passing user-defined literals directly to EXPECT_EQ causes problems +// with macro argument stringification (#) on some versions of GCC. +// Workaround: Assing the UDL result to a variable before the macro. + +using namespace fmt::literals; + +TEST(LiteralsTest, Format) { + auto udl_format = "{}c{}"_format("ab", 1); + EXPECT_EQ("abc1", udl_format); + auto udl_format_w = L"{}c{}"_format(L"ab", 1); + EXPECT_EQ(L"abc1", udl_format_w); +} + +#endif // FMT_USE_USER_DEFINED_LITERALS + +enum TestEnum { A }; + +namespace adl_test { +namespace fmt { +namespace detail { +struct foo {}; +template void write(OutputIt, foo) = delete; +} // namespace detail +} // namespace fmt +} // namespace adl_test + +FMT_BEGIN_NAMESPACE +template <> +struct formatter : formatter { + template + auto format(adl_test::fmt::detail::foo, FormatContext& ctx) + -> decltype(ctx.out()) { + return formatter::format("foo", ctx); + } +}; +FMT_END_NAMESPACE + +TEST(FormatTest, ToString) { + EXPECT_EQ("42", fmt::to_string(42)); + EXPECT_EQ("0x1234", fmt::to_string(reinterpret_cast(0x1234))); + EXPECT_EQ("foo", fmt::to_string(adl_test::fmt::detail::foo())); +} + +//TEST(FormatTest, ToWString) { EXPECT_EQ(L"42", fmt::to_wstring(42)); } + +TEST(FormatTest, OutputIterators) { + std::list out; + fmt::format_to(std::back_inserter(out), FMT_STRING("{}"), 42); + EXPECT_EQ("42", std::string(out.begin(), out.end())); + std::stringstream s; + fmt::format_to(std::ostream_iterator(s), FMT_STRING("{}"), 42); + EXPECT_EQ("42", s.str()); +} + +TEST(FormatTest, FormattedSize) { + //EXPECT_EQ(2u, fmt::formatted_size(FMT_STRING("{}"), 42)); +} + +TEST(FormatTest, FormatTo) { + std::vector v; + fmt::format_to(std::back_inserter(v), FMT_STRING("{}"), "foo"); + EXPECT_EQ(fmt::string_view(v.data(), v.size()), "foo"); +} + +TEST(FormatTest, FormatToN) { + char buffer[4]; + buffer[3] = 'x'; + auto result = fmt::format_to_n(buffer, 3, FMT_STRING("{}"), 12345); + EXPECT_EQ(5u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ("123x", fmt::string_view(buffer, 4)); + + result = fmt::format_to_n(buffer, 3, FMT_STRING("{:s}"), "foobar"); + EXPECT_EQ(6u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ("foox", fmt::string_view(buffer, 4)); + + buffer[0] = 'x'; + buffer[1] = 'x'; + buffer[2] = 'x'; + result = fmt::format_to_n(buffer, 3, FMT_STRING("{}"), 'A'); + EXPECT_EQ(1u, result.size); + EXPECT_EQ(buffer + 1, result.out); + EXPECT_EQ("Axxx", fmt::string_view(buffer, 4)); + + result = fmt::format_to_n(buffer, 3, FMT_STRING("{}{} "), 'B', 'C'); + EXPECT_EQ(3u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ("BC x", fmt::string_view(buffer, 4)); + + result = fmt::format_to_n(buffer, 4, FMT_STRING("{}"), "ABCDE"); + EXPECT_EQ(5u, result.size); + EXPECT_EQ("ABCD", fmt::string_view(buffer, 4)); + + buffer[3] = 'x'; + result = fmt::format_to_n(buffer, 3, FMT_STRING("{}"), std::string(1000, '*')); + EXPECT_EQ(1000u, result.size); + EXPECT_EQ("***x", fmt::string_view(buffer, 4)); +} + +TEST(FormatTest, WideFormatToN) { + wchar_t buffer[4]; + buffer[3] = L'x'; + auto result = fmt::format_to_n(buffer, 3, FMT_STRING(L"{}"), 12345); + EXPECT_EQ(5u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ(L"123x", fmt::wstring_view(buffer, 4)); + buffer[0] = L'x'; + buffer[1] = L'x'; + buffer[2] = L'x'; + result = fmt::format_to_n(buffer, 3, FMT_STRING(L"{}"), L'A'); + EXPECT_EQ(1u, result.size); + EXPECT_EQ(buffer + 1, result.out); + EXPECT_EQ(L"Axxx", fmt::wstring_view(buffer, 4)); + result = fmt::format_to_n(buffer, 3, FMT_STRING(L"{}{} "), L'B', L'C'); + EXPECT_EQ(3u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ(L"BC x", fmt::wstring_view(buffer, 4)); +} + +struct test_output_iterator { + char* data; + + using iterator_category = std::output_iterator_tag; + using value_type = void; + using difference_type = void; + using pointer = void; + using reference = void; + + test_output_iterator& operator++() { + ++data; + return *this; + } + test_output_iterator operator++(int) { + auto tmp = *this; + ++data; + return tmp; + } + char& operator*() { return *data; } +}; + +TEST(FormatTest, FormatToNOutputIterator) { + char buf[10] = {}; + fmt::format_to_n(test_output_iterator{buf}, 10, FMT_STRING("{}"), 42); + EXPECT_STREQ(buf, "42"); +} + +TEST(FormatTest, VFormatTo) { + typedef fmt::format_context context; + fmt::basic_format_arg arg = fmt::detail::make_arg(42); + fmt::basic_format_args args(&arg, 1); + std::string s; + fmt::vformat_to(std::back_inserter(s), FMT_STRING("{}"), args); + EXPECT_EQ("42", s); + + typedef fmt::wformat_context wcontext; + fmt::basic_format_arg warg = fmt::detail::make_arg(42); + fmt::basic_format_args wargs(&warg, 1); + std::wstring w; + fmt::vformat_to(std::back_inserter(w), FMT_STRING(L"{}"), wargs); + EXPECT_EQ(L"42", w); +} + +template static std::string FmtToString(const T& t) { + return fmt::format(FMT_STRING("{}"), t); +} + +TEST(FormatTest, FmtStringInTemplate) { + EXPECT_EQ(FmtToString(1), "1"); + EXPECT_EQ(FmtToString(0), "0"); +} + + + +#include "fmt/chrono.h" + +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +/* +TEST(ChronoTest, FormatDefault) { + EXPECT_EQ("42s", fmt::format(FMT_STRING("{}"), std::chrono::seconds(42))); +} + +TEST(ChronoTest, FormatWide) { + EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds(42))); +} + +typedef std::chrono::duration dms; + +TEST(ChronoTest, FormatDefaultFP) { + EXPECT_EQ("1.234ms", fmt::format(FMT_STRING("{}"), dms(1.234))); +} + +TEST(ChronoTest, FormatPrecision) { + EXPECT_EQ("1.2ms", fmt::format(FMT_STRING("{:.1}"), dms(1.234))); + EXPECT_EQ("1.23ms", fmt::format(FMT_STRING("{:.{}}"), dms(1.234), 2)); +} + +TEST(ChronoTest, FormatFullSpecs) { + EXPECT_EQ("1.2ms ", fmt::format(FMT_STRING("{:6.1}"), dms(1.234))); + EXPECT_EQ(" 1.2ms ", fmt::format(FMT_STRING("{:^{}.{}}"), dms(1.234), 7, 1)); +} + +TEST(ChronoTest, FormatSimpleQq) { + typedef std::chrono::duration fs; + //EXPECT_EQ("1.234 s", fmt::format(FMT_STRING("{:%Q %q}"), fs(1.234))); + typedef std::chrono::duration fms; + EXPECT_EQ("1.234 ms", fmt::format(FMT_STRING("{:%Q %q}"), fms(1.234))); + typedef std::chrono::duration ds; + EXPECT_EQ("1.234 s", fmt::format(FMT_STRING("{:%Q %q}"), ds(1.234))); + EXPECT_EQ("1.234 ms", fmt::format(FMT_STRING("{:%Q %q}"), dms(1.234))); +} + +TEST(ChronoTest, FormatPrecisionQq) { + EXPECT_EQ("1.2 ms", fmt::format(FMT_STRING("{:.1%Q %q}"), dms(1.234))); + EXPECT_EQ("1.23 ms", fmt::format(FMT_STRING("{:.{}%Q %q}"), dms(1.234), 2)); +} + +TEST(ChronoTest, FormatFullSpecsQq) { + EXPECT_EQ("1.2 ms ", fmt::format(FMT_STRING("{:7.1%Q %q}"), dms(1.234))); + EXPECT_EQ(" 1.2 ms ", fmt::format(FMT_STRING("{:^{}.{}%Q %q}"), dms(1.234), 8, 1)); +} + +TEST(ChronoTest, UnsignedDuration) { + EXPECT_EQ("42s", fmt::format(FMT_STRING("{}"), std::chrono::duration(42))); +} +*/ +#endif // FMT_STATIC_THOUSANDS_SEPARATOR + + +TEST(ColorsTest, ColorsPrint) { + EXPECT_WRITE(stdout, fmt::print(fg(fmt::rgb(255, 20, 30)), FMT_STRING("rgb(255,20,30)")), + "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); + EXPECT_WRITE( + stdout, + fmt::print(fg(fmt::color::blue) | bg(fmt::color::red), FMT_STRING("two color")), + "\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m"); + EXPECT_WRITE(stdout, fmt::print(fmt::emphasis::bold, FMT_STRING("bold")), + "\x1b[1mbold\x1b[0m"); + EXPECT_WRITE( + stdout, + fmt::print(fg(fmt::color::blue) | fmt::emphasis::bold, FMT_STRING("blue/bold")), + "\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m"); + EXPECT_WRITE(stdout, fmt::print(fmt::text_style(), FMT_STRING("hi")), "hi"); + EXPECT_WRITE(stdout, fmt::print(fg(fmt::terminal_color::red), FMT_STRING("tred")), + "\x1b[31mtred\x1b[0m"); + EXPECT_WRITE(stdout, fmt::print(bg(fmt::terminal_color::cyan), FMT_STRING("tcyan")), + "\x1b[46mtcyan\x1b[0m"); + EXPECT_WRITE(stdout, + fmt::print(bg(fmt::terminal_color::bright_magenta), FMT_STRING("tbmagenta")), + "\x1b[105mtbmagenta\x1b[0m"); +} + +TEST(ColorsTest, Format) { + EXPECT_EQ("\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m", fmt::format(fg(fmt::rgb(255, 20, 30)), FMT_STRING("rgb(255,20,30)"))); + EXPECT_EQ("\x1b[38;2;000;000;255mblue\x1b[0m", fmt::format(fg(fmt::color::blue), FMT_STRING("blue"))); + EXPECT_EQ( + "\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m", + fmt::format(fg(fmt::color::blue) | bg(fmt::color::red), FMT_STRING("two color"))); + EXPECT_EQ("\x1b[1mbold\x1b[0m", fmt::format(fmt::emphasis::bold, FMT_STRING("bold"))); + EXPECT_EQ( + "\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m", + fmt::format(fg(fmt::color::blue) | fmt::emphasis::bold, FMT_STRING("blue/bold"))); + EXPECT_EQ("hi", fmt::format(fmt::text_style(), FMT_STRING("hi"))); + EXPECT_EQ("\x1b[31mtred\x1b[0m", + fmt::format(fg(fmt::terminal_color::red), FMT_STRING("tred"))); + EXPECT_EQ("\x1b[46mtcyan\x1b[0m", + fmt::format(bg(fmt::terminal_color::cyan), FMT_STRING("tcyan"))); + EXPECT_EQ("\x1b[105mtbmagenta\x1b[0m", + fmt::format(bg(fmt::terminal_color::bright_magenta), FMT_STRING("tbmagenta"))); +} + +TEST(ColorsTest, FormatToOutAcceptsTextStyle) { + fmt::text_style ts = fg(fmt::rgb(255, 20, 30)); + std::string out; + fmt::format_to(std::back_inserter(out), ts, FMT_STRING("rgb(255,20,30){}{}{}"), 1, 2, 3); + + EXPECT_EQ(fmt::to_string(out), + "\x1b[38;2;255;020;030mrgb(255,20,30)123\x1b[0m"); +} + +// Formatting library for C++ - std::ostream support tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#define FMT_STRING_ALIAS 1 +#include "fmt/format.h" + +struct test {}; + +// Test that there is no issues with specializations when fmt/ostream.h is +// included after fmt/format.h. +namespace fmt { +template <> struct formatter : formatter { + template + typename FormatContext::iterator format(const test&, FormatContext& ctx) { + return formatter::format(42, ctx); + } +}; +} // namespace fmt + +#include + +#include "fmt/ostream.h" + +struct EmptyTest {}; +static std::ostream& operator<<(std::ostream& os, EmptyTest) { + return os << ""; +} + +TEST(OStreamTest, EmptyCustomOutput) { + EXPECT_EQ("", fmt::format(FMT_STRING("{}"), EmptyTest())); +} + +TEST(OStreamTest, Print) { + std::ostringstream os; + fmt::print(os, FMT_STRING("Don't {}!"), "panic"); + EXPECT_EQ("Don't panic!", os.str()); + std::wostringstream wos; + fmt::print(wos, FMT_STRING(L"Don't {}!"), L"panic"); + EXPECT_EQ(L"Don't panic!", wos.str()); +} + +TEST(OStreamTest, Join) { + int v[3] = {1, 2, 3}; + EXPECT_EQ("1, 2, 3", fmt::format(FMT_STRING("{}"), fmt::join(v, v + 3, ", "))); +} + +namespace fmt_test { +struct ABC {}; + +template Output& operator<<(Output& out, ABC) { + out << "ABC"; + return out; +} +} // namespace fmt_test + +template struct TestTemplate {}; + +template +std::ostream& operator<<(std::ostream& os, TestTemplate) { + return os << 1; +} + +namespace fmt { +template struct formatter> : formatter { + template + typename FormatContext::iterator format(TestTemplate, FormatContext& ctx) { + return formatter::format(2, ctx); + } +}; +} // namespace fmt + +#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 407 +TEST(OStreamTest, Template) { + EXPECT_EQ("2", fmt::format(FMT_STRING("{}"), TestTemplate())); +} + +TEST(OStreamTest, FormatToN) { + char buffer[4]; + buffer[3] = 'x'; + auto result = fmt::format_to_n(buffer, 3, FMT_STRING("{}"), fmt_test::ABC()); + EXPECT_EQ(3u, result.size); + EXPECT_EQ(buffer + 3, result.out); + EXPECT_EQ("ABCx", fmt::string_view(buffer, 4)); +} +#endif + +TEST(OStreamTest, CompileTimeString) { + EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42)); +} + +TEST(OStreamTest, ToString) { + EXPECT_EQ("ABC", fmt::to_string(fmt_test::ABC())); +} + + +#include "fmt/ranges.h" + +// Check if 'if constexpr' is supported. +#if (__cplusplus > 201402L) || \ + (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) + +# include +# include +# include +# include + +TEST(RangesTest, FormatArray) { + int32_t ia[] = {1, 2, 3, 5, 7, 11}; + auto iaf = fmt::format(FMT_STRING("{}"), ia); + EXPECT_EQ("{1, 2, 3, 5, 7, 11}", iaf); +} + +TEST(RangesTest, Format2dArray) { + int32_t ia[][2] = {{1, 2}, {3, 5}, {7, 11}}; + auto iaf = fmt::format(FMT_STRING("{}"), ia); + EXPECT_EQ("{{1, 2}, {3, 5}, {7, 11}}", iaf); +} + +TEST(RangesTest, FormatVector) { + std::vector iv{1, 2, 3, 5, 7, 11}; + auto ivf = fmt::format(FMT_STRING("{}"), iv); + EXPECT_EQ("{1, 2, 3, 5, 7, 11}", ivf); +} + +TEST(RangesTest, FormatVector2) { + std::vector> ivv{{1, 2}, {3, 5}, {7, 11}}; + auto ivf = fmt::format(FMT_STRING("{}"), ivv); + EXPECT_EQ("{{1, 2}, {3, 5}, {7, 11}}", ivf); +} + +TEST(RangesTest, FormatMap) { + std::map simap{{"one", 1}, {"two", 2}}; + EXPECT_EQ("{(\"one\", 1), (\"two\", 2)}", fmt::format(FMT_STRING("{}"), simap)); +} + +TEST(RangesTest, FormatPair) { + std::pair pa1{42, 1.5f}; + EXPECT_EQ("(42, 1.5)", fmt::format(FMT_STRING("{}"), pa1)); +} + +TEST(RangesTest, FormatTuple) { + std::tuple t{42, 1.5f, "this is tuple", + 'i'}; + EXPECT_EQ("(42, 1.5, \"this is tuple\", 'i')", fmt::format(FMT_STRING("{}"), t)); + EXPECT_EQ("()", fmt::format(FMT_STRING("{}"), std::tuple<>())); +} + +TEST(RangesTest, JoinTuple) { + // Value tuple args + std::tuple t1 = std::make_tuple('a', 1, 2.0f); + EXPECT_EQ("(a, 1, 2)", fmt::format(FMT_STRING("({})"), fmt::join(t1, ", "))); + + // Testing lvalue tuple args + int x = 4; + std::tuple t2{'b', x}; + EXPECT_EQ("b + 4", fmt::format(FMT_STRING("{}"), fmt::join(t2, " + "))); + + // Empty tuple + std::tuple<> t3; + EXPECT_EQ("", fmt::format(FMT_STRING("{}"), fmt::join(t3, "|"))); + + // Single element tuple + std::tuple t4{4.0f}; + EXPECT_EQ("4", fmt::format(FMT_STRING("{}"), fmt::join(t4, "/"))); +} + +TEST(RangesTest, JoinInitializerList) { + EXPECT_EQ("1, 2, 3", fmt::format(FMT_STRING("{}"), fmt::join({1, 2, 3}, ", "))); + EXPECT_EQ("fmt rocks !", + fmt::format(FMT_STRING("{}"), fmt::join({"fmt", "rocks", "!"}, " "))); +} + +struct my_struct { + int32_t i; + std::string str; // can throw + template decltype(auto) get() const noexcept { + if constexpr (N == 0) + return i; + else if constexpr (N == 1) + return fmt::string_view{str}; + } +}; + +template decltype(auto) get(const my_struct& s) noexcept { + return s.get(); +} + +namespace std { + +template <> struct tuple_size : std::integral_constant {}; + +template struct tuple_element { + using type = decltype(std::declval().get()); +}; + +} // namespace std + +TEST(RangesTest, FormatStruct) { + my_struct mst{13, "my struct"}; + EXPECT_EQ("(13, \"my struct\")", fmt::format(FMT_STRING("{}"), mst)); +} + +TEST(RangesTest, FormatTo) { + char buf[10]; + auto end = fmt::format_to(buf, FMT_STRING("{}"), std::vector{1, 2, 3}); + *end = '\0'; + EXPECT_STREQ(buf, "{1, 2, 3}"); +} + +struct path_like { + const path_like* begin() const; + const path_like* end() const; + + operator std::string() const; +}; + +TEST(RangesTest, PathLike) { + EXPECT_FALSE((fmt::is_range::value)); +} + +#endif // (__cplusplus > 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG > + // 201402L && _MSC_VER >= 1910) + +#ifdef FMT_USE_STRING_VIEW +struct string_like { + const char* begin(); + const char* end(); + explicit operator fmt::string_view() const { return "foo"; } + explicit operator std::string_view() const { return "foo"; } +}; + +TEST(RangesTest, FormatStringLike) { + EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like())); +} +#endif // FMT_USE_STRING_VIEW + +struct zstring_sentinel {}; + +bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; } +bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; } + +struct zstring { + const char* p; + const char* begin() const { return p; } + zstring_sentinel end() const { return {}; } +}; + +// TODO: Fix using zstrings with FMT_STRING +TEST(RangesTest, JoinSentinel) { + zstring hello{"hello"}; + //EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format(FMT_STRING("{}"), hello)); + //EXPECT_EQ("h_e_l_l_o", fmt::format(FMT_STRING("{}"), fmt::join(hello, "_"))); +} + +// A range that provides non-const only begin()/end() to test fmt::join handles +// that +// +// Some ranges (eg those produced by range-v3's views::filter()) can cache +// information during iteration so they only provide non-const begin()/end(). +template class non_const_only_range { + private: + std::vector vec; + + public: + using const_iterator = typename ::std::vector::const_iterator; + + template + explicit non_const_only_range(Args&&... args) + : vec(::std::forward(args)...) {} + + const_iterator begin() { return vec.begin(); } + const_iterator end() { return vec.end(); } +}; + +template class noncopyable_range { + private: + std::vector vec; + + public: + using const_iterator = typename ::std::vector::const_iterator; + + template + explicit noncopyable_range(Args&&... args) + : vec(::std::forward(args)...) {} + + noncopyable_range(noncopyable_range const&) = delete; + noncopyable_range(noncopyable_range&) = delete; + + const_iterator begin() const { return vec.begin(); } + const_iterator end() const { return vec.end(); } +}; + +//TODO: Fixme +TEST(RangesTest, Range) { + noncopyable_range w(3u, 0); + /*EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), w)); + EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), noncopyable_range(3u, 0))); + + non_const_only_range x(3u, 0); + EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), x)); + EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), non_const_only_range(3u, 0))); + + std::vector y(3u, 0); + EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), y)); + EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), std::vector(3u, 0))); + + const std::vector z(3u, 0); + EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), z));*/ +} + +#if !FMT_MSC_VER || FMT_MSC_VER >= 1927 +struct unformattable {}; + +TEST(RangesTest, UnformattableRange) { + EXPECT_FALSE((fmt::has_formatter, + fmt::format_context>::value)); +} +#endif + + + From f76c64abe52481e4b7ea8f086a0eff4cb2da5521 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 30 Nov 2020 10:49:15 -0800 Subject: [PATCH 02/34] fix wide string formatting --- include/fmt/format.h | 2 +- test/enforce-compiletime-test.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index ceef2ef18d93..ddf24503051e 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -3828,7 +3828,7 @@ inline std::string to_string(T value) { Converts *value* to ``std::wstring`` using the default format for type *T*. */ template inline std::wstring to_wstring(const T& value) { - return format(L"{}", value); + return format(FMT_STRING(L"{}"), value); } template diff --git a/test/enforce-compiletime-test.cc b/test/enforce-compiletime-test.cc index b39745b09c94..79ab3e82d086 100644 --- a/test/enforce-compiletime-test.cc +++ b/test/enforce-compiletime-test.cc @@ -145,7 +145,7 @@ TEST(FormatTest, ToString) { EXPECT_EQ("foo", fmt::to_string(adl_test::fmt::detail::foo())); } -//TEST(FormatTest, ToWString) { EXPECT_EQ(L"42", fmt::to_wstring(42)); } +TEST(FormatTest, ToWString) { EXPECT_EQ(L"42", fmt::to_wstring(42)); } TEST(FormatTest, OutputIterators) { std::list out; From b75bd49b6cea9c1dc2a9a73118cbd7ff615b7909 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 30 Nov 2020 11:02:28 -0800 Subject: [PATCH 03/34] fix formatted_size --- include/fmt/core.h | 8 ++++++++ test/enforce-compiletime-test.cc | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index d7d2de50126f..1def5d6f9d37 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1841,6 +1841,14 @@ inline auto format_to_n(OutputIt out, size_t n, const S& format_str, Returns the number of characters in the output of ``format(format_str, args...)``. */ +template ::value)> +inline size_t formatted_size(const S& format_str, Args&&... args) { + const auto& vargs = fmt::make_args_checked(format_str, args...); + detail::counting_buffer<> buf; + detail::vformat_to(buf, to_string_view(format_str), vargs); + return buf.count(); +} + template inline size_t formatted_size(string_view format_str, Args&&... args) { const auto& vargs = fmt::make_args_checked(format_str, args...); diff --git a/test/enforce-compiletime-test.cc b/test/enforce-compiletime-test.cc index 79ab3e82d086..0260187ebc42 100644 --- a/test/enforce-compiletime-test.cc +++ b/test/enforce-compiletime-test.cc @@ -157,7 +157,7 @@ TEST(FormatTest, OutputIterators) { } TEST(FormatTest, FormattedSize) { - //EXPECT_EQ(2u, fmt::formatted_size(FMT_STRING("{}"), 42)); + EXPECT_EQ(2u, fmt::formatted_size(FMT_STRING("{}"), 42)); } TEST(FormatTest, FormatTo) { From 2f9484c74b5385b0fe4e1d8fc6102a181b64741e Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 30 Nov 2020 11:05:50 -0800 Subject: [PATCH 04/34] fix chrono --- include/fmt/chrono.h | 10 +++++----- test/enforce-compiletime-test.cc | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index d68d8c9a0dce..4c05551626ed 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -769,7 +769,7 @@ template ::value)> OutputIt format_duration_value(OutputIt out, Rep val, int) { static FMT_CONSTEXPR_DECL const Char format[] = {'{', '}', 0}; - return format_to(out, compile_string_to_view(format), val); + return format_to(out, FMT_STRING(format), val); } template = 0) - return format_to(out, compile_string_to_view(pr_f), val, precision); + return format_to(out, FMT_STRING(pr_f), val, precision); static FMT_CONSTEXPR_DECL const Char fp_f[] = {'{', ':', 'g', '}', 0}; - return format_to(out, compile_string_to_view(fp_f), val); + return format_to(out, FMT_STRING(fp_f), val); } template @@ -802,10 +802,10 @@ OutputIt format_duration_unit(OutputIt out) { return copy_unit(string_view(unit), out, Char()); static FMT_CONSTEXPR_DECL const Char num_f[] = {'[', '{', '}', ']', 's', 0}; if (const_check(Period::den == 1)) - return format_to(out, compile_string_to_view(num_f), Period::num); + return format_to(out, FMT_STRING(num_f), Period::num); static FMT_CONSTEXPR_DECL const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0}; - return format_to(out, compile_string_to_view(num_def_f), Period::num, + return format_to(out, FMT_STRING(num_def_f), Period::num, Period::den); } diff --git a/test/enforce-compiletime-test.cc b/test/enforce-compiletime-test.cc index 0260187ebc42..70e0e846a353 100644 --- a/test/enforce-compiletime-test.cc +++ b/test/enforce-compiletime-test.cc @@ -279,13 +279,13 @@ TEST(FormatTest, FmtStringInTemplate) { #include "fmt/chrono.h" #ifndef FMT_STATIC_THOUSANDS_SEPARATOR -/* + TEST(ChronoTest, FormatDefault) { EXPECT_EQ("42s", fmt::format(FMT_STRING("{}"), std::chrono::seconds(42))); } TEST(ChronoTest, FormatWide) { - EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds(42))); + EXPECT_EQ(L"42s", fmt::format(FMT_STRING(L"{}"), std::chrono::seconds(42))); } typedef std::chrono::duration dms; @@ -327,7 +327,7 @@ TEST(ChronoTest, FormatFullSpecsQq) { TEST(ChronoTest, UnsignedDuration) { EXPECT_EQ("42s", fmt::format(FMT_STRING("{}"), std::chrono::duration(42))); } -*/ + #endif // FMT_STATIC_THOUSANDS_SEPARATOR From 3b6d21be159ed5b426a703b648338692188f68d5 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 30 Nov 2020 11:21:34 -0800 Subject: [PATCH 05/34] clang-format, test fixes --- include/fmt/chrono.h | 6 +- include/fmt/core.h | 3 +- test/enforce-compiletime-test.cc | 157 ++++++++++++++++--------------- 3 files changed, 85 insertions(+), 81 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 4c05551626ed..98eac2efde90 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -777,8 +777,7 @@ template = 0) - return format_to(out, FMT_STRING(pr_f), val, precision); + if (precision >= 0) return format_to(out, FMT_STRING(pr_f), val, precision); static FMT_CONSTEXPR_DECL const Char fp_f[] = {'{', ':', 'g', '}', 0}; return format_to(out, FMT_STRING(fp_f), val); } @@ -805,8 +804,7 @@ OutputIt format_duration_unit(OutputIt out) { return format_to(out, FMT_STRING(num_f), Period::num); static FMT_CONSTEXPR_DECL const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0}; - return format_to(out, FMT_STRING(num_def_f), Period::num, - Period::den); + return format_to(out, FMT_STRING(num_def_f), Period::num, Period::den); } template ::value)> +template ::value)> inline size_t formatted_size(const S& format_str, Args&&... args) { const auto& vargs = fmt::make_args_checked(format_str, args...); detail::counting_buffer<> buf; diff --git a/test/enforce-compiletime-test.cc b/test/enforce-compiletime-test.cc index 70e0e846a353..db5f0c5687e2 100644 --- a/test/enforce-compiletime-test.cc +++ b/test/enforce-compiletime-test.cc @@ -5,20 +5,25 @@ // // For the license information refer to format.h. -#include +#include #include +#include +#include +#include #include #include +#include #ifdef WIN32 # define _CRT_SECURE_NO_WARNINGS #endif -#include "fmt/format.h" -#include "fmt/locale.h" #include "fmt/chrono.h" #include "fmt/color.h" +#include "fmt/format.h" +#include "fmt/locale.h" #include "fmt/ostream.h" +#include "fmt/ranges.h" #undef index @@ -52,7 +57,15 @@ template <> struct formatter : formatter { }; FMT_END_NAMESPACE -struct string_like {}; +struct string_like { + const char* begin(); + const char* end(); +#if defined(FMT_USE_STRING_VIEW) + explicit operator fmt::string_view() const { return "foo"; } + explicit operator std::string_view() const { return "foo"; } +#endif +}; + fmt::string_view to_string_view(string_like) { return "foo"; } constexpr char with_null[3] = {'{', '}', '\0'}; @@ -197,7 +210,8 @@ TEST(FormatTest, FormatToN) { EXPECT_EQ("ABCD", fmt::string_view(buffer, 4)); buffer[3] = 'x'; - result = fmt::format_to_n(buffer, 3, FMT_STRING("{}"), std::string(1000, '*')); + result = + fmt::format_to_n(buffer, 3, FMT_STRING("{}"), std::string(1000, '*')); EXPECT_EQ(1000u, result.size); EXPECT_EQ("***x", fmt::string_view(buffer, 4)); } @@ -274,10 +288,6 @@ TEST(FormatTest, FmtStringInTemplate) { EXPECT_EQ(FmtToString(0), "0"); } - - -#include "fmt/chrono.h" - #ifndef FMT_STATIC_THOUSANDS_SEPARATOR TEST(ChronoTest, FormatDefault) { @@ -306,7 +316,7 @@ TEST(ChronoTest, FormatFullSpecs) { TEST(ChronoTest, FormatSimpleQq) { typedef std::chrono::duration fs; - //EXPECT_EQ("1.234 s", fmt::format(FMT_STRING("{:%Q %q}"), fs(1.234))); + // EXPECT_EQ("1.234 s", fmt::format(FMT_STRING("{:%Q %q}"), fs(1.234))); typedef std::chrono::duration fms; EXPECT_EQ("1.234 ms", fmt::format(FMT_STRING("{:%Q %q}"), fms(1.234))); typedef std::chrono::duration ds; @@ -321,62 +331,74 @@ TEST(ChronoTest, FormatPrecisionQq) { TEST(ChronoTest, FormatFullSpecsQq) { EXPECT_EQ("1.2 ms ", fmt::format(FMT_STRING("{:7.1%Q %q}"), dms(1.234))); - EXPECT_EQ(" 1.2 ms ", fmt::format(FMT_STRING("{:^{}.{}%Q %q}"), dms(1.234), 8, 1)); + EXPECT_EQ(" 1.2 ms ", + fmt::format(FMT_STRING("{:^{}.{}%Q %q}"), dms(1.234), 8, 1)); } TEST(ChronoTest, UnsignedDuration) { - EXPECT_EQ("42s", fmt::format(FMT_STRING("{}"), std::chrono::duration(42))); + EXPECT_EQ("42s", + fmt::format(FMT_STRING("{}"), std::chrono::duration(42))); } #endif // FMT_STATIC_THOUSANDS_SEPARATOR - TEST(ColorsTest, ColorsPrint) { - EXPECT_WRITE(stdout, fmt::print(fg(fmt::rgb(255, 20, 30)), FMT_STRING("rgb(255,20,30)")), - "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); EXPECT_WRITE( stdout, - fmt::print(fg(fmt::color::blue) | bg(fmt::color::red), FMT_STRING("two color")), - "\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m"); + fmt::print(fg(fmt::rgb(255, 20, 30)), FMT_STRING("rgb(255,20,30)")), + "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); + EXPECT_WRITE(stdout, + fmt::print(fg(fmt::color::blue) | bg(fmt::color::red), + FMT_STRING("two color")), + "\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m"); EXPECT_WRITE(stdout, fmt::print(fmt::emphasis::bold, FMT_STRING("bold")), "\x1b[1mbold\x1b[0m"); - EXPECT_WRITE( - stdout, - fmt::print(fg(fmt::color::blue) | fmt::emphasis::bold, FMT_STRING("blue/bold")), - "\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m"); + EXPECT_WRITE(stdout, + fmt::print(fg(fmt::color::blue) | fmt::emphasis::bold, + FMT_STRING("blue/bold")), + "\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m"); EXPECT_WRITE(stdout, fmt::print(fmt::text_style(), FMT_STRING("hi")), "hi"); - EXPECT_WRITE(stdout, fmt::print(fg(fmt::terminal_color::red), FMT_STRING("tred")), + EXPECT_WRITE(stdout, + fmt::print(fg(fmt::terminal_color::red), FMT_STRING("tred")), "\x1b[31mtred\x1b[0m"); - EXPECT_WRITE(stdout, fmt::print(bg(fmt::terminal_color::cyan), FMT_STRING("tcyan")), + EXPECT_WRITE(stdout, + fmt::print(bg(fmt::terminal_color::cyan), FMT_STRING("tcyan")), "\x1b[46mtcyan\x1b[0m"); EXPECT_WRITE(stdout, - fmt::print(bg(fmt::terminal_color::bright_magenta), FMT_STRING("tbmagenta")), + fmt::print(bg(fmt::terminal_color::bright_magenta), + FMT_STRING("tbmagenta")), "\x1b[105mtbmagenta\x1b[0m"); } TEST(ColorsTest, Format) { - EXPECT_EQ("\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m", fmt::format(fg(fmt::rgb(255, 20, 30)), FMT_STRING("rgb(255,20,30)"))); - EXPECT_EQ("\x1b[38;2;000;000;255mblue\x1b[0m", fmt::format(fg(fmt::color::blue), FMT_STRING("blue"))); EXPECT_EQ( - "\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m", - fmt::format(fg(fmt::color::blue) | bg(fmt::color::red), FMT_STRING("two color"))); - EXPECT_EQ("\x1b[1mbold\x1b[0m", fmt::format(fmt::emphasis::bold, FMT_STRING("bold"))); - EXPECT_EQ( - "\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m", - fmt::format(fg(fmt::color::blue) | fmt::emphasis::bold, FMT_STRING("blue/bold"))); + "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m", + fmt::format(fg(fmt::rgb(255, 20, 30)), FMT_STRING("rgb(255,20,30)"))); + EXPECT_EQ("\x1b[38;2;000;000;255mblue\x1b[0m", + fmt::format(fg(fmt::color::blue), FMT_STRING("blue"))); + EXPECT_EQ("\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m", + fmt::format(fg(fmt::color::blue) | bg(fmt::color::red), + FMT_STRING("two color"))); + EXPECT_EQ("\x1b[1mbold\x1b[0m", + fmt::format(fmt::emphasis::bold, FMT_STRING("bold"))); + EXPECT_EQ("\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m", + fmt::format(fg(fmt::color::blue) | fmt::emphasis::bold, + FMT_STRING("blue/bold"))); EXPECT_EQ("hi", fmt::format(fmt::text_style(), FMT_STRING("hi"))); - EXPECT_EQ("\x1b[31mtred\x1b[0m", - fmt::format(fg(fmt::terminal_color::red), FMT_STRING("tred"))); - EXPECT_EQ("\x1b[46mtcyan\x1b[0m", - fmt::format(bg(fmt::terminal_color::cyan), FMT_STRING("tcyan"))); - EXPECT_EQ("\x1b[105mtbmagenta\x1b[0m", - fmt::format(bg(fmt::terminal_color::bright_magenta), FMT_STRING("tbmagenta"))); + EXPECT_EQ("\x1b[31mtred\x1b[0m", + fmt::format(fg(fmt::terminal_color::red), FMT_STRING("tred"))); + EXPECT_EQ("\x1b[46mtcyan\x1b[0m", + fmt::format(bg(fmt::terminal_color::cyan), FMT_STRING("tcyan"))); + EXPECT_EQ("\x1b[105mtbmagenta\x1b[0m", + fmt::format(bg(fmt::terminal_color::bright_magenta), + FMT_STRING("tbmagenta"))); } TEST(ColorsTest, FormatToOutAcceptsTextStyle) { fmt::text_style ts = fg(fmt::rgb(255, 20, 30)); std::string out; - fmt::format_to(std::back_inserter(out), ts, FMT_STRING("rgb(255,20,30){}{}{}"), 1, 2, 3); + fmt::format_to(std::back_inserter(out), ts, + FMT_STRING("rgb(255,20,30){}{}{}"), 1, 2, 3); EXPECT_EQ(fmt::to_string(out), "\x1b[38;2;255;020;030mrgb(255,20,30)123\x1b[0m"); @@ -389,9 +411,6 @@ TEST(ColorsTest, FormatToOutAcceptsTextStyle) { // // For the license information refer to format.h. -#define FMT_STRING_ALIAS 1 -#include "fmt/format.h" - struct test {}; // Test that there is no issues with specializations when fmt/ostream.h is @@ -405,10 +424,6 @@ template <> struct formatter : formatter { }; } // namespace fmt -#include - -#include "fmt/ostream.h" - struct EmptyTest {}; static std::ostream& operator<<(std::ostream& os, EmptyTest) { return os << ""; @@ -429,7 +444,8 @@ TEST(OStreamTest, Print) { TEST(OStreamTest, Join) { int v[3] = {1, 2, 3}; - EXPECT_EQ("1, 2, 3", fmt::format(FMT_STRING("{}"), fmt::join(v, v + 3, ", "))); + EXPECT_EQ("1, 2, 3", + fmt::format(FMT_STRING("{}"), fmt::join(v, v + 3, ", "))); } namespace fmt_test { @@ -480,18 +496,10 @@ TEST(OStreamTest, ToString) { EXPECT_EQ("ABC", fmt::to_string(fmt_test::ABC())); } - -#include "fmt/ranges.h" - // Check if 'if constexpr' is supported. #if (__cplusplus > 201402L) || \ (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) -# include -# include -# include -# include - TEST(RangesTest, FormatArray) { int32_t ia[] = {1, 2, 3, 5, 7, 11}; auto iaf = fmt::format(FMT_STRING("{}"), ia); @@ -518,7 +526,8 @@ TEST(RangesTest, FormatVector2) { TEST(RangesTest, FormatMap) { std::map simap{{"one", 1}, {"two", 2}}; - EXPECT_EQ("{(\"one\", 1), (\"two\", 2)}", fmt::format(FMT_STRING("{}"), simap)); + EXPECT_EQ("{(\"one\", 1), (\"two\", 2)}", + fmt::format(FMT_STRING("{}"), simap)); } TEST(RangesTest, FormatPair) { @@ -529,7 +538,8 @@ TEST(RangesTest, FormatPair) { TEST(RangesTest, FormatTuple) { std::tuple t{42, 1.5f, "this is tuple", 'i'}; - EXPECT_EQ("(42, 1.5, \"this is tuple\", 'i')", fmt::format(FMT_STRING("{}"), t)); + EXPECT_EQ("(42, 1.5, \"this is tuple\", 'i')", + fmt::format(FMT_STRING("{}"), t)); EXPECT_EQ("()", fmt::format(FMT_STRING("{}"), std::tuple<>())); } @@ -553,9 +563,10 @@ TEST(RangesTest, JoinTuple) { } TEST(RangesTest, JoinInitializerList) { - EXPECT_EQ("1, 2, 3", fmt::format(FMT_STRING("{}"), fmt::join({1, 2, 3}, ", "))); - EXPECT_EQ("fmt rocks !", - fmt::format(FMT_STRING("{}"), fmt::join({"fmt", "rocks", "!"}, " "))); + EXPECT_EQ("1, 2, 3", + fmt::format(FMT_STRING("{}"), fmt::join({1, 2, 3}, ", "))); + EXPECT_EQ("fmt rocks !", fmt::format(FMT_STRING("{}"), + fmt::join({"fmt", "rocks", "!"}, " "))); } struct my_struct { @@ -610,13 +621,6 @@ TEST(RangesTest, PathLike) { // 201402L && _MSC_VER >= 1910) #ifdef FMT_USE_STRING_VIEW -struct string_like { - const char* begin(); - const char* end(); - explicit operator fmt::string_view() const { return "foo"; } - explicit operator std::string_view() const { return "foo"; } -}; - TEST(RangesTest, FormatStringLike) { EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like())); } @@ -636,8 +640,9 @@ struct zstring { // TODO: Fix using zstrings with FMT_STRING TEST(RangesTest, JoinSentinel) { zstring hello{"hello"}; - //EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format(FMT_STRING("{}"), hello)); - //EXPECT_EQ("h_e_l_l_o", fmt::format(FMT_STRING("{}"), fmt::join(hello, "_"))); + // EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format(FMT_STRING("{}"), + // hello)); EXPECT_EQ("h_e_l_l_o", fmt::format(FMT_STRING("{}"), + // fmt::join(hello, "_"))); } // A range that provides non-const only begin()/end() to test fmt::join handles @@ -678,19 +683,22 @@ template class noncopyable_range { const_iterator end() const { return vec.end(); } }; -//TODO: Fixme +// TODO: Fixme TEST(RangesTest, Range) { noncopyable_range w(3u, 0); /*EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), w)); - EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), noncopyable_range(3u, 0))); + EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), + noncopyable_range(3u, 0))); non_const_only_range x(3u, 0); EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), x)); - EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), non_const_only_range(3u, 0))); + EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), + non_const_only_range(3u, 0))); std::vector y(3u, 0); EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), y)); - EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), std::vector(3u, 0))); + EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), std::vector(3u, + 0))); const std::vector z(3u, 0); EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), z));*/ @@ -704,6 +712,3 @@ TEST(RangesTest, UnformattableRange) { fmt::format_context>::value)); } #endif - - - From aa65e99969a2161f4563de3b16555ade7042ed94 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 30 Nov 2020 12:49:15 -0800 Subject: [PATCH 06/34] fix ranges --- include/fmt/ranges.h | 10 +++++----- test/enforce-compiletime-test.cc | 21 ++++++--------------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 732d32094701..05ad4952c208 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -291,10 +291,10 @@ struct formatter::value>> { } out = detail::copy(formatting.delimiter, out); } - out = format_to(out, + out = vformat_to(out, detail::format_str_quoted( (formatting.add_delimiter_spaces && i > 0), v), - v); + make_format_args(v)); ++i; } @@ -366,12 +366,12 @@ struct formatter< if (formatting.add_prepostfix_space) *out++ = ' '; out = detail::copy(formatting.delimiter, out); } - out = format_to(out, + out = vformat_to(out, detail::format_str_quoted( (formatting.add_delimiter_spaces && i > 0), *it), - *it); + make_format_args(*it)); if (++i > formatting.range_length_limit) { - out = format_to(out, " ... "); + out = format_to(out, FMT_STRING(" ... ")); break; } } diff --git a/test/enforce-compiletime-test.cc b/test/enforce-compiletime-test.cc index db5f0c5687e2..27a5c561e6cf 100644 --- a/test/enforce-compiletime-test.cc +++ b/test/enforce-compiletime-test.cc @@ -316,7 +316,7 @@ TEST(ChronoTest, FormatFullSpecs) { TEST(ChronoTest, FormatSimpleQq) { typedef std::chrono::duration fs; - // EXPECT_EQ("1.234 s", fmt::format(FMT_STRING("{:%Q %q}"), fs(1.234))); + EXPECT_EQ("1.234 s", fmt::format(FMT_STRING("{:%Q %q}"), fs(1.234))); typedef std::chrono::duration fms; EXPECT_EQ("1.234 ms", fmt::format(FMT_STRING("{:%Q %q}"), fms(1.234))); typedef std::chrono::duration ds; @@ -404,13 +404,6 @@ TEST(ColorsTest, FormatToOutAcceptsTextStyle) { "\x1b[38;2;255;020;030mrgb(255,20,30)123\x1b[0m"); } -// Formatting library for C++ - std::ostream support tests -// -// Copyright (c) 2012 - present, Victor Zverovich -// All rights reserved. -// -// For the license information refer to format.h. - struct test {}; // Test that there is no issues with specializations when fmt/ostream.h is @@ -637,12 +630,11 @@ struct zstring { zstring_sentinel end() const { return {}; } }; -// TODO: Fix using zstrings with FMT_STRING TEST(RangesTest, JoinSentinel) { zstring hello{"hello"}; - // EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format(FMT_STRING("{}"), - // hello)); EXPECT_EQ("h_e_l_l_o", fmt::format(FMT_STRING("{}"), - // fmt::join(hello, "_"))); + EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format(FMT_STRING("{}"), + hello)); EXPECT_EQ("h_e_l_l_o", fmt::format(FMT_STRING("{}"), + fmt::join(hello, "_"))); } // A range that provides non-const only begin()/end() to test fmt::join handles @@ -683,10 +675,9 @@ template class noncopyable_range { const_iterator end() const { return vec.end(); } }; -// TODO: Fixme TEST(RangesTest, Range) { noncopyable_range w(3u, 0); - /*EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), w)); + EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), w)); EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), noncopyable_range(3u, 0))); @@ -701,7 +692,7 @@ TEST(RangesTest, Range) { 0))); const std::vector z(3u, 0); - EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), z));*/ + EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), z)); } #if !FMT_MSC_VER || FMT_MSC_VER >= 1927 From 54d6d5bc846d1afd81b71d2d9e9aa489f71a4079 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 30 Nov 2020 13:17:48 -0800 Subject: [PATCH 07/34] fix gcc test --- include/fmt/ranges.h | 12 ++++++------ test/enforce-compiletime-test.cc | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 05ad4952c208..b1bd0efc24ae 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -292,9 +292,9 @@ struct formatter::value>> { out = detail::copy(formatting.delimiter, out); } out = vformat_to(out, - detail::format_str_quoted( - (formatting.add_delimiter_spaces && i > 0), v), - make_format_args(v)); + to_string_view(detail::format_str_quoted( + (formatting.add_delimiter_spaces && i > 0), v)), + make_format_args(v)); ++i; } @@ -367,9 +367,9 @@ struct formatter< out = detail::copy(formatting.delimiter, out); } out = vformat_to(out, - detail::format_str_quoted( - (formatting.add_delimiter_spaces && i > 0), *it), - make_format_args(*it)); + to_string_view(detail::format_str_quoted( + (formatting.add_delimiter_spaces && i > 0), *it)), + make_format_args(*it)); if (++i > formatting.range_length_limit) { out = format_to(out, FMT_STRING(" ... ")); break; diff --git a/test/enforce-compiletime-test.cc b/test/enforce-compiletime-test.cc index 27a5c561e6cf..6bda4ab7ff3b 100644 --- a/test/enforce-compiletime-test.cc +++ b/test/enforce-compiletime-test.cc @@ -114,7 +114,7 @@ TEST(FormatTest, CustomFormatCompileTimeString) { EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), const_answer)); } -#if FMT_USE_USER_DEFINED_LITERALS +#if FMT_USE_UDL_TEMPLATE // Passing user-defined literals directly to EXPECT_EQ causes problems // with macro argument stringification (#) on some versions of GCC. // Workaround: Assing the UDL result to a variable before the macro. @@ -128,7 +128,7 @@ TEST(LiteralsTest, Format) { EXPECT_EQ(L"abc1", udl_format_w); } -#endif // FMT_USE_USER_DEFINED_LITERALS +#endif // FMT_USE_UDL_TEMPLATE enum TestEnum { A }; From e805e8ee2909aa4bba6560cf9e397c8bf3a35462 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 30 Nov 2020 14:03:47 -0800 Subject: [PATCH 08/34] workaround MSVC bug --- include/fmt/chrono.h | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 98eac2efde90..03fb44644598 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -769,7 +769,13 @@ template ::value)> OutputIt format_duration_value(OutputIt out, Rep val, int) { static FMT_CONSTEXPR_DECL const Char format[] = {'{', '}', 0}; + +#if defined(FMT_MSC_VER) && FMT_MSC_VER < 1928 + return vformat_to(out, to_string_view(format), + make_format_args>(val)); +#else return format_to(out, FMT_STRING(format), val); +#endif } template = 0) return format_to(out, FMT_STRING(pr_f), val, precision); + if (precision >= 0) { +#if defined(FMT_MSC_VER) && FMT_MSC_VER < 1928 + return vformat_to(out, to_string_view(pr_f), + make_format_args>(val, precision)); +#else + return format_to(out, FMT_STRING(pr_f), val, precision); +#endif + } static FMT_CONSTEXPR_DECL const Char fp_f[] = {'{', ':', 'g', '}', 0}; + +#if defined(FMT_MSC_VER) && FMT_MSC_VER < 1928 + return vformat_to(out, to_string_view(fp_f), + make_format_args>(val)); +#else return format_to(out, FMT_STRING(fp_f), val); +#endif } template @@ -800,11 +819,23 @@ OutputIt format_duration_unit(OutputIt out) { if (const char* unit = get_units()) return copy_unit(string_view(unit), out, Char()); static FMT_CONSTEXPR_DECL const Char num_f[] = {'[', '{', '}', ']', 's', 0}; - if (const_check(Period::den == 1)) + if (const_check(Period::den == 1)) { +#if defined(FMT_MSC_VER) && FMT_MSC_VER < 1928 + return vformat_to(out, to_string_view(num_f), + make_format_args>(Period::num)); +#else return format_to(out, FMT_STRING(num_f), Period::num); +#endif + } static FMT_CONSTEXPR_DECL const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0}; +#if defined(FMT_MSC_VER) && FMT_MSC_VER < 1928 + return vformat_to( + out, to_string_view(num_def_f), + make_format_args>(Period::num, Period::den)); +#else return format_to(out, FMT_STRING(num_def_f), Period::num, Period::den); +#endif } template Date: Mon, 30 Nov 2020 14:45:57 -0800 Subject: [PATCH 09/34] Improve docs --- doc/api.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/api.rst b/doc/api.rst index 260fd3f7caf5..57722878605a 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -165,6 +165,14 @@ functions in their ``formatter`` specializations. .. _udt: +Enforcing Compile-time Format Checks +------------------------------------ +To force the use of Compile-time checks, compile with +``FMT_ENFORCE_COMPILE_STRING`` defined. When this preprocessor definition is +given, functions accepting ``FMT_STRING`` will fail to compile with regular +strings. When this is set, runtime-checked formatting is still possible using +``fmt::vformat``, ``fmt::vprint``, etc. + Formatting User-defined Types ----------------------------- From 55600c96e941cbfcaffdf5b6346c69c4a5cdf6aa Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 30 Nov 2020 14:51:17 -0800 Subject: [PATCH 10/34] fix ranges.h --- include/fmt/ranges.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index b1bd0efc24ae..a211b6c69e2c 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -294,7 +294,7 @@ struct formatter::value>> { out = vformat_to(out, to_string_view(detail::format_str_quoted( (formatting.add_delimiter_spaces && i > 0), v)), - make_format_args(v)); + make_format_args(v)); ++i; } @@ -369,7 +369,7 @@ struct formatter< out = vformat_to(out, to_string_view(detail::format_str_quoted( (formatting.add_delimiter_spaces && i > 0), *it)), - make_format_args(*it)); + make_format_args(*it)); if (++i > formatting.range_length_limit) { out = format_to(out, FMT_STRING(" ... ")); break; From 35b94fa9b36e6eb86d0df65f9019ae7cb2ade605 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 30 Nov 2020 15:02:51 -0800 Subject: [PATCH 11/34] increase MSVC version restriction --- include/fmt/chrono.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 03fb44644598..aeadb23a8b66 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -770,7 +770,8 @@ template >(val)); #else @@ -784,7 +785,7 @@ OutputIt format_duration_value(OutputIt out, Rep val, int precision) { static FMT_CONSTEXPR_DECL const Char pr_f[] = {'{', ':', '.', '{', '}', 'f', '}', 0}; if (precision >= 0) { -#if defined(FMT_MSC_VER) && FMT_MSC_VER < 1928 +#if defined(FMT_MSC_VER) && FMT_MSC_VER <= 1928 return vformat_to(out, to_string_view(pr_f), make_format_args>(val, precision)); #else @@ -793,7 +794,7 @@ OutputIt format_duration_value(OutputIt out, Rep val, int precision) { } static FMT_CONSTEXPR_DECL const Char fp_f[] = {'{', ':', 'g', '}', 0}; -#if defined(FMT_MSC_VER) && FMT_MSC_VER < 1928 +#if defined(FMT_MSC_VER) && FMT_MSC_VER <= 1928 return vformat_to(out, to_string_view(fp_f), make_format_args>(val)); #else @@ -820,7 +821,7 @@ OutputIt format_duration_unit(OutputIt out) { return copy_unit(string_view(unit), out, Char()); static FMT_CONSTEXPR_DECL const Char num_f[] = {'[', '{', '}', ']', 's', 0}; if (const_check(Period::den == 1)) { -#if defined(FMT_MSC_VER) && FMT_MSC_VER < 1928 +#if defined(FMT_MSC_VER) && FMT_MSC_VER <= 1928 return vformat_to(out, to_string_view(num_f), make_format_args>(Period::num)); #else @@ -829,7 +830,7 @@ OutputIt format_duration_unit(OutputIt out) { } static FMT_CONSTEXPR_DECL const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0}; -#if defined(FMT_MSC_VER) && FMT_MSC_VER < 1928 +#if defined(FMT_MSC_VER) && FMT_MSC_VER <= 1928 return vformat_to( out, to_string_view(num_def_f), make_format_args>(Period::num, Period::den)); From 1e034bddf4167628affe8069a122e7aed54142a6 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 30 Nov 2020 17:20:16 -0800 Subject: [PATCH 12/34] try a fix for strange gcc failures --- include/fmt/ranges.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index a211b6c69e2c..91045b58e524 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -292,8 +292,8 @@ struct formatter::value>> { out = detail::copy(formatting.delimiter, out); } out = vformat_to(out, - to_string_view(detail::format_str_quoted( - (formatting.add_delimiter_spaces && i > 0), v)), + detail::format_str_quoted( + (formatting.add_delimiter_spaces && i > 0), v), make_format_args(v)); ++i; } @@ -367,8 +367,8 @@ struct formatter< out = detail::copy(formatting.delimiter, out); } out = vformat_to(out, - to_string_view(detail::format_str_quoted( - (formatting.add_delimiter_spaces && i > 0), *it)), + detail::format_str_quoted( + (formatting.add_delimiter_spaces && i > 0), *it), make_format_args(*it)); if (++i > formatting.range_length_limit) { out = format_to(out, FMT_STRING(" ... ")); From 900adebb4b004605ee1ab7561657d2e3ec90d855 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 30 Nov 2020 17:58:11 -0800 Subject: [PATCH 13/34] fix gcc pedantic error --- include/fmt/ranges.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 91045b58e524..c4db83f66aa7 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -371,7 +371,7 @@ struct formatter< (formatting.add_delimiter_spaces && i > 0), *it), make_format_args(*it)); if (++i > formatting.range_length_limit) { - out = format_to(out, FMT_STRING(" ... ")); + out = format_to(out, FMT_STRING("{}"), " ... "); break; } } From 7f1c0411d632ef01ed4abe1a7d8acbc2af306f06 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 30 Nov 2020 19:09:08 -0800 Subject: [PATCH 14/34] fix gcc errors --- test/enforce-compiletime-test.cc | 71 +++++++++++++++++--------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/test/enforce-compiletime-test.cc b/test/enforce-compiletime-test.cc index 6bda4ab7ff3b..4170a93eee49 100644 --- a/test/enforce-compiletime-test.cc +++ b/test/enforce-compiletime-test.cc @@ -345,53 +345,57 @@ TEST(ChronoTest, UnsignedDuration) { TEST(ColorsTest, ColorsPrint) { EXPECT_WRITE( stdout, - fmt::print(fg(fmt::rgb(255, 20, 30)), FMT_STRING("rgb(255,20,30)")), + fmt::print(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), "rgb(255,20,30)"), "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); EXPECT_WRITE(stdout, fmt::print(fg(fmt::color::blue) | bg(fmt::color::red), - FMT_STRING("two color")), + FMT_STRING("{}"), "two color"), "\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m"); - EXPECT_WRITE(stdout, fmt::print(fmt::emphasis::bold, FMT_STRING("bold")), + EXPECT_WRITE(stdout, + fmt::print(fmt::emphasis::bold, FMT_STRING("{}"), "bold"), "\x1b[1mbold\x1b[0m"); EXPECT_WRITE(stdout, fmt::print(fg(fmt::color::blue) | fmt::emphasis::bold, - FMT_STRING("blue/bold")), + FMT_STRING("{}"), "blue/bold"), "\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m"); - EXPECT_WRITE(stdout, fmt::print(fmt::text_style(), FMT_STRING("hi")), "hi"); - EXPECT_WRITE(stdout, - fmt::print(fg(fmt::terminal_color::red), FMT_STRING("tred")), - "\x1b[31mtred\x1b[0m"); - EXPECT_WRITE(stdout, - fmt::print(bg(fmt::terminal_color::cyan), FMT_STRING("tcyan")), - "\x1b[46mtcyan\x1b[0m"); + EXPECT_WRITE(stdout, fmt::print(fmt::text_style(), FMT_STRING("{}"), "hi"), + "hi"); + EXPECT_WRITE( + stdout, + fmt::print(fg(fmt::terminal_color::red), FMT_STRING("{}"), "tred"), + "\x1b[31mtred\x1b[0m"); + EXPECT_WRITE( + stdout, + fmt::print(bg(fmt::terminal_color::cyan), FMT_STRING("{}"), "tcyan"), + "\x1b[46mtcyan\x1b[0m"); EXPECT_WRITE(stdout, fmt::print(bg(fmt::terminal_color::bright_magenta), - FMT_STRING("tbmagenta")), + FMT_STRING("{}"), "tbmagenta"), "\x1b[105mtbmagenta\x1b[0m"); } TEST(ColorsTest, Format) { - EXPECT_EQ( - "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m", - fmt::format(fg(fmt::rgb(255, 20, 30)), FMT_STRING("rgb(255,20,30)"))); + EXPECT_EQ("\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m", + fmt::format(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), + "rgb(255,20,30)")); EXPECT_EQ("\x1b[38;2;000;000;255mblue\x1b[0m", - fmt::format(fg(fmt::color::blue), FMT_STRING("blue"))); + fmt::format(fg(fmt::color::blue), FMT_STRING("{}"), "blue")); EXPECT_EQ("\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m", fmt::format(fg(fmt::color::blue) | bg(fmt::color::red), - FMT_STRING("two color"))); + FMT_STRING("{}"), "two color")); EXPECT_EQ("\x1b[1mbold\x1b[0m", - fmt::format(fmt::emphasis::bold, FMT_STRING("bold"))); + fmt::format(fmt::emphasis::bold, FMT_STRING("{}"), "bold")); EXPECT_EQ("\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m", fmt::format(fg(fmt::color::blue) | fmt::emphasis::bold, - FMT_STRING("blue/bold"))); - EXPECT_EQ("hi", fmt::format(fmt::text_style(), FMT_STRING("hi"))); - EXPECT_EQ("\x1b[31mtred\x1b[0m", - fmt::format(fg(fmt::terminal_color::red), FMT_STRING("tred"))); - EXPECT_EQ("\x1b[46mtcyan\x1b[0m", - fmt::format(bg(fmt::terminal_color::cyan), FMT_STRING("tcyan"))); + FMT_STRING("{}"), "blue/bold")); + EXPECT_EQ("hi", fmt::format(fmt::text_style(), FMT_STRING("{}"), "hi")); + EXPECT_EQ("\x1b[31mtred\x1b[0m", fmt::format(fg(fmt::terminal_color::red), + FMT_STRING("{}"), "tred")); + EXPECT_EQ("\x1b[46mtcyan\x1b[0m", fmt::format(bg(fmt::terminal_color::cyan), + FMT_STRING("{}"), "tcyan")); EXPECT_EQ("\x1b[105mtbmagenta\x1b[0m", fmt::format(bg(fmt::terminal_color::bright_magenta), - FMT_STRING("tbmagenta"))); + FMT_STRING("{}"), "tbmagenta")); } TEST(ColorsTest, FormatToOutAcceptsTextStyle) { @@ -632,9 +636,8 @@ struct zstring { TEST(RangesTest, JoinSentinel) { zstring hello{"hello"}; - EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format(FMT_STRING("{}"), - hello)); EXPECT_EQ("h_e_l_l_o", fmt::format(FMT_STRING("{}"), - fmt::join(hello, "_"))); + EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format(FMT_STRING("{}"), hello)); + EXPECT_EQ("h_e_l_l_o", fmt::format(FMT_STRING("{}"), fmt::join(hello, "_"))); } // A range that provides non-const only begin()/end() to test fmt::join handles @@ -678,18 +681,18 @@ template class noncopyable_range { TEST(RangesTest, Range) { noncopyable_range w(3u, 0); EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), w)); - EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), - noncopyable_range(3u, 0))); + EXPECT_EQ("{0, 0, 0}", + fmt::format(FMT_STRING("{}"), noncopyable_range(3u, 0))); non_const_only_range x(3u, 0); EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), x)); - EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), - non_const_only_range(3u, 0))); + EXPECT_EQ("{0, 0, 0}", + fmt::format(FMT_STRING("{}"), non_const_only_range(3u, 0))); std::vector y(3u, 0); EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), y)); - EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), std::vector(3u, - 0))); + EXPECT_EQ("{0, 0, 0}", + fmt::format(FMT_STRING("{}"), std::vector(3u, 0))); const std::vector z(3u, 0); EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), z)); From d343e4ba7b50c5cc8a56956112ac1a8c9757ea62 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 30 Nov 2020 19:23:41 -0800 Subject: [PATCH 15/34] fix MSVC version check #if statements --- include/fmt/chrono.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index aeadb23a8b66..7e28e199d928 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -770,8 +770,8 @@ template >(val)); #else @@ -785,7 +785,7 @@ OutputIt format_duration_value(OutputIt out, Rep val, int precision) { static FMT_CONSTEXPR_DECL const Char pr_f[] = {'{', ':', '.', '{', '}', 'f', '}', 0}; if (precision >= 0) { -#if defined(FMT_MSC_VER) && FMT_MSC_VER <= 1928 +#if FMT_MSC_VER && FMT_MSC_VER <= 1928 return vformat_to(out, to_string_view(pr_f), make_format_args>(val, precision)); #else @@ -794,7 +794,7 @@ OutputIt format_duration_value(OutputIt out, Rep val, int precision) { } static FMT_CONSTEXPR_DECL const Char fp_f[] = {'{', ':', 'g', '}', 0}; -#if defined(FMT_MSC_VER) && FMT_MSC_VER <= 1928 +#if FMT_MSC_VER && FMT_MSC_VER <= 1928 return vformat_to(out, to_string_view(fp_f), make_format_args>(val)); #else @@ -821,7 +821,7 @@ OutputIt format_duration_unit(OutputIt out) { return copy_unit(string_view(unit), out, Char()); static FMT_CONSTEXPR_DECL const Char num_f[] = {'[', '{', '}', ']', 's', 0}; if (const_check(Period::den == 1)) { -#if defined(FMT_MSC_VER) && FMT_MSC_VER <= 1928 +#if FMT_MSC_VER && FMT_MSC_VER <= 1928 return vformat_to(out, to_string_view(num_f), make_format_args>(Period::num)); #else @@ -830,7 +830,7 @@ OutputIt format_duration_unit(OutputIt out) { } static FMT_CONSTEXPR_DECL const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0}; -#if defined(FMT_MSC_VER) && FMT_MSC_VER <= 1928 +#if FMT_MSC_VER && FMT_MSC_VER <= 1928 return vformat_to( out, to_string_view(num_def_f), make_format_args>(Period::num, Period::den)); From c4c83517a4e3690ad867fa546f86f7df4b8157d4 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Thu, 3 Dec 2020 20:20:26 -0800 Subject: [PATCH 16/34] trim down compiletime test --- test/enforce-compiletime-test.cc | 569 ++----------------------------- 1 file changed, 22 insertions(+), 547 deletions(-) diff --git a/test/enforce-compiletime-test.cc b/test/enforce-compiletime-test.cc index 4170a93eee49..7f05d29a5077 100644 --- a/test/enforce-compiletime-test.cc +++ b/test/enforce-compiletime-test.cc @@ -28,90 +28,19 @@ #undef index #include "gmock.h" -#include "gtest-extra.h" -#include "mock-allocator.h" -#include "util.h" - -#undef ERROR - -using fmt::basic_memory_buffer; -using fmt::format; -using fmt::format_error; -using fmt::memory_buffer; -using fmt::string_view; -using fmt::wmemory_buffer; -using fmt::wstring_view; -using fmt::detail::max_value; - -using testing::Return; -using testing::StrictMock; - -class Answer {}; - -FMT_BEGIN_NAMESPACE -template <> struct formatter : formatter { - template - auto format(Answer, FormatContext& ctx) -> decltype(ctx.out()) { - return formatter::format(42, ctx); - } -}; -FMT_END_NAMESPACE - -struct string_like { - const char* begin(); - const char* end(); -#if defined(FMT_USE_STRING_VIEW) - explicit operator fmt::string_view() const { return "foo"; } - explicit operator std::string_view() const { return "foo"; } -#endif -}; - -fmt::string_view to_string_view(string_like) { return "foo"; } - -constexpr char with_null[3] = {'{', '}', '\0'}; -constexpr char no_null[2] = {'{', '}'}; -static FMT_CONSTEXPR_DECL const char static_with_null[3] = {'{', '}', '\0'}; -static FMT_CONSTEXPR_DECL const wchar_t static_with_null_wide[3] = {'{', '}', - '\0'}; -static FMT_CONSTEXPR_DECL const char static_no_null[2] = {'{', '}'}; -static FMT_CONSTEXPR_DECL const wchar_t static_no_null_wide[2] = {'{', '}'}; -TEST(FormatTest, CompileTimeString) { +TEST(CompileTimeTest, FormatApi) { EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42)); EXPECT_EQ(L"42", fmt::format(FMT_STRING(L"{}"), 42)); - EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like())); - - (void)static_with_null; - (void)static_with_null_wide; - (void)static_no_null; - (void)static_no_null_wide; -#if !defined(_MSC_VER) - EXPECT_EQ("42", fmt::format(FMT_STRING(static_with_null), 42)); - EXPECT_EQ(L"42", fmt::format(FMT_STRING(static_with_null_wide), 42)); - EXPECT_EQ("42", fmt::format(FMT_STRING(static_no_null), 42)); - EXPECT_EQ(L"42", fmt::format(FMT_STRING(static_no_null_wide), 42)); -#endif - - (void)with_null; - (void)no_null; -#if __cplusplus >= 201703L - EXPECT_EQ("42", fmt::format(FMT_STRING(with_null), 42)); - EXPECT_EQ("42", fmt::format(FMT_STRING(no_null), 42)); -#endif -#if defined(FMT_USE_STRING_VIEW) && __cplusplus >= 201703L - EXPECT_EQ("42", fmt::format(FMT_STRING(std::string_view("{}")), 42)); - EXPECT_EQ(L"42", fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42)); -#endif -} + EXPECT_EQ("42", fmt::to_string(42)); + EXPECT_EQ(L"42", fmt::to_wstring(42)); -TEST(FormatTest, CustomFormatCompileTimeString) { - EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), Answer())); - Answer answer; - EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), answer)); - char buf[10] = {}; - fmt::format_to(buf, FMT_STRING("{}"), answer); - const Answer const_answer = Answer(); - EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), const_answer)); + std::list out; + fmt::format_to(std::back_inserter(out), FMT_STRING("{}"), 42); + EXPECT_EQ("42", std::string(out.begin(), out.end())); + std::stringstream s; + fmt::format_to(std::ostream_iterator(s), FMT_STRING("{}"), 42); + EXPECT_EQ("42", s.str()); } #if FMT_USE_UDL_TEMPLATE @@ -121,102 +50,34 @@ TEST(FormatTest, CustomFormatCompileTimeString) { using namespace fmt::literals; -TEST(LiteralsTest, Format) { +TEST(CompileTimeTest, Literals) { auto udl_format = "{}c{}"_format("ab", 1); EXPECT_EQ("abc1", udl_format); auto udl_format_w = L"{}c{}"_format(L"ab", 1); EXPECT_EQ(L"abc1", udl_format_w); } +#endif -#endif // FMT_USE_UDL_TEMPLATE - -enum TestEnum { A }; - -namespace adl_test { -namespace fmt { -namespace detail { -struct foo {}; -template void write(OutputIt, foo) = delete; -} // namespace detail -} // namespace fmt -} // namespace adl_test - -FMT_BEGIN_NAMESPACE -template <> -struct formatter : formatter { - template - auto format(adl_test::fmt::detail::foo, FormatContext& ctx) - -> decltype(ctx.out()) { - return formatter::format("foo", ctx); - } -}; -FMT_END_NAMESPACE - -TEST(FormatTest, ToString) { - EXPECT_EQ("42", fmt::to_string(42)); - EXPECT_EQ("0x1234", fmt::to_string(reinterpret_cast(0x1234))); - EXPECT_EQ("foo", fmt::to_string(adl_test::fmt::detail::foo())); -} - -TEST(FormatTest, ToWString) { EXPECT_EQ(L"42", fmt::to_wstring(42)); } - -TEST(FormatTest, OutputIterators) { - std::list out; - fmt::format_to(std::back_inserter(out), FMT_STRING("{}"), 42); - EXPECT_EQ("42", std::string(out.begin(), out.end())); - std::stringstream s; - fmt::format_to(std::ostream_iterator(s), FMT_STRING("{}"), 42); - EXPECT_EQ("42", s.str()); -} - -TEST(FormatTest, FormattedSize) { +TEST(CompileTimeTest, FormattedSize) { EXPECT_EQ(2u, fmt::formatted_size(FMT_STRING("{}"), 42)); } -TEST(FormatTest, FormatTo) { +TEST(CompileTimeTest, FormatTo) { std::vector v; fmt::format_to(std::back_inserter(v), FMT_STRING("{}"), "foo"); EXPECT_EQ(fmt::string_view(v.data(), v.size()), "foo"); } -TEST(FormatTest, FormatToN) { +TEST(CompileTimeTest, FormatToN) { char buffer[4]; buffer[3] = 'x'; auto result = fmt::format_to_n(buffer, 3, FMT_STRING("{}"), 12345); EXPECT_EQ(5u, result.size); EXPECT_EQ(buffer + 3, result.out); EXPECT_EQ("123x", fmt::string_view(buffer, 4)); - - result = fmt::format_to_n(buffer, 3, FMT_STRING("{:s}"), "foobar"); - EXPECT_EQ(6u, result.size); - EXPECT_EQ(buffer + 3, result.out); - EXPECT_EQ("foox", fmt::string_view(buffer, 4)); - - buffer[0] = 'x'; - buffer[1] = 'x'; - buffer[2] = 'x'; - result = fmt::format_to_n(buffer, 3, FMT_STRING("{}"), 'A'); - EXPECT_EQ(1u, result.size); - EXPECT_EQ(buffer + 1, result.out); - EXPECT_EQ("Axxx", fmt::string_view(buffer, 4)); - - result = fmt::format_to_n(buffer, 3, FMT_STRING("{}{} "), 'B', 'C'); - EXPECT_EQ(3u, result.size); - EXPECT_EQ(buffer + 3, result.out); - EXPECT_EQ("BC x", fmt::string_view(buffer, 4)); - - result = fmt::format_to_n(buffer, 4, FMT_STRING("{}"), "ABCDE"); - EXPECT_EQ(5u, result.size); - EXPECT_EQ("ABCD", fmt::string_view(buffer, 4)); - - buffer[3] = 'x'; - result = - fmt::format_to_n(buffer, 3, FMT_STRING("{}"), std::string(1000, '*')); - EXPECT_EQ(1000u, result.size); - EXPECT_EQ("***x", fmt::string_view(buffer, 4)); } -TEST(FormatTest, WideFormatToN) { +TEST(CompileTimeTest, WideFormatToN) { wchar_t buffer[4]; buffer[3] = L'x'; auto result = fmt::format_to_n(buffer, 3, FMT_STRING(L"{}"), 12345); @@ -257,148 +118,38 @@ struct test_output_iterator { char& operator*() { return *data; } }; -TEST(FormatTest, FormatToNOutputIterator) { +TEST(CompileTimeTest, FormatToNOutputIterator) { char buf[10] = {}; fmt::format_to_n(test_output_iterator{buf}, 10, FMT_STRING("{}"), 42); EXPECT_STREQ(buf, "42"); } -TEST(FormatTest, VFormatTo) { - typedef fmt::format_context context; - fmt::basic_format_arg arg = fmt::detail::make_arg(42); - fmt::basic_format_args args(&arg, 1); - std::string s; - fmt::vformat_to(std::back_inserter(s), FMT_STRING("{}"), args); - EXPECT_EQ("42", s); - - typedef fmt::wformat_context wcontext; - fmt::basic_format_arg warg = fmt::detail::make_arg(42); - fmt::basic_format_args wargs(&warg, 1); - std::wstring w; - fmt::vformat_to(std::back_inserter(w), FMT_STRING(L"{}"), wargs); - EXPECT_EQ(L"42", w); -} - -template static std::string FmtToString(const T& t) { - return fmt::format(FMT_STRING("{}"), t); -} - -TEST(FormatTest, FmtStringInTemplate) { - EXPECT_EQ(FmtToString(1), "1"); - EXPECT_EQ(FmtToString(0), "0"); -} - #ifndef FMT_STATIC_THOUSANDS_SEPARATOR -TEST(ChronoTest, FormatDefault) { +TEST(CompileTimeTest, Chrono) { EXPECT_EQ("42s", fmt::format(FMT_STRING("{}"), std::chrono::seconds(42))); } -TEST(ChronoTest, FormatWide) { +TEST(CompileTimeTest, ChronoWide) { EXPECT_EQ(L"42s", fmt::format(FMT_STRING(L"{}"), std::chrono::seconds(42))); } -typedef std::chrono::duration dms; - -TEST(ChronoTest, FormatDefaultFP) { - EXPECT_EQ("1.234ms", fmt::format(FMT_STRING("{}"), dms(1.234))); -} - -TEST(ChronoTest, FormatPrecision) { - EXPECT_EQ("1.2ms", fmt::format(FMT_STRING("{:.1}"), dms(1.234))); - EXPECT_EQ("1.23ms", fmt::format(FMT_STRING("{:.{}}"), dms(1.234), 2)); -} - -TEST(ChronoTest, FormatFullSpecs) { - EXPECT_EQ("1.2ms ", fmt::format(FMT_STRING("{:6.1}"), dms(1.234))); - EXPECT_EQ(" 1.2ms ", fmt::format(FMT_STRING("{:^{}.{}}"), dms(1.234), 7, 1)); -} - -TEST(ChronoTest, FormatSimpleQq) { - typedef std::chrono::duration fs; - EXPECT_EQ("1.234 s", fmt::format(FMT_STRING("{:%Q %q}"), fs(1.234))); - typedef std::chrono::duration fms; - EXPECT_EQ("1.234 ms", fmt::format(FMT_STRING("{:%Q %q}"), fms(1.234))); - typedef std::chrono::duration ds; - EXPECT_EQ("1.234 s", fmt::format(FMT_STRING("{:%Q %q}"), ds(1.234))); - EXPECT_EQ("1.234 ms", fmt::format(FMT_STRING("{:%Q %q}"), dms(1.234))); -} - -TEST(ChronoTest, FormatPrecisionQq) { - EXPECT_EQ("1.2 ms", fmt::format(FMT_STRING("{:.1%Q %q}"), dms(1.234))); - EXPECT_EQ("1.23 ms", fmt::format(FMT_STRING("{:.{}%Q %q}"), dms(1.234), 2)); -} - -TEST(ChronoTest, FormatFullSpecsQq) { - EXPECT_EQ("1.2 ms ", fmt::format(FMT_STRING("{:7.1%Q %q}"), dms(1.234))); - EXPECT_EQ(" 1.2 ms ", - fmt::format(FMT_STRING("{:^{}.{}%Q %q}"), dms(1.234), 8, 1)); -} - -TEST(ChronoTest, UnsignedDuration) { - EXPECT_EQ("42s", - fmt::format(FMT_STRING("{}"), std::chrono::duration(42))); -} - #endif // FMT_STATIC_THOUSANDS_SEPARATOR -TEST(ColorsTest, ColorsPrint) { +TEST(CompileTimeTest, PrintTextStyle) { EXPECT_WRITE( stdout, fmt::print(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), "rgb(255,20,30)"), "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); - EXPECT_WRITE(stdout, - fmt::print(fg(fmt::color::blue) | bg(fmt::color::red), - FMT_STRING("{}"), "two color"), - "\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m"); - EXPECT_WRITE(stdout, - fmt::print(fmt::emphasis::bold, FMT_STRING("{}"), "bold"), - "\x1b[1mbold\x1b[0m"); - EXPECT_WRITE(stdout, - fmt::print(fg(fmt::color::blue) | fmt::emphasis::bold, - FMT_STRING("{}"), "blue/bold"), - "\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m"); - EXPECT_WRITE(stdout, fmt::print(fmt::text_style(), FMT_STRING("{}"), "hi"), - "hi"); - EXPECT_WRITE( - stdout, - fmt::print(fg(fmt::terminal_color::red), FMT_STRING("{}"), "tred"), - "\x1b[31mtred\x1b[0m"); - EXPECT_WRITE( - stdout, - fmt::print(bg(fmt::terminal_color::cyan), FMT_STRING("{}"), "tcyan"), - "\x1b[46mtcyan\x1b[0m"); - EXPECT_WRITE(stdout, - fmt::print(bg(fmt::terminal_color::bright_magenta), - FMT_STRING("{}"), "tbmagenta"), - "\x1b[105mtbmagenta\x1b[0m"); } -TEST(ColorsTest, Format) { +TEST(CompileTimeTest, FormatTextStyle) { EXPECT_EQ("\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m", fmt::format(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), "rgb(255,20,30)")); - EXPECT_EQ("\x1b[38;2;000;000;255mblue\x1b[0m", - fmt::format(fg(fmt::color::blue), FMT_STRING("{}"), "blue")); - EXPECT_EQ("\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m", - fmt::format(fg(fmt::color::blue) | bg(fmt::color::red), - FMT_STRING("{}"), "two color")); - EXPECT_EQ("\x1b[1mbold\x1b[0m", - fmt::format(fmt::emphasis::bold, FMT_STRING("{}"), "bold")); - EXPECT_EQ("\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m", - fmt::format(fg(fmt::color::blue) | fmt::emphasis::bold, - FMT_STRING("{}"), "blue/bold")); - EXPECT_EQ("hi", fmt::format(fmt::text_style(), FMT_STRING("{}"), "hi")); - EXPECT_EQ("\x1b[31mtred\x1b[0m", fmt::format(fg(fmt::terminal_color::red), - FMT_STRING("{}"), "tred")); - EXPECT_EQ("\x1b[46mtcyan\x1b[0m", fmt::format(bg(fmt::terminal_color::cyan), - FMT_STRING("{}"), "tcyan")); - EXPECT_EQ("\x1b[105mtbmagenta\x1b[0m", - fmt::format(bg(fmt::terminal_color::bright_magenta), - FMT_STRING("{}"), "tbmagenta")); } -TEST(ColorsTest, FormatToOutAcceptsTextStyle) { +TEST(CompileTimeTest, FormatToOutAcceptsTextStyle) { fmt::text_style ts = fg(fmt::rgb(255, 20, 30)); std::string out; fmt::format_to(std::back_inserter(out), ts, @@ -410,219 +161,10 @@ TEST(ColorsTest, FormatToOutAcceptsTextStyle) { struct test {}; -// Test that there is no issues with specializations when fmt/ostream.h is -// included after fmt/format.h. -namespace fmt { -template <> struct formatter : formatter { - template - typename FormatContext::iterator format(const test&, FormatContext& ctx) { - return formatter::format(42, ctx); - } -}; -} // namespace fmt - -struct EmptyTest {}; -static std::ostream& operator<<(std::ostream& os, EmptyTest) { - return os << ""; -} - -TEST(OStreamTest, EmptyCustomOutput) { - EXPECT_EQ("", fmt::format(FMT_STRING("{}"), EmptyTest())); -} - -TEST(OStreamTest, Print) { - std::ostringstream os; - fmt::print(os, FMT_STRING("Don't {}!"), "panic"); - EXPECT_EQ("Don't panic!", os.str()); - std::wostringstream wos; - fmt::print(wos, FMT_STRING(L"Don't {}!"), L"panic"); - EXPECT_EQ(L"Don't panic!", wos.str()); -} - -TEST(OStreamTest, Join) { - int v[3] = {1, 2, 3}; - EXPECT_EQ("1, 2, 3", - fmt::format(FMT_STRING("{}"), fmt::join(v, v + 3, ", "))); -} - -namespace fmt_test { -struct ABC {}; - -template Output& operator<<(Output& out, ABC) { - out << "ABC"; - return out; -} -} // namespace fmt_test - -template struct TestTemplate {}; - -template -std::ostream& operator<<(std::ostream& os, TestTemplate) { - return os << 1; -} - -namespace fmt { -template struct formatter> : formatter { - template - typename FormatContext::iterator format(TestTemplate, FormatContext& ctx) { - return formatter::format(2, ctx); - } -}; -} // namespace fmt - -#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 407 -TEST(OStreamTest, Template) { - EXPECT_EQ("2", fmt::format(FMT_STRING("{}"), TestTemplate())); -} - -TEST(OStreamTest, FormatToN) { - char buffer[4]; - buffer[3] = 'x'; - auto result = fmt::format_to_n(buffer, 3, FMT_STRING("{}"), fmt_test::ABC()); - EXPECT_EQ(3u, result.size); - EXPECT_EQ(buffer + 3, result.out); - EXPECT_EQ("ABCx", fmt::string_view(buffer, 4)); -} -#endif - -TEST(OStreamTest, CompileTimeString) { - EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42)); -} - -TEST(OStreamTest, ToString) { - EXPECT_EQ("ABC", fmt::to_string(fmt_test::ABC())); -} - // Check if 'if constexpr' is supported. #if (__cplusplus > 201402L) || \ (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) -TEST(RangesTest, FormatArray) { - int32_t ia[] = {1, 2, 3, 5, 7, 11}; - auto iaf = fmt::format(FMT_STRING("{}"), ia); - EXPECT_EQ("{1, 2, 3, 5, 7, 11}", iaf); -} - -TEST(RangesTest, Format2dArray) { - int32_t ia[][2] = {{1, 2}, {3, 5}, {7, 11}}; - auto iaf = fmt::format(FMT_STRING("{}"), ia); - EXPECT_EQ("{{1, 2}, {3, 5}, {7, 11}}", iaf); -} - -TEST(RangesTest, FormatVector) { - std::vector iv{1, 2, 3, 5, 7, 11}; - auto ivf = fmt::format(FMT_STRING("{}"), iv); - EXPECT_EQ("{1, 2, 3, 5, 7, 11}", ivf); -} - -TEST(RangesTest, FormatVector2) { - std::vector> ivv{{1, 2}, {3, 5}, {7, 11}}; - auto ivf = fmt::format(FMT_STRING("{}"), ivv); - EXPECT_EQ("{{1, 2}, {3, 5}, {7, 11}}", ivf); -} - -TEST(RangesTest, FormatMap) { - std::map simap{{"one", 1}, {"two", 2}}; - EXPECT_EQ("{(\"one\", 1), (\"two\", 2)}", - fmt::format(FMT_STRING("{}"), simap)); -} - -TEST(RangesTest, FormatPair) { - std::pair pa1{42, 1.5f}; - EXPECT_EQ("(42, 1.5)", fmt::format(FMT_STRING("{}"), pa1)); -} - -TEST(RangesTest, FormatTuple) { - std::tuple t{42, 1.5f, "this is tuple", - 'i'}; - EXPECT_EQ("(42, 1.5, \"this is tuple\", 'i')", - fmt::format(FMT_STRING("{}"), t)); - EXPECT_EQ("()", fmt::format(FMT_STRING("{}"), std::tuple<>())); -} - -TEST(RangesTest, JoinTuple) { - // Value tuple args - std::tuple t1 = std::make_tuple('a', 1, 2.0f); - EXPECT_EQ("(a, 1, 2)", fmt::format(FMT_STRING("({})"), fmt::join(t1, ", "))); - - // Testing lvalue tuple args - int x = 4; - std::tuple t2{'b', x}; - EXPECT_EQ("b + 4", fmt::format(FMT_STRING("{}"), fmt::join(t2, " + "))); - - // Empty tuple - std::tuple<> t3; - EXPECT_EQ("", fmt::format(FMT_STRING("{}"), fmt::join(t3, "|"))); - - // Single element tuple - std::tuple t4{4.0f}; - EXPECT_EQ("4", fmt::format(FMT_STRING("{}"), fmt::join(t4, "/"))); -} - -TEST(RangesTest, JoinInitializerList) { - EXPECT_EQ("1, 2, 3", - fmt::format(FMT_STRING("{}"), fmt::join({1, 2, 3}, ", "))); - EXPECT_EQ("fmt rocks !", fmt::format(FMT_STRING("{}"), - fmt::join({"fmt", "rocks", "!"}, " "))); -} - -struct my_struct { - int32_t i; - std::string str; // can throw - template decltype(auto) get() const noexcept { - if constexpr (N == 0) - return i; - else if constexpr (N == 1) - return fmt::string_view{str}; - } -}; - -template decltype(auto) get(const my_struct& s) noexcept { - return s.get(); -} - -namespace std { - -template <> struct tuple_size : std::integral_constant {}; - -template struct tuple_element { - using type = decltype(std::declval().get()); -}; - -} // namespace std - -TEST(RangesTest, FormatStruct) { - my_struct mst{13, "my struct"}; - EXPECT_EQ("(13, \"my struct\")", fmt::format(FMT_STRING("{}"), mst)); -} - -TEST(RangesTest, FormatTo) { - char buf[10]; - auto end = fmt::format_to(buf, FMT_STRING("{}"), std::vector{1, 2, 3}); - *end = '\0'; - EXPECT_STREQ(buf, "{1, 2, 3}"); -} - -struct path_like { - const path_like* begin() const; - const path_like* end() const; - - operator std::string() const; -}; - -TEST(RangesTest, PathLike) { - EXPECT_FALSE((fmt::is_range::value)); -} - -#endif // (__cplusplus > 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG > - // 201402L && _MSC_VER >= 1910) - -#ifdef FMT_USE_STRING_VIEW -TEST(RangesTest, FormatStringLike) { - EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like())); -} -#endif // FMT_USE_STRING_VIEW - struct zstring_sentinel {}; bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; } @@ -634,75 +176,8 @@ struct zstring { zstring_sentinel end() const { return {}; } }; -TEST(RangesTest, JoinSentinel) { +TEST(CompileTimeTest, JoinSentinel) { zstring hello{"hello"}; EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format(FMT_STRING("{}"), hello)); EXPECT_EQ("h_e_l_l_o", fmt::format(FMT_STRING("{}"), fmt::join(hello, "_"))); } - -// A range that provides non-const only begin()/end() to test fmt::join handles -// that -// -// Some ranges (eg those produced by range-v3's views::filter()) can cache -// information during iteration so they only provide non-const begin()/end(). -template class non_const_only_range { - private: - std::vector vec; - - public: - using const_iterator = typename ::std::vector::const_iterator; - - template - explicit non_const_only_range(Args&&... args) - : vec(::std::forward(args)...) {} - - const_iterator begin() { return vec.begin(); } - const_iterator end() { return vec.end(); } -}; - -template class noncopyable_range { - private: - std::vector vec; - - public: - using const_iterator = typename ::std::vector::const_iterator; - - template - explicit noncopyable_range(Args&&... args) - : vec(::std::forward(args)...) {} - - noncopyable_range(noncopyable_range const&) = delete; - noncopyable_range(noncopyable_range&) = delete; - - const_iterator begin() const { return vec.begin(); } - const_iterator end() const { return vec.end(); } -}; - -TEST(RangesTest, Range) { - noncopyable_range w(3u, 0); - EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), w)); - EXPECT_EQ("{0, 0, 0}", - fmt::format(FMT_STRING("{}"), noncopyable_range(3u, 0))); - - non_const_only_range x(3u, 0); - EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), x)); - EXPECT_EQ("{0, 0, 0}", - fmt::format(FMT_STRING("{}"), non_const_only_range(3u, 0))); - - std::vector y(3u, 0); - EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), y)); - EXPECT_EQ("{0, 0, 0}", - fmt::format(FMT_STRING("{}"), std::vector(3u, 0))); - - const std::vector z(3u, 0); - EXPECT_EQ("{0, 0, 0}", fmt::format(FMT_STRING("{}"), z)); -} - -#if !FMT_MSC_VER || FMT_MSC_VER >= 1927 -struct unformattable {}; - -TEST(RangesTest, UnformattableRange) { - EXPECT_FALSE((fmt::has_formatter, - fmt::format_context>::value)); -} -#endif From 60bceae2f454792f656cb2ce78e63163e6951813 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Thu, 3 Dec 2020 20:23:59 -0800 Subject: [PATCH 17/34] fixup chrono.h with better notes about MSVC workaround --- include/fmt/chrono.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 7e28e199d928..5cec73674f2b 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -770,8 +770,8 @@ template >(val)); #else @@ -821,7 +821,7 @@ OutputIt format_duration_unit(OutputIt out) { return copy_unit(string_view(unit), out, Char()); static FMT_CONSTEXPR_DECL const Char num_f[] = {'[', '{', '}', ']', 's', 0}; if (const_check(Period::den == 1)) { -#if FMT_MSC_VER && FMT_MSC_VER <= 1928 +#if FMT_MSC_VER return vformat_to(out, to_string_view(num_f), make_format_args>(Period::num)); #else @@ -830,7 +830,7 @@ OutputIt format_duration_unit(OutputIt out) { } static FMT_CONSTEXPR_DECL const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0}; -#if FMT_MSC_VER && FMT_MSC_VER <= 1928 +#if FMT_MSC_VER return vformat_to( out, to_string_view(num_def_f), make_format_args>(Period::num, Period::den)); From b61537a9df869b1f437d14b4a5cbf7d47353526b Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Thu, 3 Dec 2020 21:36:12 -0800 Subject: [PATCH 18/34] restore missing include --- test/enforce-compiletime-test.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/test/enforce-compiletime-test.cc b/test/enforce-compiletime-test.cc index 7f05d29a5077..286e3d0e124d 100644 --- a/test/enforce-compiletime-test.cc +++ b/test/enforce-compiletime-test.cc @@ -28,6 +28,7 @@ #undef index #include "gmock.h" +#include "gtest-extra.h" TEST(CompileTimeTest, FormatApi) { EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42)); From d539f76da624fa004d56a7439bfff6c534325952 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 7 Dec 2020 21:21:17 -0800 Subject: [PATCH 19/34] cleanup doc string --- doc/api.rst | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 57722878605a..3542e489b687 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -165,13 +165,10 @@ functions in their ``formatter`` specializations. .. _udt: -Enforcing Compile-time Format Checks ------------------------------------- -To force the use of Compile-time checks, compile with -``FMT_ENFORCE_COMPILE_STRING`` defined. When this preprocessor definition is -given, functions accepting ``FMT_STRING`` will fail to compile with regular -strings. When this is set, runtime-checked formatting is still possible using -``fmt::vformat``, ``fmt::vprint``, etc. +To force the use of compile-time checks, define the preprocessor variable +``FMT_ENFORCE_COMPILE_STRING``. When set, functions accepting ``FMT_STRING`` +will fail to compile with regular strings. Runtime-checked +formatting is still possible using ``fmt::vformat``, ``fmt::vprint``, etc. Formatting User-defined Types ----------------------------- From dafbc05dbd38aea5261bab61ca17329618ce0605 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 7 Dec 2020 21:30:11 -0800 Subject: [PATCH 20/34] fixup test --- test/enforce-compiletime-test.cc | 137 ++++++++++--------------------- 1 file changed, 44 insertions(+), 93 deletions(-) diff --git a/test/enforce-compiletime-test.cc b/test/enforce-compiletime-test.cc index 286e3d0e124d..a335806e74e0 100644 --- a/test/enforce-compiletime-test.cc +++ b/test/enforce-compiletime-test.cc @@ -25,77 +25,41 @@ #include "fmt/ostream.h" #include "fmt/ranges.h" -#undef index +// Exercise the API to verify that everything we expect to can compile. +void TestFormatApi() { + (void)fmt::format(FMT_STRING("noop")); + (void)fmt::format(FMT_STRING("{}"), 42); + (void)fmt::format(FMT_STRING(L"{}"), 42); -#include "gmock.h" -#include "gtest-extra.h" - -TEST(CompileTimeTest, FormatApi) { - EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42)); - EXPECT_EQ(L"42", fmt::format(FMT_STRING(L"{}"), 42)); - EXPECT_EQ("42", fmt::to_string(42)); - EXPECT_EQ(L"42", fmt::to_wstring(42)); + (void)fmt::to_string(42); + (void)fmt::to_wstring(42); std::list out; fmt::format_to(std::back_inserter(out), FMT_STRING("{}"), 42); - EXPECT_EQ("42", std::string(out.begin(), out.end())); std::stringstream s; fmt::format_to(std::ostream_iterator(s), FMT_STRING("{}"), 42); - EXPECT_EQ("42", s.str()); -} + char buffer[4]; + (void)fmt::format_to_n(buffer, 3, FMT_STRING("{}"), 12345); + + wchar_t wbuffer[4]; + (void)fmt::format_to_n(wbuffer, 3, FMT_STRING(L"{}"), 12345); + + (void)fmt::formatted_size(FMT_STRING("{}"), 42); +} +void TestLiteralsApi() { #if FMT_USE_UDL_TEMPLATE -// Passing user-defined literals directly to EXPECT_EQ causes problems -// with macro argument stringification (#) on some versions of GCC. -// Workaround: Assing the UDL result to a variable before the macro. + // Passing user-defined literals directly to EXPECT_EQ causes problems + // with macro argument stringification (#) on some versions of GCC. + // Workaround: Assing the UDL result to a variable before the macro. -using namespace fmt::literals; + using namespace fmt::literals; -TEST(CompileTimeTest, Literals) { auto udl_format = "{}c{}"_format("ab", 1); - EXPECT_EQ("abc1", udl_format); auto udl_format_w = L"{}c{}"_format(L"ab", 1); - EXPECT_EQ(L"abc1", udl_format_w); -} + (void)udl_format; + (void)udl_format_w; #endif - -TEST(CompileTimeTest, FormattedSize) { - EXPECT_EQ(2u, fmt::formatted_size(FMT_STRING("{}"), 42)); -} - -TEST(CompileTimeTest, FormatTo) { - std::vector v; - fmt::format_to(std::back_inserter(v), FMT_STRING("{}"), "foo"); - EXPECT_EQ(fmt::string_view(v.data(), v.size()), "foo"); -} - -TEST(CompileTimeTest, FormatToN) { - char buffer[4]; - buffer[3] = 'x'; - auto result = fmt::format_to_n(buffer, 3, FMT_STRING("{}"), 12345); - EXPECT_EQ(5u, result.size); - EXPECT_EQ(buffer + 3, result.out); - EXPECT_EQ("123x", fmt::string_view(buffer, 4)); -} - -TEST(CompileTimeTest, WideFormatToN) { - wchar_t buffer[4]; - buffer[3] = L'x'; - auto result = fmt::format_to_n(buffer, 3, FMT_STRING(L"{}"), 12345); - EXPECT_EQ(5u, result.size); - EXPECT_EQ(buffer + 3, result.out); - EXPECT_EQ(L"123x", fmt::wstring_view(buffer, 4)); - buffer[0] = L'x'; - buffer[1] = L'x'; - buffer[2] = L'x'; - result = fmt::format_to_n(buffer, 3, FMT_STRING(L"{}"), L'A'); - EXPECT_EQ(1u, result.size); - EXPECT_EQ(buffer + 1, result.out); - EXPECT_EQ(L"Axxx", fmt::wstring_view(buffer, 4)); - result = fmt::format_to_n(buffer, 3, FMT_STRING(L"{}{} "), L'B', L'C'); - EXPECT_EQ(3u, result.size); - EXPECT_EQ(buffer + 3, result.out); - EXPECT_EQ(L"BC x", fmt::wstring_view(buffer, 4)); } struct test_output_iterator { @@ -119,53 +83,29 @@ struct test_output_iterator { char& operator*() { return *data; } }; -TEST(CompileTimeTest, FormatToNOutputIterator) { +void FormatToNOutputIteratorTest() { char buf[10] = {}; fmt::format_to_n(test_output_iterator{buf}, 10, FMT_STRING("{}"), 42); - EXPECT_STREQ(buf, "42"); } +void TestChrono() { #ifndef FMT_STATIC_THOUSANDS_SEPARATOR - -TEST(CompileTimeTest, Chrono) { - EXPECT_EQ("42s", fmt::format(FMT_STRING("{}"), std::chrono::seconds(42))); -} - -TEST(CompileTimeTest, ChronoWide) { - EXPECT_EQ(L"42s", fmt::format(FMT_STRING(L"{}"), std::chrono::seconds(42))); -} - + (void)fmt::format(FMT_STRING("{}"), std::chrono::seconds(42)); + (void)fmt::format(FMT_STRING(L"{}"), std::chrono::seconds(42)); #endif // FMT_STATIC_THOUSANDS_SEPARATOR - -TEST(CompileTimeTest, PrintTextStyle) { - EXPECT_WRITE( - stdout, - fmt::print(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), "rgb(255,20,30)"), - "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); } -TEST(CompileTimeTest, FormatTextStyle) { - EXPECT_EQ("\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m", - fmt::format(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), - "rgb(255,20,30)")); -} +void TestTextStyle() { + fmt::print(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), "rgb(255,20,30)"); + (void)fmt::format(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), + "rgb(255,20,30)"); -TEST(CompileTimeTest, FormatToOutAcceptsTextStyle) { fmt::text_style ts = fg(fmt::rgb(255, 20, 30)); std::string out; fmt::format_to(std::back_inserter(out), ts, FMT_STRING("rgb(255,20,30){}{}{}"), 1, 2, 3); - - EXPECT_EQ(fmt::to_string(out), - "\x1b[38;2;255;020;030mrgb(255,20,30)123\x1b[0m"); } -struct test {}; - -// Check if 'if constexpr' is supported. -#if (__cplusplus > 201402L) || \ - (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) - struct zstring_sentinel {}; bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; } @@ -177,8 +117,19 @@ struct zstring { zstring_sentinel end() const { return {}; } }; -TEST(CompileTimeTest, JoinSentinel) { +void TestZString() { zstring hello{"hello"}; - EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format(FMT_STRING("{}"), hello)); - EXPECT_EQ("h_e_l_l_o", fmt::format(FMT_STRING("{}"), fmt::join(hello, "_"))); + (void)fmt::format(FMT_STRING("{}"), hello); + (void)fmt::format(FMT_STRING("{}"), fmt::join(hello, "_")); +} + +int main(int argc, char** argv) { + TestFormatApi(); + TestLiteralsApi(); + FormatToNOutputIteratorTest(); + TestChrono(); + TestTextStyle(); + TestZString(); + + return 0; } From 9fafb14f29b9bbbe099e1faee30571307d55d68a Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 7 Dec 2020 22:07:47 -0800 Subject: [PATCH 21/34] fix unused arg issue in test --- test/enforce-compiletime-test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/enforce-compiletime-test.cc b/test/enforce-compiletime-test.cc index a335806e74e0..6ae06b966970 100644 --- a/test/enforce-compiletime-test.cc +++ b/test/enforce-compiletime-test.cc @@ -123,7 +123,7 @@ void TestZString() { (void)fmt::format(FMT_STRING("{}"), fmt::join(hello, "_")); } -int main(int argc, char** argv) { +int main(int, char**) { TestFormatApi(); TestLiteralsApi(); FormatToNOutputIteratorTest(); From 2489501c392db51db6f15d1d4e2be81c106ab95b Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 7 Dec 2020 22:20:29 -0800 Subject: [PATCH 22/34] fix gcc issue --- test/enforce-compiletime-test.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/enforce-compiletime-test.cc b/test/enforce-compiletime-test.cc index 6ae06b966970..4b2e62b8b8ce 100644 --- a/test/enforce-compiletime-test.cc +++ b/test/enforce-compiletime-test.cc @@ -27,9 +27,11 @@ // Exercise the API to verify that everything we expect to can compile. void TestFormatApi() { - (void)fmt::format(FMT_STRING("noop")); (void)fmt::format(FMT_STRING("{}"), 42); (void)fmt::format(FMT_STRING(L"{}"), 42); +#if !FMT_GCC_VERSION + (void)fmt::format(FMT_STRING("noop")); +#endif (void)fmt::to_string(42); (void)fmt::to_wstring(42); From b8ebadd3ac8e953a4cdca47d0caca3a294a3d0fb Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Wed, 9 Dec 2020 11:22:32 -0800 Subject: [PATCH 23/34] fixup chrono MSVC workaround --- include/fmt/chrono.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 5cec73674f2b..8e0cca56f684 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -771,6 +771,7 @@ OutputIt format_duration_value(OutputIt out, Rep val, int) { static FMT_CONSTEXPR_DECL const Char format[] = {'{', '}', 0}; // Note(12/3/2020): Workaround an as-of-yet unfixed compiler error in MSVC. +// See https://developercommunity.visualstudio.com/content/problem/1277597/internal-compiler-c0001-error-on-complex-nested-la.html #if FMT_MSC_VER return vformat_to(out, to_string_view(format), make_format_args>(val)); @@ -785,7 +786,7 @@ OutputIt format_duration_value(OutputIt out, Rep val, int precision) { static FMT_CONSTEXPR_DECL const Char pr_f[] = {'{', ':', '.', '{', '}', 'f', '}', 0}; if (precision >= 0) { -#if FMT_MSC_VER && FMT_MSC_VER <= 1928 +#if FMT_MSC_VER return vformat_to(out, to_string_view(pr_f), make_format_args>(val, precision)); #else @@ -794,7 +795,7 @@ OutputIt format_duration_value(OutputIt out, Rep val, int precision) { } static FMT_CONSTEXPR_DECL const Char fp_f[] = {'{', ':', 'g', '}', 0}; -#if FMT_MSC_VER && FMT_MSC_VER <= 1928 +#if FMT_MSC_VER return vformat_to(out, to_string_view(fp_f), make_format_args>(val)); #else From 098de0d54fbb8354aecee7c76ebb34c719d715e3 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Sat, 12 Dec 2020 14:57:59 -0800 Subject: [PATCH 24/34] Remove formatted_size overlaod accepting compile-time strings --- include/fmt/core.h | 8 -------- test/enforce-compiletime-test.cc | 2 -- 2 files changed, 10 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 0318afc74cc7..f96ce3a15512 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1841,14 +1841,6 @@ inline auto format_to_n(OutputIt out, size_t n, const S& format_str, Returns the number of characters in the output of ``format(format_str, args...)``. */ -template ::value)> -inline size_t formatted_size(const S& format_str, Args&&... args) { - const auto& vargs = fmt::make_args_checked(format_str, args...); - detail::counting_buffer<> buf; - detail::vformat_to(buf, to_string_view(format_str), vargs); - return buf.count(); -} template inline size_t formatted_size(string_view format_str, Args&&... args) { diff --git a/test/enforce-compiletime-test.cc b/test/enforce-compiletime-test.cc index 4b2e62b8b8ce..4ae943e03c7f 100644 --- a/test/enforce-compiletime-test.cc +++ b/test/enforce-compiletime-test.cc @@ -46,8 +46,6 @@ void TestFormatApi() { wchar_t wbuffer[4]; (void)fmt::format_to_n(wbuffer, 3, FMT_STRING(L"{}"), 12345); - - (void)fmt::formatted_size(FMT_STRING("{}"), 42); } void TestLiteralsApi() { #if FMT_USE_UDL_TEMPLATE From 8e575bd507a6f4eb5770707e092d9ffccafa01f3 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Sat, 12 Dec 2020 16:55:02 -0800 Subject: [PATCH 25/34] address comments --- include/fmt/chrono.h | 22 ------ test/CMakeLists.txt | 10 ++- ...test.cc => enforce-compile-string-test.cc} | 67 ++++--------------- 3 files changed, 19 insertions(+), 80 deletions(-) rename test/{enforce-compiletime-test.cc => enforce-compile-string-test.cc} (60%) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 8e0cca56f684..417ff992f88c 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -770,14 +770,8 @@ template >(val)); -#else - return format_to(out, FMT_STRING(format), val); -#endif } template = 0) { -#if FMT_MSC_VER return vformat_to(out, to_string_view(pr_f), make_format_args>(val, precision)); -#else - return format_to(out, FMT_STRING(pr_f), val, precision); -#endif } static FMT_CONSTEXPR_DECL const Char fp_f[] = {'{', ':', 'g', '}', 0}; -#if FMT_MSC_VER return vformat_to(out, to_string_view(fp_f), make_format_args>(val)); -#else - return format_to(out, FMT_STRING(fp_f), val); -#endif } template @@ -822,22 +808,14 @@ OutputIt format_duration_unit(OutputIt out) { return copy_unit(string_view(unit), out, Char()); static FMT_CONSTEXPR_DECL const Char num_f[] = {'[', '{', '}', ']', 's', 0}; if (const_check(Period::den == 1)) { -#if FMT_MSC_VER return vformat_to(out, to_string_view(num_f), make_format_args>(Period::num)); -#else - return format_to(out, FMT_STRING(num_f), Period::num); -#endif } static FMT_CONSTEXPR_DECL const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0}; -#if FMT_MSC_VER return vformat_to( out, to_string_view(num_def_f), make_format_args>(Period::num, Period::den)); -#else - return format_to(out, FMT_STRING(num_def_f), Period::num, Period::den); -#endif } template #include -#ifdef WIN32 -# define _CRT_SECURE_NO_WARNINGS -#endif - #include "fmt/chrono.h" #include "fmt/color.h" #include "fmt/format.h" @@ -26,20 +22,16 @@ #include "fmt/ranges.h" // Exercise the API to verify that everything we expect to can compile. -void TestFormatApi() { +void test_format_api() { (void)fmt::format(FMT_STRING("{}"), 42); (void)fmt::format(FMT_STRING(L"{}"), 42); -#if !FMT_GCC_VERSION (void)fmt::format(FMT_STRING("noop")); -#endif (void)fmt::to_string(42); (void)fmt::to_wstring(42); std::list out; fmt::format_to(std::back_inserter(out), FMT_STRING("{}"), 42); - std::stringstream s; - fmt::format_to(std::ostream_iterator(s), FMT_STRING("{}"), 42); char buffer[4]; (void)fmt::format_to_n(buffer, 3, FMT_STRING("{}"), 12345); @@ -47,12 +39,9 @@ void TestFormatApi() { wchar_t wbuffer[4]; (void)fmt::format_to_n(wbuffer, 3, FMT_STRING(L"{}"), 12345); } -void TestLiteralsApi() { -#if FMT_USE_UDL_TEMPLATE - // Passing user-defined literals directly to EXPECT_EQ causes problems - // with macro argument stringification (#) on some versions of GCC. - // Workaround: Assing the UDL result to a variable before the macro. +void test_literals_api() { +#if FMT_USE_UDL_TEMPLATE using namespace fmt::literals; auto udl_format = "{}c{}"_format("ab", 1); @@ -62,40 +51,12 @@ void TestLiteralsApi() { #endif } -struct test_output_iterator { - char* data; - - using iterator_category = std::output_iterator_tag; - using value_type = void; - using difference_type = void; - using pointer = void; - using reference = void; - - test_output_iterator& operator++() { - ++data; - return *this; - } - test_output_iterator operator++(int) { - auto tmp = *this; - ++data; - return tmp; - } - char& operator*() { return *data; } -}; - -void FormatToNOutputIteratorTest() { - char buf[10] = {}; - fmt::format_to_n(test_output_iterator{buf}, 10, FMT_STRING("{}"), 42); -} - -void TestChrono() { -#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +void test_chrono() { (void)fmt::format(FMT_STRING("{}"), std::chrono::seconds(42)); (void)fmt::format(FMT_STRING(L"{}"), std::chrono::seconds(42)); -#endif // FMT_STATIC_THOUSANDS_SEPARATOR } -void TestTextStyle() { +void test_text_style() { fmt::print(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), "rgb(255,20,30)"); (void)fmt::format(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), "rgb(255,20,30)"); @@ -117,19 +78,15 @@ struct zstring { zstring_sentinel end() const { return {}; } }; -void TestZString() { +void test_zstring() { zstring hello{"hello"}; (void)fmt::format(FMT_STRING("{}"), hello); - (void)fmt::format(FMT_STRING("{}"), fmt::join(hello, "_")); } -int main(int, char**) { - TestFormatApi(); - TestLiteralsApi(); - FormatToNOutputIteratorTest(); - TestChrono(); - TestTextStyle(); - TestZString(); - - return 0; +int main() { + test_format_api(); + test_literals_api(); + test_chrono(); + test_text_style(); + test_zstring(); } From f2a5a0f27e20020d17c82cee790775145115c0a3 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Sat, 12 Dec 2020 17:43:26 -0800 Subject: [PATCH 26/34] restore ifdef guarding GCC bug --- test/enforce-compile-string-test.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/enforce-compile-string-test.cc b/test/enforce-compile-string-test.cc index 79a5cf8d2848..5573f59c69fa 100644 --- a/test/enforce-compile-string-test.cc +++ b/test/enforce-compile-string-test.cc @@ -25,7 +25,10 @@ void test_format_api() { (void)fmt::format(FMT_STRING("{}"), 42); (void)fmt::format(FMT_STRING(L"{}"), 42); +#if !FMT_GCC_VERSION // Currently will not compile: See + // https://github.com/fmtlib/fmt/issues/2039 (void)fmt::format(FMT_STRING("noop")); +#endif (void)fmt::to_string(42); (void)fmt::to_wstring(42); From c85d5e342c2386ae487eec3468ea595d286ffabb Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Sun, 20 Dec 2020 16:07:45 -0800 Subject: [PATCH 27/34] address easy comments --- include/fmt/chrono.h | 1 - include/fmt/core.h | 1 - test/CMakeLists.txt | 2 +- test/enforce-compile-string-test.cc | 32 ++++++----------------------- 4 files changed, 7 insertions(+), 29 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 417ff992f88c..d5c9d64164e1 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -769,7 +769,6 @@ template ::value)> OutputIt format_duration_value(OutputIt out, Rep val, int) { static FMT_CONSTEXPR_DECL const Char format[] = {'{', '}', 0}; - return vformat_to(out, to_string_view(format), make_format_args>(val)); } diff --git a/include/fmt/core.h b/include/fmt/core.h index f96ce3a15512..d7d2de50126f 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1841,7 +1841,6 @@ inline auto format_to_n(OutputIt out, size_t n, const S& format_str, Returns the number of characters in the output of ``format(format_str, args...)``. */ - template inline size_t formatted_size(string_view format_str, Args&&... args) { const auto& vargs = fmt::make_args_checked(format_str, args...); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f34a71ee0e26..78798d8719e0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -106,7 +106,7 @@ add_fmt_test(printf-test) add_fmt_test(ranges-test) add_fmt_test(scan-test) -if(NOT MSVC) +if (NOT MSVC) # FMT_ENFORCE_COMPILE_STRING not supported under MSVC # See https://developercommunity.visualstudio.com/content/problem/1277597/internal-compiler-c0001-error-on-complex-nested-la.html add_fmt_test(enforce-compile-string-test) diff --git a/test/enforce-compile-string-test.cc b/test/enforce-compile-string-test.cc index 5573f59c69fa..f1e8edeebab2 100644 --- a/test/enforce-compile-string-test.cc +++ b/test/enforce-compile-string-test.cc @@ -6,13 +6,10 @@ // For the license information refer to format.h. #include +#include #include #include -#include -#include #include -#include -#include #include "fmt/chrono.h" #include "fmt/color.h" @@ -25,10 +22,7 @@ void test_format_api() { (void)fmt::format(FMT_STRING("{}"), 42); (void)fmt::format(FMT_STRING(L"{}"), 42); -#if !FMT_GCC_VERSION // Currently will not compile: See - // https://github.com/fmtlib/fmt/issues/2039 (void)fmt::format(FMT_STRING("noop")); -#endif (void)fmt::to_string(42); (void)fmt::to_wstring(42); @@ -46,11 +40,8 @@ void test_format_api() { void test_literals_api() { #if FMT_USE_UDL_TEMPLATE using namespace fmt::literals; - - auto udl_format = "{}c{}"_format("ab", 1); - auto udl_format_w = L"{}c{}"_format(L"ab", 1); - (void)udl_format; - (void)udl_format_w; + "{}c{}"_format("ab", 1); + L"{}c{}"_format(L"ab", 1); #endif } @@ -70,19 +61,8 @@ void test_text_style() { FMT_STRING("rgb(255,20,30){}{}{}"), 1, 2, 3); } -struct zstring_sentinel {}; - -bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; } -bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; } - -struct zstring { - const char* p; - const char* begin() const { return p; } - zstring_sentinel end() const { return {}; } -}; - -void test_zstring() { - zstring hello{"hello"}; +void test_range() { + std::array hello = {'h','e','l','l','o'}; (void)fmt::format(FMT_STRING("{}"), hello); } @@ -91,5 +71,5 @@ int main() { test_literals_api(); test_chrono(); test_text_style(); - test_zstring(); + test_range(); } From b9063f9392b50efacb6c11c69cb46627720eed9e Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Sun, 20 Dec 2020 16:26:53 -0800 Subject: [PATCH 28/34] remove vformat from chrono.h --- include/fmt/chrono.h | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index d5c9d64164e1..9574a7e03bed 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -768,24 +768,16 @@ inline std::chrono::duration get_milliseconds( template ::value)> OutputIt format_duration_value(OutputIt out, Rep val, int) { - static FMT_CONSTEXPR_DECL const Char format[] = {'{', '}', 0}; - return vformat_to(out, to_string_view(format), - make_format_args>(val)); + return write(out, val); } template ::value)> OutputIt format_duration_value(OutputIt out, Rep val, int precision) { - static FMT_CONSTEXPR_DECL const Char pr_f[] = {'{', ':', '.', '{', - '}', 'f', '}', 0}; - if (precision >= 0) { - return vformat_to(out, to_string_view(pr_f), - make_format_args>(val, precision)); - } - static FMT_CONSTEXPR_DECL const Char fp_f[] = {'{', ':', 'g', '}', 0}; - - return vformat_to(out, to_string_view(fp_f), - make_format_args>(val)); + basic_format_specs specs; + specs.precision = precision; + specs.type = precision > 0 ? 'f' : 'g'; + return write(out, val, specs); } template @@ -805,16 +797,20 @@ template OutputIt format_duration_unit(OutputIt out) { if (const char* unit = get_units()) return copy_unit(string_view(unit), out, Char()); - static FMT_CONSTEXPR_DECL const Char num_f[] = {'[', '{', '}', ']', 's', 0}; - if (const_check(Period::den == 1)) { - return vformat_to(out, to_string_view(num_f), - make_format_args>(Period::num)); + + basic_memory_buffer buffer; + auto bufOut = std::back_inserter(buffer); + *bufOut++ = '['; + bufOut = write(bufOut, Period::num); + + if (const_check(Period::den != 1)) { + *bufOut++ = '/'; + bufOut = write(bufOut, Period::den); } - static FMT_CONSTEXPR_DECL const Char num_def_f[] = {'[', '{', '}', '/', '{', - '}', ']', 's', 0}; - return vformat_to( - out, to_string_view(num_def_f), - make_format_args>(Period::num, Period::den)); + + *bufOut++ = ']'; + *bufOut++ = 's'; + return write(out, {buffer.data(), buffer.size()}); } template Date: Sun, 20 Dec 2020 21:17:53 -0800 Subject: [PATCH 29/34] attempt stripping void casts --- test/enforce-compile-string-test.cc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test/enforce-compile-string-test.cc b/test/enforce-compile-string-test.cc index f1e8edeebab2..3720ba9d09e9 100644 --- a/test/enforce-compile-string-test.cc +++ b/test/enforce-compile-string-test.cc @@ -20,21 +20,21 @@ // Exercise the API to verify that everything we expect to can compile. void test_format_api() { - (void)fmt::format(FMT_STRING("{}"), 42); - (void)fmt::format(FMT_STRING(L"{}"), 42); - (void)fmt::format(FMT_STRING("noop")); + fmt::format(FMT_STRING("{}"), 42); + fmt::format(FMT_STRING(L"{}"), 42); + fmt::format(FMT_STRING("noop")); - (void)fmt::to_string(42); - (void)fmt::to_wstring(42); + fmt::to_string(42); + fmt::to_wstring(42); std::list out; fmt::format_to(std::back_inserter(out), FMT_STRING("{}"), 42); char buffer[4]; - (void)fmt::format_to_n(buffer, 3, FMT_STRING("{}"), 12345); + fmt::format_to_n(buffer, 3, FMT_STRING("{}"), 12345); wchar_t wbuffer[4]; - (void)fmt::format_to_n(wbuffer, 3, FMT_STRING(L"{}"), 12345); + fmt::format_to_n(wbuffer, 3, FMT_STRING(L"{}"), 12345); } void test_literals_api() { @@ -46,13 +46,13 @@ void test_literals_api() { } void test_chrono() { - (void)fmt::format(FMT_STRING("{}"), std::chrono::seconds(42)); - (void)fmt::format(FMT_STRING(L"{}"), std::chrono::seconds(42)); + fmt::format(FMT_STRING("{}"), std::chrono::seconds(42)); + fmt::format(FMT_STRING(L"{}"), std::chrono::seconds(42)); } void test_text_style() { fmt::print(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), "rgb(255,20,30)"); - (void)fmt::format(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), + fmt::format(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), "rgb(255,20,30)"); fmt::text_style ts = fg(fmt::rgb(255, 20, 30)); @@ -63,7 +63,7 @@ void test_text_style() { void test_range() { std::array hello = {'h','e','l','l','o'}; - (void)fmt::format(FMT_STRING("{}"), hello); + fmt::format(FMT_STRING("{}"), hello); } int main() { From b0c65dffe6d58afe8446f5e293dfe6840b1abf52 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 21 Dec 2020 11:16:46 -0800 Subject: [PATCH 30/34] Refactor ranges, fix chrono, apply clang format to include/fmt/*.h --- include/fmt/chrono.h | 16 +++---- include/fmt/locale.h | 4 +- include/fmt/ranges.h | 74 +++++++++++++++-------------- test/enforce-compile-string-test.cc | 5 +- 4 files changed, 50 insertions(+), 49 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 9574a7e03bed..87f3a46f7c66 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -798,19 +798,17 @@ OutputIt format_duration_unit(OutputIt out) { if (const char* unit = get_units()) return copy_unit(string_view(unit), out, Char()); - basic_memory_buffer buffer; - auto bufOut = std::back_inserter(buffer); - *bufOut++ = '['; - bufOut = write(bufOut, Period::num); + *out++ = '['; + out = write(out, Period::num); if (const_check(Period::den != 1)) { - *bufOut++ = '/'; - bufOut = write(bufOut, Period::den); + *out++ = '/'; + out = write(out, Period::den); } - *bufOut++ = ']'; - *bufOut++ = 's'; - return write(out, {buffer.data(), buffer.size()}); + *out++ = ']'; + *out++ = 's'; + return out; } template >::value> -inline auto format_to(OutputIt out, const std::locale& loc, - const S& format_str, Args&&... args) -> +inline auto format_to(OutputIt out, const std::locale& loc, const S& format_str, + Args&&... args) -> typename std::enable_if::type { const auto& vargs = fmt::make_args_checked(format_str, args...); return vformat_to(out, loc, to_string_view(format_str), vargs); diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index c4db83f66aa7..d6c2eb116b09 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -247,31 +247,40 @@ template using value_type = remove_cvref_t()))>; -template ::type>::value)> -FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { - return add_space ? " {}" : "{}"; +template +OutputIt add_range_formatting_spaces(OutputIt out, + const Formatting& formatting) { + if (const_check(formatting.add_prepostfix_space)) { + *out++ = ' '; + } + out = detail::copy(formatting.delimiter, out); + if (const_check(formatting.add_delimiter_spaces)) { + *out++ = ' '; + } + return out; } -template ::type>::value)> -FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { - return add_space ? " \"{}\"" : "\"{}\""; -} +template +OutputIt write_range_entry(OutputIt out, const Arg& v) { + FMT_CONSTEXPR_DECL const char quote = []() { + if (const_check( + std::is_same::value || + is_like_std_string::type>::value)) { + return '"'; + } else if (const_check(std::is_same::value)) { + return '\''; + } else { + return '\0'; + } + }(); -FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) { - return add_space ? " \"{}\"" : "\"{}\""; -} -FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) { - return add_space ? L" \"{}\"" : L"\"{}\""; -} + if (const_check(quote)) *out++ = quote; + out = write(out, v); + if (const_check(quote)) *out++ = quote; -FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) { - return add_space ? " '{}'" : "'{}'"; -} -FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) { - return add_space ? L" '{}'" : L"'{}'"; + return out; } + } // namespace detail template struct is_tuple_like { @@ -286,15 +295,10 @@ struct formatter::value>> { template struct format_each { template void operator()(const T& v) { if (i > 0) { - if (formatting.add_prepostfix_space) { - *out++ = ' '; - } - out = detail::copy(formatting.delimiter, out); + out = add_range_formatting_spaces(out, formatting); } - out = vformat_to(out, - detail::format_str_quoted( - (formatting.add_delimiter_spaces && i > 0), v), - make_format_args(v)); + + out = detail::write_range_entry(out, v); ++i; } @@ -363,19 +367,19 @@ struct formatter< auto end = view.end(); for (; it != end; ++it) { if (i > 0) { - if (formatting.add_prepostfix_space) *out++ = ' '; - out = detail::copy(formatting.delimiter, out); + out = detail::add_range_formatting_spaces(out, formatting); } - out = vformat_to(out, - detail::format_str_quoted( - (formatting.add_delimiter_spaces && i > 0), *it), - make_format_args(*it)); + + out = detail::write_range_entry(out, *it); + if (++i > formatting.range_length_limit) { out = format_to(out, FMT_STRING("{}"), " ... "); break; } } - if (formatting.add_prepostfix_space) *out++ = ' '; + if (formatting.add_prepostfix_space) { + *out++ = ' '; + } return detail::copy(formatting.postfix, out); } }; diff --git a/test/enforce-compile-string-test.cc b/test/enforce-compile-string-test.cc index 3720ba9d09e9..146e8d5ea721 100644 --- a/test/enforce-compile-string-test.cc +++ b/test/enforce-compile-string-test.cc @@ -52,8 +52,7 @@ void test_chrono() { void test_text_style() { fmt::print(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), "rgb(255,20,30)"); - fmt::format(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), - "rgb(255,20,30)"); + fmt::format(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), "rgb(255,20,30)"); fmt::text_style ts = fg(fmt::rgb(255, 20, 30)); std::string out; @@ -62,7 +61,7 @@ void test_text_style() { } void test_range() { - std::array hello = {'h','e','l','l','o'}; + std::array hello = {'h', 'e', 'l', 'l', 'o'}; fmt::format(FMT_STRING("{}"), hello); } From 6f393f2839593ddef07d56a3fd3d0b1838563d3d Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Mon, 21 Dec 2020 12:35:06 -0800 Subject: [PATCH 31/34] fixup constexpr issues --- include/fmt/ranges.h | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index d6c2eb116b09..9459c03810bc 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -260,27 +260,35 @@ OutputIt add_range_formatting_spaces(OutputIt out, return out; } -template +template < + typename Char, typename OutputIt, typename Arg, + FMT_ENABLE_IF(std::is_same::value || + is_like_std_string::type>::value)> OutputIt write_range_entry(OutputIt out, const Arg& v) { - FMT_CONSTEXPR_DECL const char quote = []() { - if (const_check( - std::is_same::value || - is_like_std_string::type>::value)) { - return '"'; - } else if (const_check(std::is_same::value)) { - return '\''; - } else { - return '\0'; - } - }(); - - if (const_check(quote)) *out++ = quote; + *out++ = '"'; out = write(out, v); - if (const_check(quote)) *out++ = quote; + *out++ = '"'; + return out; +} +template ::value)> +OutputIt write_range_entry(OutputIt out, const Arg v) { + *out++ = '\''; + *out++ = v; + *out++ = '\''; return out; } +template < + typename Char, typename OutputIt, typename Arg, + FMT_ENABLE_IF(!std::is_same::value && + !is_like_std_string::type>::value && + !std::is_same::value)> +OutputIt write_range_entry(OutputIt out, const Arg& v) { + return write(out, v); +} + } // namespace detail template struct is_tuple_like { From 06a576c9dd32e366e2efcb19e7def5db781ff39f Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Wed, 23 Dec 2020 19:05:07 -0800 Subject: [PATCH 32/34] revert locale format fix --- include/fmt/locale.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/fmt/locale.h b/include/fmt/locale.h index b5990ef19efb..7301bf92a243 100644 --- a/include/fmt/locale.h +++ b/include/fmt/locale.h @@ -52,8 +52,8 @@ inline OutputIt vformat_to( template >::value> -inline auto format_to(OutputIt out, const std::locale& loc, const S& format_str, - Args&&... args) -> +inline auto format_to(OutputIt out, const std::locale& loc, + const S& format_str, Args&&... args) -> typename std::enable_if::type { const auto& vargs = fmt::make_args_checked(format_str, args...); return vformat_to(out, loc, to_string_view(format_str), vargs); From 99ed2a846466377bf1548753c404805033d7d254 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Wed, 23 Dec 2020 19:35:47 -0800 Subject: [PATCH 33/34] address feedback --- include/fmt/ranges.h | 37 ++++++++----------------------------- test/ranges-test.cc | 4 ++++ 2 files changed, 12 insertions(+), 29 deletions(-) diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 9459c03810bc..91f55e0b31e6 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -37,19 +37,13 @@ struct formatting_range : formatting_base { FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the // range. Char prefix = '{'; - Char delimiter = ','; Char postfix = '}'; - static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; - static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; }; template struct formatting_tuple : formatting_base { Char prefix = '('; - Char delimiter = ','; Char postfix = ')'; - static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; - static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; }; namespace detail { @@ -247,23 +241,15 @@ template using value_type = remove_cvref_t()))>; -template -OutputIt add_range_formatting_spaces(OutputIt out, - const Formatting& formatting) { - if (const_check(formatting.add_prepostfix_space)) { - *out++ = ' '; - } - out = detail::copy(formatting.delimiter, out); - if (const_check(formatting.add_delimiter_spaces)) { - *out++ = ' '; - } +template OutputIt write_delimiter(OutputIt out) { + *out++ = ','; + *out++ = ' '; return out; } template < typename Char, typename OutputIt, typename Arg, - FMT_ENABLE_IF(std::is_same::value || - is_like_std_string::type>::value)> + FMT_ENABLE_IF(is_like_std_string::type>::value)> OutputIt write_range_entry(OutputIt out, const Arg& v) { *out++ = '"'; out = write(out, v); @@ -282,8 +268,7 @@ OutputIt write_range_entry(OutputIt out, const Arg v) { template < typename Char, typename OutputIt, typename Arg, - FMT_ENABLE_IF(!std::is_same::value && - !is_like_std_string::type>::value && + FMT_ENABLE_IF(!is_like_std_string::type>::value && !std::is_same::value)> OutputIt write_range_entry(OutputIt out, const Arg& v) { return write(out, v); @@ -303,7 +288,7 @@ struct formatter::value>> { template struct format_each { template void operator()(const T& v) { if (i > 0) { - out = add_range_formatting_spaces(out, formatting); + out = write_delimiter(out); } out = detail::write_range_entry(out, v); @@ -328,12 +313,9 @@ struct formatter::value>> { auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) { auto out = ctx.out(); size_t i = 0; - detail::copy(formatting.prefix, out); + detail::copy(formatting.prefix, out); detail::for_each(values, format_each{formatting, i, out}); - if (formatting.add_prepostfix_space) { - *out++ = ' '; - } detail::copy(formatting.postfix, out); return ctx.out(); @@ -375,7 +357,7 @@ struct formatter< auto end = view.end(); for (; it != end; ++it) { if (i > 0) { - out = detail::add_range_formatting_spaces(out, formatting); + out = detail::write_delimiter(out); } out = detail::write_range_entry(out, *it); @@ -385,9 +367,6 @@ struct formatter< break; } } - if (formatting.add_prepostfix_space) { - *out++ = ' '; - } return detail::copy(formatting.postfix, out); } }; diff --git a/test/ranges-test.cc b/test/ranges-test.cc index c05e5fcb26e5..9ae8b08f7613 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -243,6 +243,10 @@ TEST(RangesTest, Range) { const std::vector z(3u, 0); EXPECT_EQ("{0, 0, 0}", fmt::format("{}", z)); + + const char* array_of_string_literals[] = {"1234", "abcd"}; + EXPECT_EQ("{\"1234\", \"abcd\"}", + fmt::format("{}", array_of_string_literals)); } #if !FMT_MSC_VER || FMT_MSC_VER >= 1927 From 508348607354f0ea7be6f755d61cb8f3a8fcdb90 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Wed, 23 Dec 2020 19:41:11 -0800 Subject: [PATCH 34/34] move array of literals format test --- test/ranges-test.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/ranges-test.cc b/test/ranges-test.cc index 9ae8b08f7613..802f4ab201f7 100644 --- a/test/ranges-test.cc +++ b/test/ranges-test.cc @@ -51,6 +51,11 @@ TEST(RangesTest, FormatMap) { EXPECT_EQ("{(\"one\", 1), (\"two\", 2)}", fmt::format("{}", simap)); } +TEST(RangesTest, FormatArrayOfLiterals) { + const char* aol[] = {"1234", "abcd"}; + EXPECT_EQ("{\"1234\", \"abcd\"}", fmt::format("{}", aol)); +} + TEST(RangesTest, FormatPair) { std::pair pa1{42, 1.5f}; EXPECT_EQ("(42, 1.5)", fmt::format("{}", pa1)); @@ -243,10 +248,6 @@ TEST(RangesTest, Range) { const std::vector z(3u, 0); EXPECT_EQ("{0, 0, 0}", fmt::format("{}", z)); - - const char* array_of_string_literals[] = {"1234", "abcd"}; - EXPECT_EQ("{\"1234\", \"abcd\"}", - fmt::format("{}", array_of_string_literals)); } #if !FMT_MSC_VER || FMT_MSC_VER >= 1927