Skip to content

Commit

Permalink
define printer for ranges, types without stream insertion (#6)
Browse files Browse the repository at this point in the history
Change-Id: I60bf6c70b610d38da41bc1bd55a9c3f4fac42b2d
  • Loading branch information
oliverlee authored Dec 24, 2023
1 parent 5199eab commit aeff88f
Show file tree
Hide file tree
Showing 11 changed files with 215 additions and 92 deletions.
3 changes: 2 additions & 1 deletion BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ cc_library(
"src/aborts.hpp",
"src/cfg.hpp",
"src/default_printer.hpp",
"src/detail/arg_fmt.hpp",
"src/detail/is_defined.hpp",
"src/detail/is_range.hpp",
"src/detail/is_specialization_of.hpp",
"src/detail/predicate.hpp",
"src/detail/priority.hpp",
"src/detail/relation.hpp",
"src/detail/remove_cvref.hpp",
"src/detail/trim_substring.hpp",
"src/detail/tuple_fmt.hpp",
"src/detail/type_name.hpp",
"src/expect.hpp",
"src/pred.hpp",
Expand Down
5 changes: 3 additions & 2 deletions src/default_printer.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include "src/detail/arg_fmt.hpp"
#include "src/detail/priority.hpp"
#include "src/result.hpp"
#include "src/utility.hpp"
Expand Down Expand Up @@ -47,10 +48,10 @@ class default_printer
};

os() << "(";
(*this) << std::get<0>(r.args);
(*this) << detail::arg_fmt(std::get<0>(r.args));

os() << " " << Relation::predicate_type::symbol << " ";
(*this) << std::get<1>(r.args);
(*this) << detail::arg_fmt(std::get<1>(r.args));

os() << ")" << colors::none;

Expand Down
107 changes: 107 additions & 0 deletions src/detail/arg_fmt.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#pragma once

#include "src/detail/is_range.hpp"
#include "src/detail/priority.hpp"
#include "src/detail/type_name.hpp"
#include "src/rope.hpp"

#include <cstddef>
#include <ostream>
#include <tuple>
#include <utility>

namespace skytest::detail {
template <class R>
struct range_fmt;
template <class... Ts>
struct tuple_fmt;

class arg_fmt_fn
{
struct ostream_inserter
{
template <class T>
auto operator()(const T& value)
-> decltype(std::declval<std::ostream&>() << value);
};

template <class T>
static constexpr auto is_ostreamable_v =
std::is_invocable_r_v<std::ostream&, ostream_inserter, const T&>;

template <
class R,
std::enable_if_t<is_range_v<R> and not is_ostreamable_v<R>, bool> = true>
static constexpr auto impl(priority<3>, const R& arg)
{
return range_fmt<R>{arg};
}

template <
class... Ts,
std::enable_if_t<not is_ostreamable_v<std::tuple<Ts...>>, bool> = true>
static constexpr auto impl(priority<2>, const std::tuple<Ts...>& arg)
{
return tuple_fmt<Ts...>{arg};
}

template <class T, std::enable_if_t<is_ostreamable_v<T>, bool> = true>
static constexpr auto& impl(priority<1>, const T& arg)
{
return arg;
}

template <class T>
static constexpr auto impl(priority<0>, const T&)
{
return rope<2>{type_name<T>, "{...}"};
}

public:
template <class T>
constexpr decltype(auto) operator()(const T & arg) const
{
return impl(priority<3>{}, arg);
}
};
inline constexpr auto arg_fmt = arg_fmt_fn{};

template <class R>
struct range_fmt
{
const R& range;

friend auto& operator<<(std::ostream& os, const range_fmt& f)
{
auto first = true;

os << "[";
for (const auto& value : f.range) {
os << (std::exchange(first, false) ? "" : ", ") << value;
}
os << "]";
return os;
}
};

template <class... Ts>
struct tuple_fmt
{
const std::tuple<Ts...>& value;

template <std::size_t... Is>
auto& stream_insert(std::index_sequence<Is...>, std::ostream& os) const
{
std::ignore =
((os << (Is == 0 ? "" : ", ") << arg_fmt(std::get<Is>(value)), true) and
...);
return os;
}

friend auto& operator<<(std::ostream& os, const tuple_fmt& tup)
{
return tup.stream_insert(std::index_sequence_for<Ts...>{}, os);
}
};

} // namespace skytest::detail
23 changes: 23 additions & 0 deletions src/detail/is_range.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#pragma once

#include <type_traits>
#include <utility>

namespace skytest::detail {

template <class T, class = void>
struct is_range : std::false_type
{};
template <class T>
struct is_range<
T,
std::enable_if_t<
decltype(std::declval<T&>().begin(),
std::declval<T&>().end(),
std::true_type{})::value>> : std::true_type
{};

template <class T>
inline constexpr auto is_range_v = is_range<T>::value;

} // namespace skytest::detail
10 changes: 5 additions & 5 deletions src/detail/relation.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once

#include "src/detail/tuple_fmt.hpp"
#include "src/detail/arg_fmt.hpp"
#include "src/detail/type_name.hpp"
#include "src/utility.hpp"

Expand Down Expand Up @@ -53,22 +53,22 @@ struct relation
os << predicate_type::name << "{}";
}

