Skip to content

Commit 5682338

Browse files
authored
Fix is_formattable for tuple-like types. (#2940)
1 parent f0de128 commit 5682338

File tree

2 files changed

+44
-2
lines changed

2 files changed

+44
-2
lines changed

include/fmt/ranges.h

+33-1
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,31 @@ template <size_t N>
202202
using make_index_sequence = make_integer_sequence<size_t, N>;
203203
#endif
204204

205+
template <typename T>
206+
using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
207+
208+
template <typename T, typename C, bool = is_tuple_like_<T>::value>
209+
class is_tuple_formattable_ {
210+
public:
211+
static constexpr const bool value = false;
212+
};
213+
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
214+
template <std::size_t... I>
215+
static std::true_type check2(index_sequence<I...>,
216+
integer_sequence<bool, (I == I)...>);
217+
static std::false_type check2(...);
218+
template <std::size_t... I>
219+
static decltype(check2(
220+
index_sequence<I...>{},
221+
integer_sequence<
222+
bool, (is_formattable<typename std::tuple_element<I, T>::type,
223+
C>::value)...>{})) check(index_sequence<I...>);
224+
225+
public:
226+
static constexpr const bool value =
227+
decltype(check(tuple_index_sequence<T>{}))::value;
228+
};
229+
205230
template <class Tuple, class F, size_t... Is>
206231
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) noexcept {
207232
using std::get;
@@ -283,8 +308,15 @@ template <typename T> struct is_tuple_like {
283308
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
284309
};
285310

311+
template <typename T, typename C> struct is_tuple_formattable {
312+
static constexpr const bool value =
313+
detail::is_tuple_formattable_<T, C>::value;
314+
};
315+
286316
template <typename TupleT, typename Char>
287-
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
317+
struct formatter<TupleT, Char,
318+
enable_if_t<fmt::is_tuple_like<TupleT>::value &&
319+
fmt::is_tuple_formattable<TupleT, Char>::value>> {
288320
private:
289321
// C++11 generic lambda for format().
290322
template <typename FormatContext> struct format_each {

test/ranges-test.cc

+11-1
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,22 @@ TEST(ranges_test, format_pair) {
8585
EXPECT_EQ(fmt::format("{}", p), "(42, 1.5)");
8686
}
8787

88+
struct unformattable {};
89+
8890
TEST(ranges_test, format_tuple) {
8991
auto t =
9092
std::tuple<int, float, std::string, char>(42, 1.5f, "this is tuple", 'i');
9193
EXPECT_EQ(fmt::format("{}", t), "(42, 1.5, \"this is tuple\", 'i')");
9294
EXPECT_EQ(fmt::format("{}", std::tuple<>()), "()");
95+
96+
EXPECT_TRUE((fmt::is_formattable<std::tuple<>>::value));
97+
EXPECT_FALSE((fmt::is_formattable<unformattable>::value));
98+
EXPECT_FALSE((fmt::is_formattable<std::tuple<unformattable>>::value));
99+
EXPECT_FALSE((fmt::is_formattable<std::tuple<unformattable, int>>::value));
100+
EXPECT_FALSE((fmt::is_formattable<std::tuple<int, unformattable>>::value));
101+
EXPECT_FALSE(
102+
(fmt::is_formattable<std::tuple<unformattable, unformattable>>::value));
103+
EXPECT_TRUE((fmt::is_formattable<std::tuple<int, float>>::value));
93104
}
94105

95106
#ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
@@ -220,7 +231,6 @@ TEST(ranges_test, enum_range) {
220231
}
221232

222233
#if !FMT_MSC_VERSION
223-
struct unformattable {};
224234

225235
TEST(ranges_test, unformattable_range) {
226236
EXPECT_FALSE((fmt::has_formatter<std::vector<unformattable>,

0 commit comments

Comments
 (0)