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
4 changes: 2 additions & 2 deletions .github/workflows/asciidoctor-ghpages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
env:
cache-name: cpm-cache-0
id: cpm-cache-restore
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: ~/cpm-cache
key: ${{runner.os}}-${{env.cache-name}}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
Expand All @@ -64,7 +64,7 @@ jobs:
env:
cache-name: cpm-cache-0
if: steps.cpm-cache-restore.outputs.cache-hit != 'true'
uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: ~/cpm-cache
key: ${{runner.os}}-${{env.cache-name}}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
Expand Down
58 changes: 42 additions & 16 deletions include/stdx/tuple.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <stdx/type_traits.hpp>
#include <stdx/udls.hpp>
#include <stdx/utility.hpp>

Expand Down Expand Up @@ -335,18 +336,6 @@ struct tuple_impl<std::index_sequence<Is...>, index_function_list<Fs...>, Ts...>
constexpr static auto size =
std::integral_constant<std::size_t, sizeof...(Ts)>{};

[[nodiscard]] constexpr static auto fill_inner_indices(index_pair *p)
-> index_pair * {
((p++->inner = Is), ...);
return p;
}
[[nodiscard]] constexpr static auto
fill_outer_indices(index_pair *p, [[maybe_unused]] std::size_t n)
-> index_pair * {
((p++->outer = (static_cast<void>(Is), n)), ...);
return p;
}

private:
template <typename Funcs, typename... Us>
requires(... and std::equality_comparable_with<Ts, Us>)
Expand Down Expand Up @@ -407,18 +396,56 @@ tuple_impl(Ts...)
template <typename T> constexpr auto tuple_size_v = T::size();
template <typename T, std::size_t N>
constexpr auto tuple_size_v<std::array<T, N>> = N;
template <typename T, typename U>
constexpr auto tuple_size_v<std::pair<T, U>> = std::size_t{2};
template <typename T, T N>
constexpr auto tuple_size_v<make_integer_sequence<T, N>> = std::size_t{N};

template <std::size_t I, typename T>
using tuple_element_t = typename T::template element_t<I>;

template <typename T>
concept tuple_comparable = requires { typename T::common_tuple_comparable; };

template <typename T>
concept tuplelike = requires { typename remove_cvref_t<T>::is_tuple; };

template <std::size_t I, typename T> struct tuple_element;

template <std::size_t I, tuplelike T> struct tuple_element<I, T> {
using type = typename T::template element_t<I>;
};

template <std::size_t I, typename T, std::size_t N>
struct tuple_element<I, std::array<T, N>> {
using type = T;
};

template <std::size_t I, typename T, typename U>
struct tuple_element<I, std::pair<T, U>> {
using type = nth_t<I, T, U>;
};

template <std::size_t I, typename T>
using tuple_element_t = typename tuple_element<I, T>::type;

namespace detail {
template <typename T>
concept has_vacuous_tuple_protocol = requires {
{
tuple_size_v<std::remove_cvref_t<T>>
} -> std::same_as<std::size_t const &>;
};
template <typename T>
concept is_vacuous_tuple = tuple_size_v<std::remove_cvref_t<T>> == 0;
} // namespace detail

template <typename T>
concept has_tuple_protocol =
detail::has_vacuous_tuple_protocol<T> and
(detail::is_vacuous_tuple<T> or requires(T &t) {
{
get<0>(t)
} -> std::same_as<tuple_element_t<0, std::remove_cvref_t<T>> &>;
});

template <typename... Ts>
class tuple : public detail::tuple_impl<std::index_sequence_for<Ts...>,
detail::index_function_list<>, Ts...> {
Expand Down Expand Up @@ -524,6 +551,5 @@ class one_of : public detail::tuple_impl<std::index_sequence_for<Ts...>,
}
};
template <typename... Ts> one_of(Ts...) -> one_of<Ts...>;

} // namespace v1
} // namespace stdx
39 changes: 31 additions & 8 deletions include/stdx/tuple_algorithms.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,49 @@

