// Formatting library for C++ - the core API // // Copyright (c) 2012 - present, Victor Zverovich // All rights reserved. // // For the license information refer to format.h. // // Copyright (c) 2018 - present, Remotion (Igor Schulz) // All Rights Reserved // {fmt} support for ranges, containers and types tuple interface. #include "fmt/ranges.h" #include #include #include #include #include "gtest.h" #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 601 # define FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY #endif #if !FMT_MSC_VER || FMT_MSC_VER > 1910 # define FMT_RANGES_TEST_ENABLE_JOIN # define FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT #endif #ifdef FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY TEST(RangesTest, FormatArray) { int32_t ia[] = {1, 2, 3, 5, 7, 11}; auto iaf = fmt::format("{}", 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("{}", ia); EXPECT_EQ("{{1, 2}, {3, 5}, {7, 11}}", iaf); } TEST(RangesTest, FormatArrayOfLiterals) { const char* aol[] = {"1234", "abcd"}; EXPECT_EQ("{\"1234\", \"abcd\"}", fmt::format("{}", aol)); } #endif // FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY TEST(RangesTest, FormatVector) { std::vector iv{1, 2, 3, 5, 7, 11}; auto ivf = fmt::format("{}", 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("{}", 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("{}", simap)); } TEST(RangesTest, FormatPair) { std::pair pa1{42, 1.5f}; EXPECT_EQ("(42, 1.5)", fmt::format("{}", 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("{}", t)); EXPECT_EQ("()", fmt::format("{}", std::tuple<>())); } #ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT struct my_struct { int32_t i; std::string str; // can throw template fmt::enable_if_t get() const noexcept { return i; } template fmt::enable_if_t get() const noexcept { return {str}; } }; template auto get(const my_struct& s) noexcept -> decltype(s.get()) { 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("{}", mst)); } #endif // FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT TEST(RangesTest, FormatTo) { char buf[10]; auto end = fmt::format_to(buf, "{}", 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)); } #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("{}", string_like())); } #endif // FMT_USE_STRING_VIEW // 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("{}", w)); EXPECT_EQ("{0, 0, 0}", fmt::format("{}", noncopyable_range(3u, 0))); non_const_only_range x(3u, 0); EXPECT_EQ("{0, 0, 0}", fmt::format("{}", x)); EXPECT_EQ("{0, 0, 0}", fmt::format("{}", non_const_only_range(3u, 0))); std::vector y(3u, 0); EXPECT_EQ("{0, 0, 0}", fmt::format("{}", y)); EXPECT_EQ("{0, 0, 0}", fmt::format("{}", std::vector(3u, 0))); const std::vector z(3u, 0); EXPECT_EQ("{0, 0, 0}", fmt::format("{}", z)); } #if !FMT_MSC_VER || FMT_MSC_VER >= 1927 struct unformattable {}; TEST(RangesTest, UnformattableRange) { EXPECT_FALSE((fmt::has_formatter, fmt::format_context>::value)); } #endif #ifdef FMT_RANGES_TEST_ENABLE_JOIN TEST(RangesTest, JoinTuple) { // Value tuple args std::tuple t1 = std::make_tuple('a', 1, 2.0f); EXPECT_EQ("(a, 1, 2)", fmt::format("({})", fmt::join(t1, ", "))); // Testing lvalue tuple args int x = 4; std::tuple t2{'b', x}; EXPECT_EQ("b + 4", fmt::format("{}", fmt::join(t2, " + "))); // Empty tuple std::tuple<> t3; EXPECT_EQ("", fmt::format("{}", fmt::join(t3, "|"))); // Single element tuple std::tuple t4{4.0f}; EXPECT_EQ("4", fmt::format("{}", fmt::join(t4, "/"))); } TEST(RangesTest, WideStringJoinTuple) { // Value tuple args std::tuple t1 = std::make_tuple('a', 1, 2.0f); EXPECT_EQ(L"(a, 1, 2)", fmt::format(L"({})", fmt::join(t1, L", "))); // Testing lvalue tuple args int x = 4; std::tuple t2{'b', x}; EXPECT_EQ(L"b + 4", fmt::format(L"{}", fmt::join(t2, L" + "))); // Empty tuple std::tuple<> t3; EXPECT_EQ(L"", fmt::format(L"{}", fmt::join(t3, L"|"))); // Single element tuple std::tuple t4{4.0f}; EXPECT_EQ(L"4", fmt::format(L"{}", fmt::join(t4, L"/"))); } TEST(RangesTest, JoinInitializerList) { EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join({1, 2, 3}, ", "))); EXPECT_EQ("fmt rocks !", fmt::format("{}", fmt::join({"fmt", "rocks", "!"}, " "))); } 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 {}; } }; TEST(RangesTest, JoinSentinel) { zstring hello{"hello"}; EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format("{}", hello)); EXPECT_EQ("h_e_l_l_o", fmt::format("{}", fmt::join(hello, "_"))); } TEST(RangesTest, JoinRange) { noncopyable_range w(3u, 0); EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(w, ","))); EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(noncopyable_range(3u, 0), ","))); non_const_only_range x(3u, 0); EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(x, ","))); EXPECT_EQ( "0,0,0", fmt::format("{}", fmt::join(non_const_only_range(3u, 0), ","))); std::vector y(3u, 0); EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(y, ","))); EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(std::vector(3u, 0), ","))); const std::vector z(3u, 0); EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(z, ","))); } #endif // FMT_RANGES_TEST_ENABLE_JOIN