diff --git a/include/iris/alloy/adapt.hpp b/include/iris/alloy/adapt.hpp index f3ebf23..e6133c4 100644 --- a/include/iris/alloy/adapt.hpp +++ b/include/iris/alloy/adapt.hpp @@ -3,33 +3,35 @@ // SPDX-License-Identifier: MIT +#include // IWYU pragma: keep + +#include // IWYU pragma: keep + #include #include #include namespace iris::alloy { -namespace detail { - -template -struct non_type_list; - -} // detail - template struct adaptor; -template -using make_getters_list = detail::non_type_list; - } // iris::alloy -#define IRIS_ALLOY_ADAPT_STRUCT(class_name, ...) \ - template<> \ - struct iris::alloy::adaptor { \ - using getters_list = make_getters_list; \ +#define IRIS_ALLOY_ADAPT_STRUCT_I(index, data_member, class_name) \ + IRIS_PP_COMMA_IF(index) & class_name::data_member + +#define IRIS_ALLOY_ADAPT_STRUCT(class_name, ...) \ + template<> \ + struct iris::alloy::adaptor \ + { \ + using getters_list = iris::constant_list< \ + IRIS_PP_SEQ_FOR_EACH_WITH_INDEX( \ + IRIS_PP_TUPLE_TO_SEQ((__VA_ARGS__)), \ + IRIS_ALLOY_ADAPT_STRUCT_I, \ + class_name \ + ) \ + >; \ }; -#define IRIS_ALLOY_ADAPT_STRUCT_I(index, data_member, class_name) IRIS_PP_COMMA_IF(index) & class_name::data_member - #endif diff --git a/include/iris/alloy/adapted/std_pair.hpp b/include/iris/alloy/adapted/std_pair.hpp index cc4b9d9..186aa0b 100644 --- a/include/iris/alloy/adapted/std_pair.hpp +++ b/include/iris/alloy/adapted/std_pair.hpp @@ -3,16 +3,54 @@ // SPDX-License-Identifier: MIT -#include +#include // IWYU pragma: keep + +#include + +#include // IWYU pragma: export + +#include namespace iris::alloy { -namespace detail { +template +struct tuple_size; + +template +struct tuple_size> : std::integral_constant +{}; + +template +struct tuple_size const> : std::integral_constant +{}; + +template +struct tuple_size volatile> : std::integral_constant +{}; + +template +struct tuple_size const volatile> : std::integral_constant +{}; + +template +struct tuple_element; + +template +struct tuple_element> : std::tuple_element> +{}; + +template +struct tuple_element const> : std::tuple_element const> +{}; + +template +struct tuple_element volatile> : std::tuple_element volatile> +{}; -template -struct non_type_list; +template +struct tuple_element const volatile> : std::tuple_element const volatile> +{}; -} // detail template struct adaptor; @@ -20,7 +58,7 @@ struct adaptor; template struct adaptor> { - using getters_list = detail::non_type_list<&std::pair::first, &std::pair::second>; + using getters_list = detail::call_std_get>::type; }; } // iris::alloy diff --git a/include/iris/alloy/adapted/std_tuple.hpp b/include/iris/alloy/adapted/std_tuple.hpp index 1325dc2..f62f31a 100644 --- a/include/iris/alloy/adapted/std_tuple.hpp +++ b/include/iris/alloy/adapted/std_tuple.hpp @@ -3,9 +3,11 @@ // SPDX-License-Identifier: MIT -#include +#include // IWYU pragma: keep -#include +#include + +#include // IWYU pragma: export #include #include @@ -13,32 +15,51 @@ namespace iris::alloy { template -struct adaptor; +struct tuple_size; -namespace detail { +template +struct tuple_size> : std::integral_constant +{}; -template -struct call_std_get -{ - template - static constexpr decltype(auto) operator()(Tuple&& t) - { - return std::get(static_cast(t)); - } -}; +template +struct tuple_size const> : std::integral_constant +{}; -template -struct make_call_std_get -{ - static constexpr auto value = call_std_get{}; -}; +template +struct tuple_size volatile> : std::integral_constant +{}; -} // detail +template +struct tuple_size const volatile> : std::integral_constant +{}; + +template +struct tuple_element; + +template +struct tuple_element> : std::tuple_element> +{}; + +template +struct tuple_element const> : std::tuple_element const> +{}; + +template +struct tuple_element volatile> : std::tuple_element volatile> +{}; + +template +struct tuple_element const volatile> : std::tuple_element const volatile> +{}; + + +template +struct adaptor; template struct adaptor> { - using getters_list = detail::integer_seq_transform_t, detail::make_call_std_get>; + using getters_list = detail::call_std_get>::type; }; } // iris::alloy diff --git a/include/iris/alloy/detail/deduce.hpp b/include/iris/alloy/detail/deduce.hpp index 3e650ff..18e5e58 100644 --- a/include/iris/alloy/detail/deduce.hpp +++ b/include/iris/alloy/detail/deduce.hpp @@ -3,6 +3,8 @@ // SPDX-License-Identifier: MIT +#include // IWYU pragma: keep + #include namespace iris::alloy::detail { @@ -10,8 +12,11 @@ namespace iris::alloy::detail { template struct deduce { - static_assert(std::conjunction_v, std::is_reference, - std::is_same, std::remove_reference_t>>); + static_assert(std::conjunction_v< + std::is_reference, + std::is_reference, + std::is_same, std::remove_reference_t> + >); }; template @@ -33,7 +38,7 @@ struct deduce }; template -using deduce_t = typename deduce::type; +using deduce_t = deduce::type; } // iris::alloy::detail diff --git a/include/iris/alloy/detail/integer_seq_transform.hpp b/include/iris/alloy/detail/integer_seq_transform.hpp deleted file mode 100644 index b7c033c..0000000 --- a/include/iris/alloy/detail/integer_seq_transform.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef IRIS_ZZ_ALLOY_DETAIL_INTEGER_SEQ_TRANSFORM_HPP -#define IRIS_ZZ_ALLOY_DETAIL_INTEGER_SEQ_TRANSFORM_HPP - -// SPDX-License-Identifier: MIT - -#include - -namespace iris::alloy::detail { - -template -struct non_type_list; - -template class F> -struct integer_seq_transform; - -template class F> -struct integer_seq_transform, F> -{ - using type = detail::non_type_list::value...>; -}; - -template class F> -using integer_seq_transform_t = typename integer_seq_transform::type; - -} // iris::alloy::detail - -#endif diff --git a/include/iris/alloy/detail/std_get.hpp b/include/iris/alloy/detail/std_get.hpp new file mode 100644 index 0000000..cc149af --- /dev/null +++ b/include/iris/alloy/detail/std_get.hpp @@ -0,0 +1,37 @@ +#ifndef IRIS_ZZ_ALLOY_DETAIL_STD_GET_HPP +#define IRIS_ZZ_ALLOY_DETAIL_STD_GET_HPP + +// SPDX-License-Identifier: MIT + +#include // IWYU pragma: keep + +#include + +#include + +#include // IWYU pragma: keep + +namespace iris::alloy::detail { + +template +struct call_std_get_impl +{ + template + [[nodiscard]] static constexpr decltype(auto) operator()(Tuple&& t) noexcept + { + return std::get(static_cast(t)); + } +}; + +template +struct call_std_get; + +template +struct call_std_get> +{ + using type = constant_list{}...>; +}; + +} // iris::alloy::detail + +#endif diff --git a/include/iris/alloy/detail/tuple_comparison.hpp b/include/iris/alloy/detail/tuple_comparison.hpp index 33c3375..88b7514 100644 --- a/include/iris/alloy/detail/tuple_comparison.hpp +++ b/include/iris/alloy/detail/tuple_comparison.hpp @@ -3,9 +3,10 @@ // SPDX-License-Identifier: MIT +#include // IWYU pragma: keep + #include -#include #include namespace iris::alloy { diff --git a/include/iris/alloy/detail/tuple_impl.hpp b/include/iris/alloy/detail/tuple_impl.hpp index 042d15f..4100180 100644 --- a/include/iris/alloy/detail/tuple_impl.hpp +++ b/include/iris/alloy/detail/tuple_impl.hpp @@ -5,7 +5,7 @@ #ifndef IRIS_ALLOY_GENERATE_PREPROCESSED -#include +#include // IWYU pragma: keep #include @@ -15,7 +15,7 @@ #include -#include +#include // IWYU pragma: keep #endif diff --git a/include/iris/alloy/io.hpp b/include/iris/alloy/io.hpp index f4aa7ab..5cfa6fc 100644 --- a/include/iris/alloy/io.hpp +++ b/include/iris/alloy/io.hpp @@ -3,6 +3,8 @@ // SPDX-License-Identifier: MIT +#include // IWYU pragma: keep + #include #include @@ -10,7 +12,7 @@ #include #include -#include +#include // IWYU pragma: keep namespace iris::alloy { diff --git a/include/iris/alloy/traits.hpp b/include/iris/alloy/traits.hpp index d70909e..edc5858 100644 --- a/include/iris/alloy/traits.hpp +++ b/include/iris/alloy/traits.hpp @@ -3,6 +3,8 @@ // SPDX-License-Identifier: MIT +#include // IWYU pragma: keep + #include #include @@ -17,22 +19,6 @@ namespace iris::alloy { template struct adaptor; -namespace detail { - -template -struct non_type_list_size {}; - -template class TList, auto... Vs> -struct non_type_list_size> : std::integral_constant {}; - -template -struct non_type_list_indexing {}; - -template class TList, auto... Vs> -struct non_type_list_indexing> : cpack_indexing {}; - -} // detail - struct value_initialize_t {}; inline constexpr value_initialize_t value_initialize{}; @@ -40,9 +26,6 @@ inline constexpr value_initialize_t value_initialize{}; template class tuple; -template -struct adaptor; - namespace detail { template @@ -71,11 +54,17 @@ struct tuple_size {}; template struct tuple_size : tuple_size {}; +template +struct tuple_size : tuple_size {}; + +template +struct tuple_size : tuple_size {}; + template struct tuple_size> : std::integral_constant {}; template -struct tuple_size : detail::non_type_list_size::getters_list> {}; +struct tuple_size : std::integral_constant>::getters_list::size> {}; template inline constexpr std::size_t tuple_size_v = tuple_size::value; @@ -89,8 +78,27 @@ struct tuple_element> using type = IRIS_CORE_PACK_INDEXING(I, Ts...); }; +template +struct tuple_element const> +{ + using type = tuple_element>::type const; +}; + +template +struct tuple_element volatile> +{ + using type = tuple_element>::type volatile; +}; + +template +struct tuple_element const volatile> +{ + using type = tuple_element>::type const volatile; +}; + + template -using tuple_element_t = typename tuple_element::type; +using tuple_element_t = tuple_element::type; template [[nodiscard]] constexpr tuple_element_t>& get(tuple& t) noexcept; @@ -106,17 +114,42 @@ template namespace detail { +template +struct adaptor_getter_t_impl; + +template +struct adaptor_getter_t_impl +{ + static constexpr auto getter = Getter; + using getter_type = decltype(Getter); + using type = decltype(std::invoke(Getter, std::declval())); +}; + +template +struct adaptor_getter_t_impl + : adaptor_getter_t_impl +{}; + +template +struct adaptor_getter_t_dispatch; + +template +struct adaptor_getter_t_dispatch> +{ + using type = adaptor_getter_t_impl; +}; + template -inline constexpr auto getter_of = non_type_list_indexing::getters_list>::value; +using adaptor_getter_t = adaptor_getter_t_dispatch>::getters_list>::type; -} // namespace detail +} // detail template -[[nodiscard]] constexpr auto get(T&& x) - noexcept(std::is_nothrow_invocable_v>), T>) - -> std::invoke_result_t>), T> +[[nodiscard]] constexpr detail::adaptor_getter_t::type +get(T&& x) + noexcept(std::is_nothrow_invocable_v::getter_type, T>) { - return std::invoke(detail::getter_of>, std::forward(x)); + return std::invoke(detail::adaptor_getter_t::getter, std::forward(x)); } namespace detail { @@ -189,7 +222,7 @@ struct basic_common_reference_impl{}.rbegin()` would // inevitably *check* `TupleLike` for `std::pair` thus -// leads to instantiation of `getter_of`. Then if the instantiation +// leads to instantiation of `alloy::get`. Then if the instantiation // yields hard error for some reason, the error is propagated to the // caller in SFINAE-unfriendly context. template class TQual, template class UQual> diff --git a/include/iris/alloy/tuple.hpp b/include/iris/alloy/tuple.hpp index 1e6a3c3..0dbdf00 100644 --- a/include/iris/alloy/tuple.hpp +++ b/include/iris/alloy/tuple.hpp @@ -3,6 +3,8 @@ // SPDX-License-Identifier: MIT +#include // IWYU pragma: keep + #ifndef IRIS_USE_PREPROCESSED #define IRIS_USE_PREPROCESSED 1 #endif @@ -13,7 +15,6 @@ #include #endif -#include #include #include @@ -25,9 +26,6 @@ namespace iris::alloy { namespace detail { -template -struct type_list; - template struct tuple_traits_impl; diff --git a/include/iris/alloy/utility.hpp b/include/iris/alloy/utility.hpp index 9b13cbd..d757202 100644 --- a/include/iris/alloy/utility.hpp +++ b/include/iris/alloy/utility.hpp @@ -3,10 +3,15 @@ // SPDX-License-Identifier: MIT +#include // IWYU pragma: keep + #include #include -#include +#include +#include + +#include // std::invoke #include #include @@ -16,9 +21,6 @@ namespace iris::alloy { namespace detail { -template -struct type_list; - template struct tuple_cat_result_impl; @@ -30,10 +32,20 @@ struct tuple_cat_result_impl, type_list<>> template struct tuple_cat_result_impl, type_list, IndexSeqs...>, Tuple, Tuples...> - : tuple_cat_result_impl>...>, type_list, Tuples...> {}; + : tuple_cat_result_impl< + type_list>...>, + type_list, + Tuples... +> +{}; template -struct tuple_cat_result : tuple_cat_result_impl, type_list>>...>, Tuples...> {}; +struct tuple_cat_result : tuple_cat_result_impl< + type_list<>, + type_list>>...>, + Tuples... +> +{}; template struct tuple_cat_impl_base; @@ -56,25 +68,31 @@ struct tuple_cat_impl_base, In { template static constexpr bool nothrow = - tuple_cat_impl_base, Tuples...>::template nothrow && (is_nothrow_gettable_v && ...); + tuple_cat_impl_base, Tuples...>::template nothrow && + (is_nothrow_gettable_v && ...); template static constexpr ResultTuple apply(Tuple&& tuple, Tuples&&... tuples, Args&&... args) noexcept(nothrow) { - return tuple_cat_impl_base, Tuples...>::apply(std::forward(tuples)..., std::forward(args)..., - alloy::get(std::forward(tuple))...); + return tuple_cat_impl_base, Tuples...>::apply( + std::forward(tuples)..., std::forward(args)..., + alloy::get(std::forward(tuple))... + ); } }; template struct tuple_cat_impl { - using Base = tuple_cat_impl_base::type, - detail::type_list>>...>, Tuples...>; + using Base = tuple_cat_impl_base< + typename tuple_cat_result::type, + type_list>>...>, + Tuples... + >; static constexpr bool nothrow = Base::template nothrow<>; - static constexpr typename tuple_cat_result::type apply(Tuples&&... tuples) noexcept(nothrow) + static constexpr tuple_cat_result::type apply(Tuples&&... tuples) noexcept(nothrow) { return Base::apply(std::forward(tuples)...); } @@ -102,20 +120,20 @@ struct index_sequence_split : index_sequence_split_impl template struct index_sequence_take { - using type = typename index_sequence_split::head; + using type = index_sequence_split::head; }; template -using index_sequence_take_t = typename index_sequence_take::type; +using index_sequence_take_t = index_sequence_take::type; template struct index_sequence_drop { - using type = typename index_sequence_split::tail; + using type = index_sequence_split::tail; }; template -using index_sequence_drop_t = typename index_sequence_drop::type; +using index_sequence_drop_t = index_sequence_drop::type; template struct index_sequence_subrange @@ -124,7 +142,7 @@ struct index_sequence_subrange }; template -using index_sequence_subrange_t = typename index_sequence_subrange::type; +using index_sequence_subrange_t = index_sequence_subrange::type; template struct index_sequence_sum; @@ -152,7 +170,7 @@ struct index_sequence_cumulative_sum> : index_sequence_cumulative_sum_impl, std::index_sequence> {}; template -using index_sequence_cumulative_sum_t = typename index_sequence_cumulative_sum::type; +using index_sequence_cumulative_sum_t = index_sequence_cumulative_sum::type; template struct index_sequence_segment_impl; @@ -171,12 +189,15 @@ struct index_sequence_segment, Sizes...> { using CumSumIndexSeq = index_sequence_cumulative_sum_t>; - using type = typename index_sequence_segment_impl, index_sequence_take_t, - index_sequence_drop_t<1, CumSumIndexSeq>>::type; + using type = index_sequence_segment_impl< + std::index_sequence, + index_sequence_take_t, + index_sequence_drop_t<1, CumSumIndexSeq> + >::type; }; template -using index_sequence_segment_t = typename index_sequence_segment::type; +using index_sequence_segment_t = index_sequence_segment::type; template struct tuple_from_tuple_and_index_sequence; @@ -188,7 +209,7 @@ struct tuple_from_tuple_and_index_sequence> }; template -using tuple_from_tuple_and_index_sequence_t = typename tuple_from_tuple_and_index_sequence::type; +using tuple_from_tuple_and_index_sequence_t = tuple_from_tuple_and_index_sequence::type; template struct tuple_split_result_impl; @@ -202,7 +223,7 @@ struct tuple_split_result_impl> template struct tuple_split_result { - using type = typename tuple_split_result_impl>>, Sizes...>>::type; + using type = tuple_split_result_impl>>, Sizes...>>::type; }; template @@ -237,8 +258,12 @@ struct tuple_split_make_outer, Tuple, type_list -struct tuple_split_impl : tuple_split_make_outer::type, Tuple, - index_sequence_segment_t>>, Sizes...>> {}; +struct tuple_split_impl : tuple_split_make_outer< + typename tuple_split_result::type, + Tuple, + index_sequence_segment_t>>, Sizes...> +> +{}; template>>> struct tuple_assign_impl; @@ -246,8 +271,10 @@ struct tuple_assign_impl; template struct tuple_assign_impl> { - static constexpr bool nothrow = std::conjunction_v, is_nothrow_gettable>..., - std::is_nothrow_assignable, tuple_get_t>...>; + static constexpr bool nothrow = std::conjunction_v< + std::conjunction, is_nothrow_gettable>..., + std::is_nothrow_assignable, tuple_get_t>... + >; static constexpr void apply(From&& from, To&& to) noexcept(nothrow) { @@ -274,21 +301,35 @@ template struct for_each_impl> { template - static constexpr void apply(Tuple&& t, F&& f){ + static constexpr void apply(Tuple&& t, F&& f) + noexcept(std::conjunction_v< + std::is_nothrow_invocable>... + >) + { ((void)std::invoke(std::forward(f), alloy::get(std::forward(t))), ...); } + + template + requires std::invocable, tuple_get_t<0, Tuple>> + static constexpr void apply(Tuple&& t, F&& f) + noexcept(std::conjunction_v< + std::is_nothrow_invocable, tuple_get_t>... + >) + { + ((void)std::invoke(std::forward(f), std::integral_constant{}, alloy::get(std::forward(t))), ...); + } }; } // detail template -using tuple_cat_t = typename detail::tuple_cat_result::type; +using tuple_cat_t = detail::tuple_cat_result::type; template -using tuple_split_t = typename detail::tuple_split_result::type; +using tuple_split_t = detail::tuple_split_result::type; template -using tuple_ref_t = typename detail::tuple_ref_result::type; +using tuple_ref_t = detail::tuple_ref_result::type; template [[nodiscard]] constexpr tuple_cat_t tuple_cat(Tuples&&... tuples) noexcept(detail::tuple_cat_impl::nothrow) @@ -318,10 +359,104 @@ template template constexpr void for_each(Tuple&& t, F&& f) + noexcept(noexcept(detail::for_each_impl>>>::apply(std::forward(t), std::forward(f)))) { return detail::for_each_impl>>>::apply(std::forward(t), std::forward(f)); } +namespace detail { + +template +concept invocable_with_index = + std::invocable, tuple_get_t<0, Tuple>>; + +template +struct visit_at_return_type +{ + using type = decltype( + std::invoke(std::declval(), std::declval>()) + ); +}; + +template + requires invocable_with_index +struct visit_at_return_type +{ + using type = decltype( + std::invoke(std::declval(), std::declval>(), std::declval>()) + ); +}; + +template +struct visit_at_noexcept_impl +{ + static constexpr bool value = noexcept( + std::invoke(std::declval(), std::declval>()) + ); +}; + +template + requires invocable_with_index +struct visit_at_noexcept_impl +{ + static constexpr bool value = noexcept( + std::invoke(std::declval(), std::declval>(), std::declval>()) + ); +}; + +template +struct visit_at_noexcept_impl_dispatch; + +template +struct visit_at_noexcept_impl_dispatch, F> + : std::conjunction...> +{}; + +template +constexpr bool visit_at_all_noexcept = visit_at_noexcept_impl_dispatch< + Tuple, + std::make_index_sequence>, + F +>::value; + +} // detail + +// `f(auto&& elem)` +template + requires (!detail::invocable_with_index) +IRIS_FORCEINLINE constexpr detail::visit_at_return_type::type +visit_at(std::size_t const i, F&& f, Tuple&& tup) + noexcept(detail::visit_at_all_noexcept) +{ + return iris::invoke_with_index>( + i, + [&](std::integral_constant) constexpr + noexcept(noexcept(std::invoke(std::declval(), std::declval>()))) + -> detail::visit_at_return_type::type + { + return std::invoke(std::forward(f), alloy::get(std::forward(tup))); + } + ); +} + +// `f(std::integral_constant, auto&& elem)` +template + requires detail::invocable_with_index +IRIS_FORCEINLINE constexpr detail::visit_at_return_type::type +visit_at(std::size_t const i, F&& f, Tuple&& tup) + noexcept(detail::visit_at_all_noexcept) +{ + return iris::invoke_with_index>( + i, + [&](std::integral_constant) constexpr + noexcept(noexcept(std::invoke(std::declval(), std::declval>(), std::declval>()))) + -> detail::visit_at_return_type::type + { + return std::invoke(std::forward(f), std::integral_constant{}, alloy::get(std::forward(tup))); + } + ); +} + } // iris::alloy #endif diff --git a/include/iris/io_fwd.hpp b/include/iris/io_fwd.hpp index a5ea198..f4d1ecb 100644 --- a/include/iris/io_fwd.hpp +++ b/include/iris/io_fwd.hpp @@ -3,6 +3,8 @@ // SPDX-License-Identifier: MIT +#include // IWYU pragma: keep + #include #include #include diff --git a/include/iris/rvariant/detail/rvariant_fwd.hpp b/include/iris/rvariant/detail/rvariant_fwd.hpp index 7d01ed6..5077324 100644 --- a/include/iris/rvariant/detail/rvariant_fwd.hpp +++ b/include/iris/rvariant/detail/rvariant_fwd.hpp @@ -3,7 +3,9 @@ // SPDX-License-Identifier: MIT -#include +#include // IWYU pragma: keep + +#include // IWYU pragma: keep #if !defined(IRIS_RVARIANT_VISIT_STRENGTHEN) # define IRIS_RVARIANT_VISIT_STRENGTHEN 1 diff --git a/include/iris/rvariant/recursive_wrapper.hpp b/include/iris/rvariant/recursive_wrapper.hpp index c5b3e06..72a58d1 100644 --- a/include/iris/rvariant/recursive_wrapper.hpp +++ b/include/iris/rvariant/recursive_wrapper.hpp @@ -3,6 +3,8 @@ // SPDX-License-Identifier: MIT +#include // IWYU pragma: keep + #include #include diff --git a/include/iris/rvariant/rvariant.hpp b/include/iris/rvariant/rvariant.hpp index 9d05b3b..a05b6ec 100644 --- a/include/iris/rvariant/rvariant.hpp +++ b/include/iris/rvariant/rvariant.hpp @@ -3,6 +3,8 @@ // SPDX-License-Identifier: MIT +#include // IWYU pragma: keep + #include #include #include diff --git a/include/iris/type_traits.hpp b/include/iris/type_traits.hpp index fcce5ad..fde5bd8 100644 --- a/include/iris/type_traits.hpp +++ b/include/iris/type_traits.hpp @@ -61,6 +61,12 @@ struct type_list static constexpr std::size_t size = sizeof...(Ts); }; +template +struct constant_list +{ + static constexpr std::size_t size = sizeof...(Vals); +}; + template using cvoid_t = void; namespace detail { diff --git a/include/iris/utility.hpp b/include/iris/utility.hpp new file mode 100644 index 0000000..2e2d3ce --- /dev/null +++ b/include/iris/utility.hpp @@ -0,0 +1,173 @@ +#ifndef IRIS_ZZ_UTILITY_HPP +#define IRIS_ZZ_UTILITY_HPP + +// SPDX-License-Identifier: MIT + +#include // IWYU pragma: keep + +#include // IWYU pragma: export +#include +#include +#include // std::invoke + +#include + +namespace iris { + +namespace detail::adl_get { + +using std::get; + +template +concept gettable_impl = requires(T&& t) { + get(std::forward(t)); +}; + +} // detail::adl_get + +template +concept gettable = detail::adl_get::gettable_impl; + + +namespace detail { + +template +using invoke_with_index_result_t = decltype( + std::invoke(std::declval(), std::declval>()) +); + +template +struct invoke_with_index_noexcept_impl +{ + static constexpr bool value = noexcept( + std::invoke(std::declval(), std::declval>()) + ); +}; + +template +struct invoke_with_index_noexcept_impl_dispatch; + +template +struct invoke_with_index_noexcept_impl_dispatch, F> + : std::conjunction...> +{}; + +template +constexpr bool invoke_with_index_all_noexcept = invoke_with_index_noexcept_impl_dispatch< + std::make_index_sequence, + F +>::value; + +template +struct invoke_with_index_impl; + +template +struct invoke_with_index_table; + +template +struct invoke_with_index_table> +{ + static constexpr bool all_noexcept = invoke_with_index_all_noexcept; + + static constexpr auto table = std::array{ + +[](F&& f) static constexpr noexcept(all_noexcept) -> invoke_with_index_result_t { + return std::invoke(std::forward(f), std::integral_constant{}); + }... + }; +}; + +template<> +struct invoke_with_index_impl<-1> +{ + template + [[nodiscard]] IRIS_FORCEINLINE static constexpr invoke_with_index_result_t + apply(std::size_t const i, F&& f) + noexcept(invoke_with_index_all_noexcept) + { + assert(i < N); + return std::invoke( + invoke_with_index_table>::table[i], + std::forward(f) + ); + } +}; + +#define IRIS_INVOKE_WITH_INDEX_CASE(n) \ + case (n): \ + if constexpr ((n) < N) { \ + return static_cast(f)(std::integral_constant{}); \ + } else std::unreachable(); [[fallthrough]] + +#define IRIS_INVOKE_WITH_INDEX_DEF(strategy) \ + template<> \ + struct invoke_with_index_impl<(strategy)> \ + { \ + template \ + [[nodiscard]] IRIS_FORCEINLINE static constexpr invoke_with_index_result_t \ + apply(std::size_t const i, [[maybe_unused]] F&& f) \ + noexcept(invoke_with_index_all_noexcept) \ + { \ + switch (i) { \ + IRIS_INVOKE_WITH_INDEX_CASES_ ## strategy (IRIS_INVOKE_WITH_INDEX_CASE, 0); \ + default: std::unreachable(); \ + } \ + } \ + }; + + +// We choose not to introduce the "256" branch, since we usually have a +// limited amount of statically sized array (or tuple) to "dispatch", +// in contrast to `variant` which ordinarily bloats the combination +// count for multi-visits. +template +constexpr int invoke_with_index_strategy = + N <= 4 ? 0 : + N <= 16 ? 1 : + N <= 64 ? 2 : + -1; + +#define IRIS_INVOKE_WITH_INDEX_CASES_0(def, ofs) \ + def(ofs); \ + def((ofs) + 1); \ + def((ofs) + 2); \ + def((ofs) + 3) + +#define IRIS_INVOKE_WITH_INDEX_CASES_1(def, ofs) \ + IRIS_INVOKE_WITH_INDEX_CASES_0(def, ofs); \ + IRIS_INVOKE_WITH_INDEX_CASES_0(def, (ofs) + 4); \ + IRIS_INVOKE_WITH_INDEX_CASES_0(def, (ofs) + 8); \ + IRIS_INVOKE_WITH_INDEX_CASES_0(def, (ofs) + 12) + +#define IRIS_INVOKE_WITH_INDEX_CASES_2(def, ofs) \ + IRIS_INVOKE_WITH_INDEX_CASES_1(def, ofs); \ + IRIS_INVOKE_WITH_INDEX_CASES_1(def, (ofs) + 16); \ + IRIS_INVOKE_WITH_INDEX_CASES_1(def, (ofs) + 32); \ + IRIS_INVOKE_WITH_INDEX_CASES_1(def, (ofs) + 48) + +IRIS_INVOKE_WITH_INDEX_DEF(0); +IRIS_INVOKE_WITH_INDEX_DEF(1); +IRIS_INVOKE_WITH_INDEX_DEF(2); + +#undef IRIS_INVOKE_WITH_INDEX_CASES_0 +#undef IRIS_INVOKE_WITH_INDEX_CASES_1 +#undef IRIS_INVOKE_WITH_INDEX_CASES_2 + +#undef IRIS_INVOKE_WITH_INDEX_CASE +#undef IRIS_INVOKE_WITH_INDEX_DEF + +} // detail + +// Invokes `F` with static index `std::integral_constant` deduced by runtime index `i`. +template +IRIS_FORCEINLINE constexpr detail::invoke_with_index_result_t +invoke_with_index(std::size_t i, F&& f) + noexcept(detail::invoke_with_index_all_noexcept) +{ + return detail::invoke_with_index_impl>::template apply( + i, std::forward(f) + ); +} + +} // iris + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2d4e6dd..aaa1f4d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -192,6 +192,7 @@ if(PROJECT_IS_TOP_LEVEL) interval_set snippet alloy + alloy_visit ) foreach(test_name IN LISTS IRIS_TEST_IRIS_TESTS) iris_define_internal_test(${test_name} ${test_name}.cpp) diff --git a/test/alloy.cpp b/test/alloy.cpp index dc366e5..0794154 100644 --- a/test/alloy.cpp +++ b/test/alloy.cpp @@ -12,6 +12,8 @@ #include #include +#include + namespace alloy = iris::alloy; struct NonAdaptedStruct @@ -36,7 +38,7 @@ struct OldStyle template<> struct alloy::adaptor { - using getters_list = make_getters_list<&OldStyle::get_int, &OldStyle::get_string>; + using getters_list = iris::constant_list<&OldStyle::get_int, &OldStyle::get_string>; }; template @@ -60,15 +62,15 @@ TEST_CASE("adapt_struct") STATIC_CHECK(alloy::tuple_size_v == 2); - STATIC_CHECK(std::is_same_v, int&>); - STATIC_CHECK(std::is_same_v, int const&>); - STATIC_CHECK(std::is_same_v, int&&>); - STATIC_CHECK(std::is_same_v, int const&&>); + STATIC_CHECK(std::same_as, int&>); + STATIC_CHECK(std::same_as, int const&>); + STATIC_CHECK(std::same_as, int&&>); + STATIC_CHECK(std::same_as, int const&&>); - STATIC_CHECK(std::is_same_v, double&>); - STATIC_CHECK(std::is_same_v, double const&>); - STATIC_CHECK(std::is_same_v, double&&>); - STATIC_CHECK(std::is_same_v, double const&&>); + STATIC_CHECK(std::same_as, double&>); + STATIC_CHECK(std::same_as, double const&>); + STATIC_CHECK(std::same_as, double&&>); + STATIC_CHECK(std::same_as, double const&&>); constexpr AdaptedStruct a{42, 3.14}; @@ -81,10 +83,10 @@ TEST_CASE("adapt_struct") STATIC_CHECK(alloy::tuple_size_v == 2); - STATIC_CHECK(std::is_same_v, int>); - STATIC_CHECK(std::is_same_v, int>); - STATIC_CHECK(std::is_same_v, std::string const&>); - STATIC_CHECK(std::is_same_v, std::string const&>); + STATIC_CHECK(std::same_as, int>); + STATIC_CHECK(std::same_as, int>); + STATIC_CHECK(std::same_as, std::string const&>); + STATIC_CHECK(std::same_as, std::string const&>); } } @@ -97,21 +99,46 @@ TEST_CASE("adapt_std_pair") STATIC_CHECK(alloy::tuple_size_v == 2); - STATIC_CHECK(std::is_same_v, int&>); - STATIC_CHECK(std::is_same_v, int const&>); - STATIC_CHECK(std::is_same_v, int&&>); - STATIC_CHECK(std::is_same_v, int const&&>); + STATIC_CHECK(std::same_as, int&>); + STATIC_CHECK(std::same_as, int const&>); + STATIC_CHECK(std::same_as, int&&>); + STATIC_CHECK(std::same_as, int const&&>); - STATIC_CHECK(std::is_same_v, double&>); - STATIC_CHECK(std::is_same_v, double const&>); - STATIC_CHECK(std::is_same_v, double&&>); - STATIC_CHECK(std::is_same_v, double const&&>); + STATIC_CHECK(std::same_as, double&>); + STATIC_CHECK(std::same_as, double const&>); + STATIC_CHECK(std::same_as, double&&>); + STATIC_CHECK(std::same_as, double const&&>); constexpr Pair p(42, 3.14); STATIC_CHECK(alloy::get<0>(p) == 42); STATIC_CHECK(alloy::get<1>(p) == 3.14); } + { + using Pair = std::pair; + + STATIC_CHECK(alloy::TupleLike); + + STATIC_CHECK(alloy::tuple_size_v == 2); + + STATIC_CHECK(std::same_as, int&>); + STATIC_CHECK(std::same_as, int&>); + STATIC_CHECK(std::same_as, int&>); + STATIC_CHECK(std::same_as, int&>); + + STATIC_CHECK(std::same_as, double&>); + STATIC_CHECK(std::same_as, double&>); + STATIC_CHECK(std::same_as, double&>); + STATIC_CHECK(std::same_as, double&>); + + int i = -1; + double d = -2; + Pair p(i, d); + alloy::get<0>(p) = 42; + alloy::get<1>(p) = 3.14; + CHECK(alloy::get<0>(p) == 42); + CHECK(alloy::get<1>(p) == 3.14); + } } TEST_CASE("adapt_std_tuple") @@ -123,20 +150,20 @@ TEST_CASE("adapt_std_tuple") STATIC_CHECK(alloy::tuple_size_v == 3); - STATIC_CHECK(std::is_same_v, int&>); - STATIC_CHECK(std::is_same_v, int const&>); - STATIC_CHECK(std::is_same_v, int&&>); - STATIC_CHECK(std::is_same_v, int const&&>); + STATIC_CHECK(std::same_as, int&>); + STATIC_CHECK(std::same_as, int const&>); + STATIC_CHECK(std::same_as, int&&>); + STATIC_CHECK(std::same_as, int const&&>); - STATIC_CHECK(std::is_same_v, double&>); - STATIC_CHECK(std::is_same_v, double const&>); - STATIC_CHECK(std::is_same_v, double&&>); - STATIC_CHECK(std::is_same_v, double const&&>); + STATIC_CHECK(std::same_as, double&>); + STATIC_CHECK(std::same_as, double const&>); + STATIC_CHECK(std::same_as, double&&>); + STATIC_CHECK(std::same_as, double const&&>); - STATIC_CHECK(std::is_same_v, char&>); - STATIC_CHECK(std::is_same_v, char const&>); - STATIC_CHECK(std::is_same_v, char&&>); - STATIC_CHECK(std::is_same_v, char const&&>); + STATIC_CHECK(std::same_as, char&>); + STATIC_CHECK(std::same_as, char const&>); + STATIC_CHECK(std::same_as, char&&>); + STATIC_CHECK(std::same_as, char const&&>); constexpr Tuple p(42, 3.14, 'A'); @@ -157,20 +184,20 @@ TEST_CASE("tuple") STATIC_CHECK(alloy::tuple_size_v == 3); - STATIC_CHECK(std::is_same_v, int&>); - STATIC_CHECK(std::is_same_v, int const&>); - STATIC_CHECK(std::is_same_v, int&&>); - STATIC_CHECK(std::is_same_v, int const&&>); + STATIC_CHECK(std::same_as, int&>); + STATIC_CHECK(std::same_as, int const&>); + STATIC_CHECK(std::same_as, int&&>); + STATIC_CHECK(std::same_as, int const&&>); - STATIC_CHECK(std::is_same_v, double&>); - STATIC_CHECK(std::is_same_v, double const&>); - STATIC_CHECK(std::is_same_v, double&&>); - STATIC_CHECK(std::is_same_v, double const&&>); + STATIC_CHECK(std::same_as, double&>); + STATIC_CHECK(std::same_as, double const&>); + STATIC_CHECK(std::same_as, double&&>); + STATIC_CHECK(std::same_as, double const&&>); - STATIC_CHECK(std::is_same_v, char&>); - STATIC_CHECK(std::is_same_v, char const&>); - STATIC_CHECK(std::is_same_v, char&&>); - STATIC_CHECK(std::is_same_v, char const&&>); + STATIC_CHECK(std::same_as, char&>); + STATIC_CHECK(std::same_as, char const&>); + STATIC_CHECK(std::same_as, char&&>); + STATIC_CHECK(std::same_as, char const&&>); constexpr Tuple t(42, 3.14, 'A'); @@ -187,20 +214,20 @@ TEST_CASE("tuple") STATIC_CHECK(alloy::tuple_size_v == 3); - STATIC_CHECK(std::is_same_v, int&>); - STATIC_CHECK(std::is_same_v, int&>); - STATIC_CHECK(std::is_same_v, int&>); - STATIC_CHECK(std::is_same_v, int&>); + STATIC_CHECK(std::same_as, int&>); + STATIC_CHECK(std::same_as, int&>); + STATIC_CHECK(std::same_as, int&>); + STATIC_CHECK(std::same_as, int&>); - STATIC_CHECK(std::is_same_v, double&>); - STATIC_CHECK(std::is_same_v, double&>); - STATIC_CHECK(std::is_same_v, double&>); - STATIC_CHECK(std::is_same_v, double&>); + STATIC_CHECK(std::same_as, double&>); + STATIC_CHECK(std::same_as, double&>); + STATIC_CHECK(std::same_as, double&>); + STATIC_CHECK(std::same_as, double&>); - STATIC_CHECK(std::is_same_v, char&>); - STATIC_CHECK(std::is_same_v, char&>); - STATIC_CHECK(std::is_same_v, char&>); - STATIC_CHECK(std::is_same_v, char&>); + STATIC_CHECK(std::same_as, char&>); + STATIC_CHECK(std::same_as, char&>); + STATIC_CHECK(std::same_as, char&>); + STATIC_CHECK(std::same_as, char&>); int x = 42; double y = 3.14; @@ -220,20 +247,20 @@ TEST_CASE("tuple") STATIC_CHECK(alloy::tuple_size_v == 3); - STATIC_CHECK(std::is_same_v, int const&>); - STATIC_CHECK(std::is_same_v, int const&>); - STATIC_CHECK(std::is_same_v, int const&>); - STATIC_CHECK(std::is_same_v, int const&>); + STATIC_CHECK(std::same_as, int const&>); + STATIC_CHECK(std::same_as, int const&>); + STATIC_CHECK(std::same_as, int const&>); + STATIC_CHECK(std::same_as, int const&>); - STATIC_CHECK(std::is_same_v, double const&>); - STATIC_CHECK(std::is_same_v, double const&>); - STATIC_CHECK(std::is_same_v, double const&>); - STATIC_CHECK(std::is_same_v, double const&>); + STATIC_CHECK(std::same_as, double const&>); + STATIC_CHECK(std::same_as, double const&>); + STATIC_CHECK(std::same_as, double const&>); + STATIC_CHECK(std::same_as, double const&>); - STATIC_CHECK(std::is_same_v, char const&>); - STATIC_CHECK(std::is_same_v, char const&>); - STATIC_CHECK(std::is_same_v, char const&>); - STATIC_CHECK(std::is_same_v, char const&>); + STATIC_CHECK(std::same_as, char const&>); + STATIC_CHECK(std::same_as, char const&>); + STATIC_CHECK(std::same_as, char const&>); + STATIC_CHECK(std::same_as, char const&>); int const x = 42; double const y = 3.14; @@ -383,8 +410,8 @@ TEST_CASE("tuple") STATIC_CHECK(sizeof(b) == sizeof(OnlyChar)); } - STATIC_CHECK(std::is_same_v, alloy::tuple&>, alloy::tuple>); - STATIC_CHECK(std::is_same_v&>, alloy::tuple>); + STATIC_CHECK(std::same_as, alloy::tuple&>, alloy::tuple>); + STATIC_CHECK(std::same_as&>, alloy::tuple>); #if __cpp_lib_reference_from_temporary >= 202202L STATIC_CHECK(!std::is_constructible_v, double>); diff --git a/test/alloy_visit.cpp b/test/alloy_visit.cpp new file mode 100644 index 0000000..825e35c --- /dev/null +++ b/test/alloy_visit.cpp @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: MIT + +#include "iris_test.hpp" + +#include +#include + +#include +#include + +namespace alloy = iris::alloy; + +namespace detail { + +template +struct make_iota_tuple_impl; + +template +struct make_iota_tuple_impl> +{ + [[nodiscard]] static constexpr auto apply() noexcept + { + return std::tuple{Is...}; + } +}; + +} // detail + +template +[[nodiscard]] consteval auto make_iota_tuple() noexcept +{ + return detail::make_iota_tuple_impl>::apply(); +} + +TEST_CASE("alloy: visit") +{ + { + constexpr auto f = [](auto&& elem) noexcept { return elem; }; + + #define IRIS_TEST_VISIT(N) \ + STATIC_CHECK(alloy::visit_at(0, f, make_iota_tuple()) == 0); \ + STATIC_CHECK(noexcept(alloy::visit_at(0, f, make_iota_tuple()))); \ + STATIC_CHECK(alloy::visit_at(N - 1, f, make_iota_tuple()) == N - 1); \ + STATIC_CHECK(noexcept(alloy::visit_at(N - 1, f, make_iota_tuple()))) + + // Strategy = 0 + IRIS_TEST_VISIT(4); + + // Strategy = 1 + IRIS_TEST_VISIT(16); + + // Strategy = 2 + IRIS_TEST_VISIT(64); + + // Strategy = -1 (table-based dispatch) + IRIS_TEST_VISIT(65); + + #undef IRIS_TEST_VISIT + } + + // Check "all noexcept" is working + { + constexpr auto conditionally_throwing_f = [](std::integral_constant, auto&& elem) + noexcept(I == 1) // arbitrary + { + return elem; + }; + + #define IRIS_TEST_VISIT(N) \ + STATIC_CHECK(alloy::visit_at(0, conditionally_throwing_f, make_iota_tuple()) == 0); \ + STATIC_CHECK(!noexcept(alloy::visit_at(0, conditionally_throwing_f, make_iota_tuple()))); \ + STATIC_CHECK(alloy::visit_at(N - 1, conditionally_throwing_f, make_iota_tuple()) == N - 1); \ + STATIC_CHECK(!noexcept(alloy::visit_at(N - 1, conditionally_throwing_f, make_iota_tuple()))) + + // Strategy = 0 + IRIS_TEST_VISIT(4); + + // Strategy = 1 + IRIS_TEST_VISIT(16); + + // Strategy = 2 + IRIS_TEST_VISIT(64); + + // Strategy = -1 (table-based dispatch) + IRIS_TEST_VISIT(65); + + #undef IRIS_TEST_VISIT + } +}