namespace stdx {
inline namespace v1 {
template <typename F, tuplelike... Ts> constexpr auto apply(F &&f, Ts &&...ts) {
namespace detail {
template <has_tuple_protocol T>
[[nodiscard]] constexpr auto fill_inner_indices([[maybe_unused]] index_pair *p)
-> index_pair * {
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
((p++->inner = Is), ...);
return p;
}(std::make_index_sequence<tuple_size_v<T>>{});
}

template <has_tuple_protocol T>
[[nodiscard]] constexpr auto fill_outer_indices([[maybe_unused]] index_pair *p,
[[maybe_unused]] std::size_t n)
-> index_pair * {
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
((p++->outer = (static_cast<void>(Is), n)), ...);
return p;
}(std::make_index_sequence<tuple_size_v<T>>{});
}
} // namespace detail

template <typename F, has_tuple_protocol... Ts>
constexpr auto apply(F &&f, Ts &&...ts) {
constexpr auto total_num_elements =
(std::size_t{} + ... + stdx::tuple_size_v<std::remove_cvref_t<Ts>>);

[[maybe_unused]] constexpr auto element_indices =
[&]() -> std::array<detail::index_pair, total_num_elements> {
std::array<detail::index_pair, total_num_elements> indices{};
[[maybe_unused]] auto p = indices.data();
((p = std::remove_cvref_t<Ts>::fill_inner_indices(p)), ...);
((p = detail::fill_inner_indices<std::remove_cvref_t<Ts>>(p)), ...);
[[maybe_unused]] auto q = indices.data();
[[maybe_unused]] std::size_t n{};
((q = std::remove_cvref_t<Ts>::fill_outer_indices(q, n++)), ...);
((q = detail::fill_outer_indices<std::remove_cvref_t<Ts>>(q, n++)),
...);
return indices;
}();

[[maybe_unused]] auto outer_tuple =
stdx::tuple<Ts &&...>{std::forward<Ts>(ts)...};
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return std::forward<F>(f)(
std::move(outer_tuple)[index<element_indices[Is].outer>]
[index<element_indices[Is].inner>]...);
return std::forward<F>(f)(get<element_indices[Is].inner>(
get<element_indices[Is].outer>(std::move(outer_tuple)))...);
}(std::make_index_sequence<total_num_elements>{});
}

