From 3a5bbd8fb4c6e3bd27985954629526d533fedb07 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 28 Jun 2025 00:42:23 +0200 Subject: [PATCH] Upgrade: put the new `tuple_like` concept into use - integrate the concept definition into tuple-helper.hpp - use it to replace the `is_Structured` traits check - do not need `enable_if_TupleProtocol` any more Integrate test coverage of the concept metafunctions and the generalised get accessor ''This changeset was made at LAC 2025 in Lyon, France'' --- research/try.cpp | 82 ++-------------- src/lib/meta/tuple-helper.hpp | 72 +++++++++++++- tests/library/meta/tuple-diagnostics.hpp | 5 +- tests/library/meta/tuple-helper-test.cpp | 102 ++++++++++++++++++++ tests/library/meta/typelist-diagnostics.hpp | 2 +- 5 files changed, 181 insertions(+), 82 deletions(-) diff --git a/research/try.cpp b/research/try.cpp index 7d435bdcf..b6e9d0a82 100644 --- a/research/try.cpp +++ b/research/try.cpp @@ -29,76 +29,6 @@ using std::string; -namespace lib { -namespace meta { - - template - concept tuple_sized = requires - { - { std::tuple_size::value } -> std::convertible_to; - }; - - - template - concept tuple_adl_accessible = requires(TUP tup) - { - typename std::tuple_element_t; - { get(tup) } -> std::convertible_to&>; - }; - - template - concept tuple_mem_accessible = requires(TUP tup) - { - typename std::tuple_element_t; - { tup.template get() } -> std::convertible_to&>; - }; - - template - concept tuple_element_accessible = tuple_mem_accessible or tuple_adl_accessible; - - template - concept tuple_accessible = - tuple_sized and - WithIdxSeq>::andAll([](auto idx) - { - return tuple_element_accessible; - }); - - - template - concept tuple_like = not is_reference_v - and tuple_sized> - and tuple_accessible>; - - - template - requires(tuple_like>) - decltype(auto) - get (TUP&& tup) - { - using Tup = std::remove_reference_t; - static_assert (0 < std::tuple_size_v); - if constexpr (tuple_mem_accessible) - { - if constexpr (std::is_reference_v) - return tup.template get(); - else - { // return value copy when tuple given as RValue - using Elm = std::tuple_element_t; - Elm elm(tup.template get()); - return elm; - } - } - else - { - using std::get; - return get (std::forward (tup)); - } - } - -}}//namespace lib::meta - - template void @@ -154,18 +84,18 @@ main (int, char**) using TupConstSeq = lib::meta::ElmTypes::Seq; SHOW_TYPE(TupConstSeq) - using T1 = decltype(lib::meta::get<0> (std::declval())); + using T1 = decltype(lib::meta::getElm<0> (std::declval())); SHOW_TYPE(T1) - using T2 = decltype(lib::meta::get<0> (std::declval())); + using T2 = decltype(lib::meta::getElm<0> (std::declval())); SHOW_TYPE(T2) - using T3 = decltype(lib::meta::get<0> (std::declval())); + using T3 = decltype(lib::meta::getElm<0> (std::declval())); SHOW_TYPE(T3) - using H1 = decltype(lib::meta::get<4> (std::declval())); + using H1 = decltype(lib::meta::getElm<4> (std::declval())); SHOW_TYPE(H1) - using H2 = decltype(lib::meta::get<4> (std::declval())); + using H2 = decltype(lib::meta::getElm<4> (std::declval())); SHOW_TYPE(H2) - using H3 = decltype(lib::meta::get<4> (std::declval())); + using H3 = decltype(lib::meta::getElm<4> (std::declval())); SHOW_TYPE(H3) cout << "\n.gulp." < using disable_if_Tuple = lib::meta::disable_if>>; + using std::remove_cv_t; + using std::is_reference_v; + + + template + concept tuple_sized = requires + { + { std::tuple_size::value } -> std::convertible_to; + }; + + + template + concept tuple_adl_accessible = requires(TUP tup) + { + typename std::tuple_element_t; + { get(tup) } -> std::convertible_to&>; + }; + + template + concept tuple_mem_accessible = requires(TUP tup) + { + typename std::tuple_element_t; + { tup.template get() } -> std::convertible_to&>; + }; + + template + concept tuple_element_accessible = tuple_mem_accessible or tuple_adl_accessible; + + template + concept tuple_accessible = + tuple_sized and + WithIdxSeq>::andAll([](auto idx) + { + return tuple_element_accessible; + }); + + + template + concept tuple_like = not is_reference_v + and tuple_sized> + and tuple_accessible>; + + + template + requires(tuple_like>) + decltype(auto) + getElm (TUP&& tup) + { + using Tup = std::remove_reference_t; + static_assert (0 < std::tuple_size_v); + if constexpr (tuple_mem_accessible) + { + if constexpr (std::is_reference_v) + return tup.template get(); + else + { // return value copy when tuple given as RValue + using Elm = std::tuple_element_t; + Elm elm(tup.template get()); + return elm; + } + } + else + { // ▽▽▽ ADL + using std::get; + return get (std::forward (tup)); + } + } + + namespace { // apply to tuple-like : helpers... @@ -86,8 +155,7 @@ namespace meta { template constexpr decltype(auto) __unpack_and_apply (FUN&& f, TUP&& tup, std::index_sequence) - { // ▽▽▽ ADL - using std::get; + { return std::invoke (std::forward (f) ,get (std::forward(tup))... ); diff --git a/tests/library/meta/tuple-diagnostics.hpp b/tests/library/meta/tuple-diagnostics.hpp index 00811d666..4f7ab8b00 100644 --- a/tests/library/meta/tuple-diagnostics.hpp +++ b/tests/library/meta/tuple-diagnostics.hpp @@ -66,9 +66,8 @@ namespace test { /* ===== printing Tuple types and contents ===== */ - template - inline enable_if, - string > + template + inline string showType () { using TypeList = typename RebindTupleTypes::List; diff --git a/tests/library/meta/tuple-helper-test.cpp b/tests/library/meta/tuple-helper-test.cpp index e77fb3a54..c27af4609 100644 --- a/tests/library/meta/tuple-helper-test.cpp +++ b/tests/library/meta/tuple-helper-test.cpp @@ -29,6 +29,7 @@ #include "meta/tuple-diagnostics.hpp" #include "lib/format-string.hpp" #include "lib/format-cout.hpp" +#include "lib/hetero-data.hpp" #include "lib/test/diagnostic-output.hpp"////////////////TODO #include @@ -79,6 +80,7 @@ namespace test { check_diagnostics(); check_tuple_from_Typelist(); demonstrate_generic_iteration(); + verify_tuple_like_concept(); } @@ -227,6 +229,106 @@ namespace test { CHECK (not ElmTypes::AndAll()); // ... __and_,is_integral,is_integral> CHECK ( ElmTypes::OrAll()); } + + + + template + string + render() + { + return lib::test::showType(); + } + + template + string + render() + { + string res{"Tup"}; + res +="("+toString(std::tuple_size_v)+") : "+ lib::test::showType(); + lib::meta::forEachIDX ([&](auto i) + { + using Elm = std::tuple_element_t; + res += " ▷"+ toString(uint(i)) + ": " + lib::test::showType(); + }); + return res; + } + + /** @test verify construction of a concept to detect _tuple-like_ classes, + * which conform to the »tuple protocol« and can thus be used + * in structural bindings + * - the size of such an entity can be detected at compile time + * - essentially, these can be considered as _product types_ — which implies + * that there are N element types + * - access to the corresponding member data is possible either through a + * `get` member function or free function detected by ADL + */ + void + verify_tuple_like_concept() + { + using Tup = std::tuple; + using Arr = std::array; + using Het = lib::HeteroData::Chain::ChainExtent::ChainType; + + CHECK ( tuple_sized ); + CHECK ( tuple_sized ); + CHECK ( tuple_sized ); + CHECK (not tuple_sized ); + + CHECK ( (tuple_element_accessible)); + // CHECK ( (tuple_element_accessible)); + CHECK ( (tuple_element_accessible)); + CHECK ( tuple_accessible ); + CHECK ( tuple_accessible ); + CHECK ( tuple_accessible ); + CHECK (not tuple_accessible ); + + CHECK ( tuple_like ); + CHECK ( tuple_like ); + CHECK ( tuple_like ); + CHECK (not tuple_like ); + + // the tuple, the array and the HeteroData are tuple-like, + // and will be handled by a special overload, exploiting the additional features + CHECK (render() == "Tup(2) : tuple ▷0: long ▷1: short"_expect); + CHECK (render() == "Tup(3) : array ▷0: int ▷1: int ▷2: int"_expect); + CHECK (render() == "Tup(5) : HeteroData, " + "Node, " + "Node, Nil> > > > " + "▷0: int ▷1: string ▷2: short ▷3: bool ▷4: Nil"_expect); + // the plain type `int` is not tuple-like and thus the fallback case is picked + CHECK (render() == "int"_expect); + + CHECK (std::tuple_size_v == 2 ); + + using Elm1 = std::tuple_element_t<1, const Tup>; + CHECK (lib::test::showType() == "const short"_expect); + + // note: a const tuple will add const qualification to each element type + using TupConstSeq = lib::meta::ElmTypes::Seq; + CHECK (lib::test::showType() == "Types"_expect); + + + // a unified access function `getElm` + // which works both with access by member or free function + + using T1 = decltype(lib::meta::getElm<0> (std::declval())); + CHECK (lib::test::showType() == "long &&"_expect); + + using T2 = decltype(lib::meta::getElm<0> (std::declval())); + CHECK (lib::test::showType() == "long&"_expect); + + using T3 = decltype(lib::meta::getElm<0> (std::declval())); + CHECK (lib::test::showType() == "long const&"_expect); + + using H1 = decltype(lib::meta::getElm<4> (std::declval())); + CHECK (lib::test::showType

() == "Nil"_expect); + + using H2 = decltype(lib::meta::getElm<4> (std::declval())); + CHECK (lib::test::showType

() == "Nil&"_expect); + + using H3 = decltype(lib::meta::getElm<4> (std::declval())); + CHECK (lib::test::showType

() == "Nil const&"_expect); + } }; diff --git a/tests/library/meta/typelist-diagnostics.hpp b/tests/library/meta/typelist-diagnostics.hpp index 60e43babc..7873824fd 100644 --- a/tests/library/meta/typelist-diagnostics.hpp +++ b/tests/library/meta/typelist-diagnostics.hpp @@ -188,7 +188,7 @@ namespace meta { return printSublist(); } - // Note: we define overloads of this function for other types, especially Tuples + // Note: we define further overloads of this function for other types, especially Tuples