Skip to content

Commit

Permalink
Add fmt support for Tuple
Browse files Browse the repository at this point in the history
  • Loading branch information
danakj committed Jun 4, 2023
1 parent aa6226e commit 85f46df
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 1 deletion.
57 changes: 57 additions & 0 deletions subspace/tuple/tuple.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

#include <compare>
#include <concepts>
#include <utility> // TODO: Replace std::index_sequence.

#include "fmt/core.h"
#include "subspace/assertions/check.h"
#include "subspace/construct/default.h"
#include "subspace/macros/__private/compiler_bugs.h"
Expand All @@ -30,6 +32,8 @@
#include "subspace/num/num_concepts.h"
#include "subspace/ops/eq.h"
#include "subspace/ops/ord.h"
#include "subspace/string/__private/any_formatter.h"
#include "subspace/string/__private/format_to_stream.h"
#include "subspace/tuple/__private/storage.h"

namespace sus::tuple_type {
Expand Down Expand Up @@ -290,6 +294,59 @@ struct tuple_element<0, ::sus::tuple_type::Tuple<T, Types...>> {

} // namespace std

// fmt support.
template <class... Types, class Char>
struct fmt::formatter<::sus::tuple_type::Tuple<Types...>, Char> {
template <class ParseContext>
constexpr decltype(auto) parse(ParseContext& ctx) {
return ctx.begin();
}

template <class FormatContext>
constexpr auto format(const ::sus::tuple_type::Tuple<Types...>& tuple,
FormatContext& ctx) const {
auto out = ctx.out();
*out++ = static_cast<Char>('(');
ctx.advance_to(out);
out =
format_tuple(tuple, ctx, std::make_index_sequence<sizeof...(Types)>());
*out++ = static_cast<Char>(')');
return out;
}

private:
template <size_t... Is, class FormatContext>
static auto format_tuple(const ::sus::tuple_type::Tuple<Types...>& tuple,
FormatContext& ctx, std::index_sequence<Is...>) {
(..., format_value<Is>(tuple, ctx));
return ctx.out();
}

template <size_t I, class FormatContext>
static void format_value(const ::sus::tuple_type::Tuple<Types...>& tuple,
FormatContext& ctx) {
using ValueFormatter =
::sus::string::__private::AnyFormatter<decltype(tuple.template at<I>()),
Char>;
auto out = ValueFormatter().format(tuple.template at<I>(), ctx);
if constexpr (I < sizeof...(Types) - 1) {
*out++ = static_cast<Char>(',');
*out++ = static_cast<Char>(' ');
ctx.advance_to(out);
}
}
};

// Stream support (written out manually due to use of template pack).
namespace sus::tuple_type {
template <class... Types>
inline std::basic_ostream<char>& operator<<(std::basic_ostream<char>& stream,
const Tuple<Types...>& value) {
return ::sus::string::__private::format_to_stream(stream,
fmt::format("{}", value));
}
} // namespace sus::tuple_type

// Promote Tuple into the `sus` namespace.
namespace sus {
using ::sus::tuple_type::Tuple;
Expand Down
30 changes: 29 additions & 1 deletion subspace/tuple/tuple_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,9 @@ TEST(Tuple, StrongOrder) {
}

struct Weak final {
auto operator==(const Weak& o) const& noexcept { return a == o.a && b == o.b; }
auto operator==(const Weak& o) const& noexcept {
return a == o.a && b == o.b;
}
auto operator<=>(const Weak& o) const& noexcept {
if (a == o.a) return std::weak_ordering::equivalent;
if (a < o.a) return std::weak_ordering::less;
Expand Down Expand Up @@ -531,4 +533,30 @@ TEST(Tuple, Destroy) {
EXPECT_EQ(destroy.primitive_value, (((0u + 1u) * 1u + 2u) * 2u + 3u) * 3u);
}

TEST(Tuple, fmt) {
auto t3 = Tuple<int, float, char>::with(2, 3.f, 'c');
static_assert(fmt::is_formattable<decltype(t3), char>::value);
EXPECT_EQ(fmt::format("{}", t3), "(2, 3, c)");

struct NoFormat {
i32 a = 0x16ae3cf2;
};
static_assert(!fmt::is_formattable<NoFormat, char>::value);

auto tn = Tuple<NoFormat, NoFormat>::with(NoFormat(), NoFormat(0xf00d));
static_assert(fmt::is_formattable<decltype(tn), char>::value);
EXPECT_EQ(fmt::format("{}", tn), "(f2-3c-ae-16, 0d-f0-00-00)");
}

TEST(Tuple, Stream) {
std::stringstream s;
s << Tuple<int, float, char>::with(2, 3.f, 'c');
EXPECT_EQ(s.str(), "(2, 3, c)");
}

TEST(Tuple, GTest) {
EXPECT_EQ(testing::PrintToString(Tuple<int, float, char>::with(2, 3.f, 'c')),
"(2, 3, c)");
}

} // namespace

0 comments on commit 85f46df

Please sign in to comment.