Expand All @@ -51,10 +73,11 @@ template <tuplelike... Ts> [[nodiscard]] constexpr auto tuple_cat(Ts &&...ts) {
[&]() -> std::array<detail::index_pair, total_num_elements> {
std::array<detail::index_pair, total_num_elements> indices{};
auto p = indices.data();
((p = std::remove_cvref_t<Ts>::fill_inner_indices(p)), ...);
((p = detail::fill_inner_indices<std::remove_cvref_t<Ts>>(p)), ...);
auto q = indices.data();
std::size_t n{};
((q = std::remove_cvref_t<Ts>::fill_outer_indices(q, n++)), ...);
((q = detail::fill_outer_indices<std::remove_cvref_t<Ts>>(q, n++)),
...);
return indices;
}();

Expand Down
10 changes: 10 additions & 0 deletions test/indexed_tuple.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ template <typename Key, typename Value> struct map_entry {
template <typename T> using key_for = typename T::key_t;
} // namespace

TEST_CASE("indexed_tuple is tuplelike", "[tuple]") {
auto t = stdx::indexed_tuple{1, 2, 3};
STATIC_CHECK(stdx::tuplelike<decltype(t)>);
}

TEST_CASE("indexed_tuple has tuple protocol", "[tuple]") {
auto t = stdx::indexed_tuple{1, 2, 3};
STATIC_CHECK(stdx::has_tuple_protocol<decltype(t)>);
}

TEST_CASE("make_indexed_tuple", "[indexed_tuple]") {
STATIC_REQUIRE(stdx::make_indexed_tuple<>() == stdx::indexed_tuple{});
STATIC_REQUIRE(stdx::make_indexed_tuple<>(1, 2, 3) ==
Expand Down
24 changes: 24 additions & 0 deletions test/tuple.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <catch2/catch_template_test_macros.hpp>
#include <catch2/catch_test_macros.hpp>

#include <array>
#include <compare>
#include <type_traits>
#include <utility>
Expand Down Expand Up @@ -44,6 +45,29 @@ TEST_CASE("multi element tuple", "[tuple]") {
STATIC_CHECK(T::size() == 2);
}

TEST_CASE("tuple is tuplelike", "[tuple]") {
auto t = stdx::tuple{1, 2, 3};
STATIC_CHECK(stdx::tuplelike<decltype(t)>);
STATIC_CHECK(stdx::tuplelike<stdx::tuple<>>);
}

TEST_CASE("tuple has tuple protocol", "[tuple]") {
auto t = stdx::tuple{1, 2, 3};
STATIC_CHECK(stdx::has_tuple_protocol<decltype(t)>);
STATIC_CHECK(stdx::has_tuple_protocol<stdx::tuple<>>);
}

TEST_CASE("std::array has tuple protocol", "[tuple_algorithms]") {
auto t = std::array{1, 2, 3};
STATIC_CHECK(stdx::has_tuple_protocol<decltype(t)>);
STATIC_CHECK(stdx::has_tuple_protocol<std::array<int, 0>>);
}

TEST_CASE("std::pair has tuple protocol", "[tuple_algorithms]") {
auto t = std::pair{1, 2};
STATIC_CHECK(stdx::has_tuple_protocol<decltype(t)>);
}

namespace {
template <auto N> struct empty {};
} // namespace
Expand Down
28 changes: 28 additions & 0 deletions test/tuple_algorithms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,16 @@ TEST_CASE("apply preserves argument order", "[tuple_algorithms]") {
CHECK(called == 1);
}

TEST_CASE("apply works on std::array", "[tuple_algorithms]") {
STATIC_REQUIRE(stdx::apply([](auto... xs) { return (0 + ... + xs); },
std::array{1, 2, 3, 4}) == 10);
}

TEST_CASE("apply works on std::pair", "[tuple_algorithms]") {
STATIC_REQUIRE(stdx::apply([](auto... xs) { return (0 + ... + xs); },
std::pair{1, 2}) == 3);
}

TEST_CASE("variadic apply", "[tuple_algorithms]") {
STATIC_REQUIRE(stdx::apply([](auto... xs) { return (0 + ... + xs); }) == 0);
STATIC_REQUIRE(stdx::apply([](auto... xs) { return (0 + ... + xs); },
Expand Down Expand Up @@ -161,6 +171,24 @@ TEST_CASE("variadic apply preserves argument order", "[tuple_algorithms]") {
CHECK(called == 1);
}

TEST_CASE("variadic apply works on std::array", "[tuple_algorithms]") {
STATIC_REQUIRE(stdx::apply([](auto... xs) { return (0 + ... + xs); },
std::array{1, 2, 3, 4},
std::array{1, 2, 3, 4, 5}) == 25);
}

TEST_CASE("variadic apply works on std::pair", "[tuple_algorithms]") {
STATIC_REQUIRE(stdx::apply([](auto... xs) { return (0 + ... + xs); },
std::pair{1, 2}, std::pair{3, 4}) == 10);
}

TEST_CASE("variadic apply works on heterogeneous arguments",
"[tuple_algorithms]") {
STATIC_REQUIRE(stdx::apply([](auto... xs) { return (0 + ... + xs); },
std::array{1, 2}, std::pair{3, 4},
stdx::tuple{5, 6}) == 21);
}

TEST_CASE("join", "[tuple_algorithms]") {
constexpr auto t = stdx::tuple{1, 2, 3};
STATIC_REQUIRE(t.join(std::plus{}) == 6);
Expand Down