Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make ranges-test available with C++11 #2114

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ template <typename T> inline T* make_checked(T* p, size_t) { return p; }
#endif

template <typename Container, FMT_ENABLE_IF(is_contiguous<Container>::value)>
#if FMT_CLANG_VERSION
#if FMT_CLANG_VERSION >= 307
__attribute__((no_sanitize("undefined")))
Copy link
Contributor Author

@alexezeder alexezeder Jan 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like there is no no_sanitize attribute prior to Clang 3.7
Maybe a bit unrelated to this PR, but it was found while I was working on it

#endif
inline checked_ptr<typename Container::value_type>
Expand Down Expand Up @@ -940,8 +940,8 @@ template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data {
static const char reset_color[5];
static const wchar_t wreset_color[5];
static const char signs[];
static constexpr const char left_padding_shifts[] = {31, 31, 0, 1, 0};
static constexpr const char right_padding_shifts[] = {0, 31, 0, 1, 0};
static constexpr const char left_padding_shifts[5] = {31, 31, 0, 1, 0};
static constexpr const char right_padding_shifts[5] = {0, 31, 0, 1, 0};
Copy link
Contributor Author

@alexezeder alexezeder Jan 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some GCC versions fail to determine the size of this array - https://godbolt.org/z/KbhMGb
Maybe a bit unrelated to this PR, but it was found while I was working on it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


// DEPRECATED! These are for ABI compatibility.
static const uint32_t zero_or_powers_of_10_32[];
Expand Down
2 changes: 1 addition & 1 deletion include/fmt/ranges.h
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ template <typename Char, typename... T> struct tuple_arg_join : detail::view {
basic_string_view<Char> sep;

tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
: tuple{t}, sep{s} {}
: tuple(t), sep{s} {}
Copy link
Contributor Author

@alexezeder alexezeder Jan 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GCC 4.8 fails to initialize tuple ref by braces - https://godbolt.org/z/qxq5eK

};

template <typename Char, typename... T>
Expand Down
180 changes: 95 additions & 85 deletions test/ranges-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,23 @@

#include "fmt/ranges.h"

#include <array>
#include <map>
#include <string>
#include <vector>

#include "gtest.h"

// Check if 'if constexpr' is supported.
#if (__cplusplus > 201402L) || \
(defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 601
# define FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
#endif
Copy link
Contributor Author

@alexezeder alexezeder Jan 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's strange but looks like GCC <6 unable to pass C-style array correctly, so {fmt} is unable right now to format them - https://godbolt.org/z/MYYf6W


# include <array>
# include <map>
# include <string>
# include <vector>
#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);
Expand All @@ -34,6 +40,12 @@ TEST(RangesTest, Format2dArray) {
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<int32_t> iv{1, 2, 3, 5, 7, 11};
auto ivf = fmt::format("{}", iv);
Expand All @@ -51,11 +63,6 @@ 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<int64_t, float> pa1{42, 1.5f};
EXPECT_EQ("(42, 1.5)", fmt::format("{}", pa1));
Expand All @@ -68,43 +75,21 @@ TEST(RangesTest, FormatTuple) {
EXPECT_EQ("()", fmt::format("{}", std::tuple<>()));
}

TEST(RangesTest, JoinTuple) {
// Value tuple args
std::tuple<char, int, float> 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<char, int&> 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<float> t4{4.0f};
EXPECT_EQ("4", fmt::format("{}", fmt::join(t4, "/")));
}

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", "!"}, " ")));
}

#ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
struct my_struct {
int32_t i;
std::string str; // can throw
template <size_t N> decltype(auto) get() const noexcept {
if constexpr (N == 0)
return i;
else if constexpr (N == 1)
return fmt::string_view{str};
template <size_t N> fmt::enable_if_t<N == 0, int32_t> get() const noexcept {
return i;
}
template <size_t N>
fmt::enable_if_t<N == 1, fmt::string_view> get() const noexcept {
return {str};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No C++14 decltype(auto) (probably was unneeded anyway) and C++17 constexpr if, only C++11 code

}
};

template <size_t N> decltype(auto) get(const my_struct& s) noexcept {
template <size_t N>
auto get(const my_struct& s) noexcept -> decltype(s.get<N>()) {
return s.get<N>();
}

Expand All @@ -122,10 +107,11 @@ 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});
auto end = fmt::format_to(buf, "{}", std::vector<int>{1, 2, 3});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No C++17 template deduction

*end = '\0';
EXPECT_STREQ(buf, "{1, 2, 3}");
}
Expand All @@ -141,9 +127,6 @@ TEST(RangesTest, PathLike) {
EXPECT_FALSE((fmt::is_range<path_like, char>::value));
}

#endif // (__cplusplus > 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >
// 201402L && _MSC_VER >= 1910)

#ifdef FMT_USE_STRING_VIEW
struct string_like {
const char* begin();
Expand All @@ -157,23 +140,6 @@ TEST(RangesTest, FormatStringLike) {
}
#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 {}; }
};

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, "_")));
}

// A range that provides non-const only begin()/end() to test fmt::join handles
// that
//
Expand Down Expand Up @@ -212,27 +178,6 @@ template <typename T> class noncopyable_range {
const_iterator end() const { return vec.end(); }
};

TEST(RangesTest, JoinRange) {
noncopyable_range<int> w(3u, 0);
EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(w, ",")));
EXPECT_EQ("0,0,0",
fmt::format("{}", fmt::join(noncopyable_range<int>(3u, 0), ",")));

non_const_only_range<int> 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<int>(3u, 0), ",")));

std::vector<int> y(3u, 0);
EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(y, ",")));
EXPECT_EQ("0,0,0",
fmt::format("{}", fmt::join(std::vector<int>(3u, 0), ",")));

const std::vector<int> z(3u, 0);
EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(z, ",")));
}

TEST(RangesTest, Range) {
noncopyable_range<int> w(3u, 0);
EXPECT_EQ("{0, 0, 0}", fmt::format("{}", w));
Expand All @@ -258,3 +203,68 @@ TEST(RangesTest, UnformattableRange) {
fmt::format_context>::value));
}
#endif

#ifdef FMT_RANGES_TEST_ENABLE_JOIN
TEST(RangesTest, JoinTuple) {
// Value tuple args
std::tuple<char, int, float> 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<char, int&> 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<float> t4{4.0f};
EXPECT_EQ("4", fmt::format("{}", fmt::join(t4, "/")));
}

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<int> w(3u, 0);
EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(w, ",")));
EXPECT_EQ("0,0,0",
fmt::format("{}", fmt::join(noncopyable_range<int>(3u, 0), ",")));

non_const_only_range<int> 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<int>(3u, 0), ",")));

std::vector<int> y(3u, 0);
EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(y, ",")));
EXPECT_EQ("0,0,0",
fmt::format("{}", fmt::join(std::vector<int>(3u, 0), ",")));

const std::vector<int> z(3u, 0);
EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(z, ",")));
}
#endif // FMT_RANGES_TEST_ENABLE_JOIN