From f6e48644868df122c9e9a41859a1d01408917945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Gl=C3=A4ser?= Date: Thu, 21 Nov 2024 13:42:42 +0100 Subject: [PATCH 1/2] [traits] add traits for value eq/less --- src/cpputils/type_traits.hpp | 6 ++++++ test/test_type_traits.cpp | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/src/cpputils/type_traits.hpp b/src/cpputils/type_traits.hpp index 952c1b9..0033ebe 100644 --- a/src/cpputils/type_traits.hpp +++ b/src/cpputils/type_traits.hpp @@ -10,6 +10,12 @@ namespace cpputils { template using index_constant = std::integral_constant; +//! Type trait to check equality of two values +template struct is_equal : std::bool_constant<(a == b)> {}; + +//! Type trait to check if a is smaller than b +template struct is_less : std::bool_constant<(a < b)> {}; + //! Wraps a type trait to evaluate it with the decay_t of a given type template typename trait> struct decayed_trait { diff --git a/test/test_type_traits.cpp b/test/test_type_traits.cpp index 913151a..d4c9bbd 100644 --- a/test/test_type_traits.cpp +++ b/test/test_type_traits.cpp @@ -9,6 +9,14 @@ struct incomplete; int main() { + { + static_assert(cpputils::is_equal<1, 1>::value); + static_assert(!cpputils::is_equal<0, 1>::value); + + static_assert(!cpputils::is_less<1, 1>::value); + static_assert(!cpputils::is_less<1, 0>::value); + static_assert(cpputils::is_less<0, 1>::value); + } { using unique = cpputils::unique_t; static_assert(unique::size == 3); From 7c7d2c22f757efd84dd55e4c27e883c1ff51a4be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20Gl=C3=A4ser?= Date: Thu, 21 Nov 2024 13:59:48 +0100 Subject: [PATCH 2/2] [utils] value list type --- src/cpputils/type_traits.hpp | 4 ++ src/cpputils/utility.hpp | 127 +++++++++++++++++++++++++++++++++++ test/test_utility.cpp | 67 ++++++++++++++++++ 3 files changed, 198 insertions(+) diff --git a/src/cpputils/type_traits.hpp b/src/cpputils/type_traits.hpp index 0033ebe..c825e34 100644 --- a/src/cpputils/type_traits.hpp +++ b/src/cpputils/type_traits.hpp @@ -10,6 +10,10 @@ namespace cpputils { template using index_constant = std::integral_constant; +//! Instance of a compile-time index +template +inline constexpr index_constant ic{}; + //! Type trait to check equality of two values template struct is_equal : std::bool_constant<(a == b)> {}; diff --git a/src/cpputils/utility.hpp b/src/cpputils/utility.hpp index d079f1d..948a58c 100644 --- a/src/cpputils/utility.hpp +++ b/src/cpputils/utility.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -109,4 +110,130 @@ struct indexed_tuple : detail::indexed_tuple indexed_tuple(Ts&&...) -> indexed_tuple; +#ifndef DOXYGEN +namespace detail { + + template + struct value_i { + static constexpr auto at(index_constant) noexcept { + return v; + } + }; + + template + struct values; + template requires(sizeof...(i) == sizeof...(v)) + struct values, v...> : value_i... { + using value_i::at...; + }; + + template + struct value_list_helper {}; + + template + struct drop_n; + template requires(i < n) + struct drop_n : drop_n {}; + template requires(i == n) + struct drop_n : std::type_identity> {}; + +} // namespace detail +#endif // DOXYGEN + +//! Class to represent a list of compile-time values. +template +struct values : detail::values, v...> { + static constexpr std::size_t size = sizeof...(v); + + //! Return a new list with the values from this list, dropping the first n values + template requires(n <= sizeof...(v)) + static constexpr auto drop() noexcept { + return [] (const detail::value_list_helper<_v...>&) constexpr { + return values<_v...>{}; + }(typename detail::drop_n<0, n, v...>::type{}); + } + + //! Return a new list with the values from this list, dropping the last n values + template requires(n <= sizeof...(v)) + static constexpr auto crop() noexcept { + return [] (const std::index_sequence&) constexpr { + return values{})...>{}; + }(std::make_index_sequence{}); + } + + //! Return a new list with the first n values from this list + template requires(n <= sizeof...(v)) + static constexpr auto take() noexcept { + return [] (const std::index_sequence&) constexpr { + return values{})...>{}; + }(std::make_index_sequence{}); + } + + //! Return the first value in the list + static constexpr auto first() noexcept { + return at(index_constant<0>{}); + } + + //! Return the last value in the list + static constexpr auto last() noexcept { + return at(index_constant{}); + } + + //! Return the value at the given index in the list + template requires(i < size) + static constexpr auto at(index_constant idx) noexcept { + using base = detail::values, v...>; + return base::at(idx); + } + + //! Perform a reduction operation on this list + template + static constexpr auto reduce_with(op&& action, T&& initial) noexcept { + return _reduce_with(std::forward(action), std::forward(initial), v...); + } + + //! Concatenate this list with another one + template + constexpr auto operator+(const values<_v...>&) const { + return values{}; + } + + //! Test this list for equality with another one + template + constexpr bool operator==(const values<_v...>&) const { + if constexpr (sizeof...(_v) == size) + return std::conjunction_v...>; + return false; + } + + //! Write this list to the given output stream + friend std::ostream& operator<<(std::ostream& s, const values&) { + if constexpr (size > 0) + _write_to(s); + return s; + } + + private: + template + static void _write_to(std::ostream& s) { + s << std::to_string(v0); + (..., (s << ", " << std::to_string(vs))); + } + + template + static constexpr auto _reduce_with(op&&, T&& initial) noexcept { + return std::forward(initial); + } + + template + static constexpr auto _reduce_with(op&& action, T&& initial, V0&& v0, V&&... values) noexcept { + auto next = action(std::forward(initial), std::forward(v0)); + if constexpr (sizeof...(V) == 0) { + return next; + } else { + return _reduce_with(std::forward(action), std::move(next), std::forward(values)...); + } + } +}; + } // namespace cpputils diff --git a/test/test_utility.cpp b/test/test_utility.cpp index eae6893..ca71283 100644 --- a/test/test_utility.cpp +++ b/test/test_utility.cpp @@ -10,6 +10,7 @@ int main() { using boost::ut::operator""_test; using boost::ut::expect; using boost::ut::eq; + using cpputils::ic; "value_or_reference_by_temporary"_test = [] () { cpputils::value_or_reference storage{std::vector{42}}; @@ -63,7 +64,73 @@ int main() { static_assert(tuple.index_of(double{}).value == 2); static_assert(tuple.template index_of().value == 2); expect(eq(tuple.get(tuple.template index_of()), 12.0)); + }; + + "value_list_access"_test = [] () { + constexpr cpputils::values<0, 1, 2> values; + static_assert(values.at(ic<0>) == 0); + static_assert(values.at(ic<1>) == 1); + static_assert(values.at(ic<2>) == 2); + + static_assert(values.first() == 0); + static_assert(values.last() == 2); + }; + + "value_list_drop_n"_test = [] () { + constexpr cpputils::values<0, 1, 2> values; + constexpr auto dropped = values.template drop<1>(); + static_assert(dropped.size == 2); + static_assert(dropped.at(ic<0>) == 1); + static_assert(dropped.at(ic<1>) == 2); + + constexpr auto empty = values.template drop<3>(); + static_assert(empty.size == 0); + }; + + "value_list_crop_n"_test = [] () { + constexpr cpputils::values<0, 1, 2> values; + constexpr auto cropped = values.template crop<1>(); + static_assert(cropped.size == 2); + static_assert(cropped.at(ic<0>) == 0); + static_assert(cropped.at(ic<1>) == 1); + + constexpr auto empty = values.template crop<3>(); + static_assert(empty.size == 0); + }; + "value_list_take_n"_test = [] () { + constexpr cpputils::values<0, 1, 2> values; + constexpr auto firsts = values.template take<2>(); + static_assert(firsts.size == 2); + static_assert(firsts.at(ic<0>) == 0); + static_assert(firsts.at(ic<1>) == 1); + }; + + "value_list_equality"_test = [] () { + constexpr cpputils::values<0, 1, 2> values; + static_assert(values == cpputils::values<0, 1, 2>{}); + }; + + "value_list_stream"_test = [] () { + std::ostringstream s; + s << cpputils::values<0, 1, 2>{}; + expect(eq(s.str(), std::string{"0, 1, 2"})); + }; + + "value_list_reduce"_test = [] () { + constexpr cpputils::values<0, 1, 2> values; + static_assert(values.reduce_with(std::plus{}, 0) == 3); + static_assert(values.reduce_with(std::multiplies{}, 1) == 0); + }; + + "value_list_concat"_test = [] () { + constexpr cpputils::values<0, 1, 2> values0; + constexpr cpputils::values<3, 4> values1; + constexpr auto sum = values0 + values1; + static_assert(std::is_same_v< + std::remove_cvref_t, + cpputils::values<0, 1, 2, 3, 4> + >); }; return EXIT_SUCCESS;