os << "(" << fmt(r.args) << ")";
os << "(" << arg_fmt(r.args) << ")";
return os;
}
static auto& print(notation::prefix, std::ostream& os, const relation& r)
{
static_assert(sizeof...(Ts) != 0);

os << "(" << predicate_type::symbol << " " << fmt(r.args) << ")";
os << "(" << predicate_type::symbol << " " << arg_fmt(r.args) << ")";
return os;
}
static auto& print(notation::infix, std::ostream& os, const relation& r)
{
static_assert(sizeof...(Ts) == 2);

os << "(" << std::get<0>(r.args) << " " << predicate_type::symbol << " "
<< std::get<1>(r.args) << ")";
os << "(" << arg_fmt(std::get<0>(r.args)) << " " << predicate_type::symbol
<< " " << arg_fmt(std::get<1>(r.args)) << ")";
return os;
}

Expand Down
35 changes: 0 additions & 35 deletions src/detail/tuple_fmt.hpp

This file was deleted.

1 change: 1 addition & 0 deletions src/rope.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ struct rope
friend auto& operator<<(std::ostream& os, const rope& r)
{
os << rope_ref{r};
return os;
}
};

Expand Down
16 changes: 1 addition & 15 deletions src/test_param.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "src/detail/is_defined.hpp"
#include "src/detail/is_range.hpp"
#include "src/detail/priority.hpp"
#include "src/detail/remove_cvref.hpp"
#include "src/detail/trim_substring.hpp"
Expand Down Expand Up @@ -32,21 +33,6 @@ namespace detail {
template <std::size_t I>
using constant = std::integral_constant<std::size_t, I>;

template <class T, class = void>
struct is_range : std::false_type
{};
template <class T>
struct is_range<
T,
std::enable_if_t<
decltype(std::declval<T&>().begin(),
std::declval<T&>().end(),
std::true_type{})::value>> : std::true_type
{};

template <class T>
inline constexpr auto is_range_v = is_range<T>::value;

template <class T, class = void>
struct has_static_value : std::false_type
{};
Expand Down
11 changes: 11 additions & 0 deletions test/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,17 @@ skytest_test(
],
)

skytest_test(
name = "arg_fmt_test",
srcs = ["arg_fmt_test.cpp"],
return_code = 1,
stdout = [
"empty(\\[1, 2, 3\\])",
"empty({4}{5})",
"wrapped{...} == wrapped{...}",
],
)

skytest_test(
name = "constexpr_test",
srcs = ["constexpr_test.cpp"],
Expand Down
54 changes: 54 additions & 0 deletions test/arg_fmt_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include "skytest/skytest.hpp"

#include <array>
#include <cstddef>
#include <iterator>
#include <ostream>
#include <string_view>

struct empty_desc
{
using notation_type = skytest::notation::function;
static constexpr auto symbol = std::string_view{"empty"};
};
constexpr auto empty = skytest::pred(empty_desc{}, [](const auto& rng) {
return std::empty(rng);
});

template <class T, std::size_t N>
struct array : std::array<T, N>
{
friend auto& operator<<(std::ostream& os, const array& arr)
{
for (const auto& x : arr) {
os << "{" << x << "}";
}
return os;
}
};

struct wrapped
{
int value;

friend constexpr auto operator==(wrapped, wrapped) { return false; }
};

auto main() -> int
{
using namespace ::skytest::literals;
using ::skytest::eq;
using ::skytest::expect;

"array uses default range arg fmt"_test = [] {
return expect(empty(std::array{1, 2, 3}));
};

"custom array uses custom fmt"_test = [] {
return expect(empty(array<int, 2>{4, 5}));
};

"type without operator<< is displayed"_test = [] {
return expect(eq(wrapped{1}, wrapped{2}));
};
}
Loading

0 comments on commit aeff88f

Please sign in to comment.