Skip to content
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
10 changes: 10 additions & 0 deletions src/cpputils/type_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ namespace cpputils {
template<std::size_t i>
using index_constant = std::integral_constant<std::size_t, i>;

//! Instance of a compile-time index
template<std::size_t i>
inline constexpr index_constant<i> ic{};

//! Type trait to check equality of two values
template<auto a, auto b> struct is_equal : std::bool_constant<(a == b)> {};

//! Type trait to check if a is smaller than b
template<auto a, auto b> struct is_less : std::bool_constant<(a < b)> {};

//! Wraps a type trait to evaluate it with the decay_t of a given type
template<template<typename> typename trait>
struct decayed_trait {
Expand Down
127 changes: 127 additions & 0 deletions src/cpputils/utility.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <type_traits>
#include <concepts>
#include <utility>
#include <ostream>

#include <cpputils/type_traits.hpp>

Expand Down Expand Up @@ -109,4 +110,130 @@ struct indexed_tuple : detail::indexed_tuple<std::make_index_sequence<sizeof...(
template<typename... Ts>
indexed_tuple(Ts&&...) -> indexed_tuple<Ts...>;

#ifndef DOXYGEN
namespace detail {

template<std::size_t i, auto v>
struct value_i {
static constexpr auto at(index_constant<i>) noexcept {
return v;
}
};

template<typename I, auto...>
struct values;
template<std::size_t... i, auto... v> requires(sizeof...(i) == sizeof...(v))
struct values<std::index_sequence<i...>, v...> : value_i<i, v>... {
using value_i<i, v>::at...;
};

template<auto... v>
struct value_list_helper {};

template<std::size_t i, std::size_t n, auto... values>
struct drop_n;
template<std::size_t i, std::size_t n, auto v0, auto... v> requires(i < n)
struct drop_n<i, n, v0, v...> : drop_n<i+1, n, v...> {};
template<std::size_t i, std::size_t n, auto... v> requires(i == n)
struct drop_n<i, n, v...> : std::type_identity<value_list_helper<v...>> {};

} // namespace detail
#endif // DOXYGEN

//! Class to represent a list of compile-time values.
template<auto... v>
struct values : detail::values<std::make_index_sequence<sizeof...(v)>, 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<std::size_t n> requires(n <= sizeof...(v))
static constexpr auto drop() noexcept {
return [] <auto... _v> (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<std::size_t n> requires(n <= sizeof...(v))
static constexpr auto crop() noexcept {
return [] <std::size_t... i> (const std::index_sequence<i...>&) constexpr {
return values<at(index_constant<i>{})...>{};
}(std::make_index_sequence<sizeof...(v) - n>{});
}

//! Return a new list with the first n values from this list
template<std::size_t n> requires(n <= sizeof...(v))
static constexpr auto take() noexcept {
return [] <std::size_t... i> (const std::index_sequence<i...>&) constexpr {
return values<at(index_constant<i>{})...>{};
}(std::make_index_sequence<n>{});
}

//! 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<size-1>{});
}

//! Return the value at the given index in the list
template<std::size_t i> requires(i < size)
static constexpr auto at(index_constant<i> idx) noexcept {
using base = detail::values<std::make_index_sequence<size>, v...>;
return base::at(idx);
}

//! Perform a reduction operation on this list
template<typename op, typename T>
static constexpr auto reduce_with(op&& action, T&& initial) noexcept {
return _reduce_with(std::forward<op>(action), std::forward<T>(initial), v...);
}

//! Concatenate this list with another one
template<auto... _v>
constexpr auto operator+(const values<_v...>&) const {
return values<v..., _v...>{};
}

//! Test this list for equality with another one
template<auto... _v>
constexpr bool operator==(const values<_v...>&) const {
if constexpr (sizeof...(_v) == size)
return std::conjunction_v<is_equal<v, _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<v...>(s);
return s;
}

private:
template<auto v0, auto... vs>
static void _write_to(std::ostream& s) {
s << std::to_string(v0);
(..., (s << ", " << std::to_string(vs)));
}

template<typename op, typename T>
static constexpr auto _reduce_with(op&&, T&& initial) noexcept {
return std::forward<T>(initial);
}

template<typename op, typename T, typename V0, typename... V>
static constexpr auto _reduce_with(op&& action, T&& initial, V0&& v0, V&&... values) noexcept {
auto next = action(std::forward<T>(initial), std::forward<V0>(v0));
if constexpr (sizeof...(V) == 0) {
return next;
} else {
return _reduce_with(std::forward<op>(action), std::move(next), std::forward<V>(values)...);
}
}
};

} // namespace cpputils
8 changes: 8 additions & 0 deletions test/test_type_traits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int, char, int, double, int, double>;
static_assert(unique::size == 3);
Expand Down
67 changes: 67 additions & 0 deletions test/test_utility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int>{42}};
Expand Down Expand Up @@ -63,7 +64,73 @@ int main() {
static_assert(tuple.index_of(double{}).value == 2);
static_assert(tuple.template index_of<double>().value == 2);
expect(eq(tuple.get(tuple.template index_of<double>()), 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<decltype(sum)>,
cpputils::values<0, 1, 2, 3, 4>
>);
};

return EXIT_SUCCESS;
Expand Down
Loading