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''
This commit is contained in:
parent
3a1f64ec41
commit
3a5bbd8fb4
5 changed files with 181 additions and 82 deletions
|
|
@ -29,76 +29,6 @@
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
|
|
||||||
namespace lib {
|
|
||||||
namespace meta {
|
|
||||||
|
|
||||||
template<class TUP>
|
|
||||||
concept tuple_sized = requires
|
|
||||||
{
|
|
||||||
{ std::tuple_size<TUP>::value } -> std::convertible_to<size_t>;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template<class TUP, std::size_t idx>
|
|
||||||
concept tuple_adl_accessible = requires(TUP tup)
|
|
||||||
{
|
|
||||||
typename std::tuple_element_t<idx, TUP>;
|
|
||||||
{ get<idx>(tup) } -> std::convertible_to<std::tuple_element_t<idx, TUP>&>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class TUP, std::size_t idx>
|
|
||||||
concept tuple_mem_accessible = requires(TUP tup)
|
|
||||||
{
|
|
||||||
typename std::tuple_element_t<idx, TUP>;
|
|
||||||
{ tup.template get<idx>() } -> std::convertible_to<std::tuple_element_t<idx, TUP>&>;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class TUP, std::size_t idx>
|
|
||||||
concept tuple_element_accessible = tuple_mem_accessible<TUP,idx> or tuple_adl_accessible<TUP,idx>;
|
|
||||||
|
|
||||||
template<class TUP>
|
|
||||||
concept tuple_accessible =
|
|
||||||
tuple_sized<TUP> and
|
|
||||||
WithIdxSeq<std::tuple_size_v<TUP>>::andAll([](auto idx)
|
|
||||||
{
|
|
||||||
return tuple_element_accessible<TUP,idx>;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
template<class TUP>
|
|
||||||
concept tuple_like = not is_reference_v<TUP>
|
|
||||||
and tuple_sized<remove_cv_t<TUP>>
|
|
||||||
and tuple_accessible<remove_cv_t<TUP>>;
|
|
||||||
|
|
||||||
|
|
||||||
template<std::size_t idx, class TUP>
|
|
||||||
requires(tuple_like<std::remove_reference_t<TUP>>)
|
|
||||||
decltype(auto)
|
|
||||||
get (TUP&& tup)
|
|
||||||
{
|
|
||||||
using Tup = std::remove_reference_t<TUP>;
|
|
||||||
static_assert (0 < std::tuple_size_v<Tup>);
|
|
||||||
if constexpr (tuple_mem_accessible<Tup,0>)
|
|
||||||
{
|
|
||||||
if constexpr (std::is_reference_v<TUP>)
|
|
||||||
return tup.template get<idx>();
|
|
||||||
else
|
|
||||||
{ // return value copy when tuple given as RValue
|
|
||||||
using Elm = std::tuple_element_t<idx, TUP>;
|
|
||||||
Elm elm(tup.template get<idx>());
|
|
||||||
return elm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
using std::get;
|
|
||||||
return get<idx> (std::forward<TUP> (tup));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}}//namespace lib::meta
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template<typename X>
|
template<typename X>
|
||||||
void
|
void
|
||||||
|
|
@ -154,18 +84,18 @@ main (int, char**)
|
||||||
using TupConstSeq = lib::meta::ElmTypes<const Tup>::Seq;
|
using TupConstSeq = lib::meta::ElmTypes<const Tup>::Seq;
|
||||||
SHOW_TYPE(TupConstSeq)
|
SHOW_TYPE(TupConstSeq)
|
||||||
|
|
||||||
using T1 = decltype(lib::meta::get<0> (std::declval<Tup>()));
|
using T1 = decltype(lib::meta::getElm<0> (std::declval<Tup>()));
|
||||||
SHOW_TYPE(T1)
|
SHOW_TYPE(T1)
|
||||||
using T2 = decltype(lib::meta::get<0> (std::declval<Tup&>()));
|
using T2 = decltype(lib::meta::getElm<0> (std::declval<Tup&>()));
|
||||||
SHOW_TYPE(T2)
|
SHOW_TYPE(T2)
|
||||||
using T3 = decltype(lib::meta::get<0> (std::declval<Tup const&>()));
|
using T3 = decltype(lib::meta::getElm<0> (std::declval<Tup const&>()));
|
||||||
SHOW_TYPE(T3)
|
SHOW_TYPE(T3)
|
||||||
|
|
||||||
using H1 = decltype(lib::meta::get<4> (std::declval<Hetero>()));
|
using H1 = decltype(lib::meta::getElm<4> (std::declval<Hetero>()));
|
||||||
SHOW_TYPE(H1)
|
SHOW_TYPE(H1)
|
||||||
using H2 = decltype(lib::meta::get<4> (std::declval<Hetero&>()));
|
using H2 = decltype(lib::meta::getElm<4> (std::declval<Hetero&>()));
|
||||||
SHOW_TYPE(H2)
|
SHOW_TYPE(H2)
|
||||||
using H3 = decltype(lib::meta::get<4> (std::declval<Hetero const&>()));
|
using H3 = decltype(lib::meta::getElm<4> (std::declval<Hetero const&>()));
|
||||||
SHOW_TYPE(H3)
|
SHOW_TYPE(H3)
|
||||||
|
|
||||||
cout << "\n.gulp." <<endl;
|
cout << "\n.gulp." <<endl;
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,75 @@ namespace meta {
|
||||||
template<class TUP>
|
template<class TUP>
|
||||||
using disable_if_Tuple = lib::meta::disable_if<lib::meta::is_Tuple<std::remove_reference_t<TUP>>>;
|
using disable_if_Tuple = lib::meta::disable_if<lib::meta::is_Tuple<std::remove_reference_t<TUP>>>;
|
||||||
|
|
||||||
|
using std::remove_cv_t;
|
||||||
|
using std::is_reference_v;
|
||||||
|
|
||||||
|
|
||||||
|
template<class TUP>
|
||||||
|
concept tuple_sized = requires
|
||||||
|
{
|
||||||
|
{ std::tuple_size<TUP>::value } -> std::convertible_to<size_t>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<class TUP, std::size_t idx>
|
||||||
|
concept tuple_adl_accessible = requires(TUP tup)
|
||||||
|
{
|
||||||
|
typename std::tuple_element_t<idx, TUP>;
|
||||||
|
{ get<idx>(tup) } -> std::convertible_to<std::tuple_element_t<idx, TUP>&>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class TUP, std::size_t idx>
|
||||||
|
concept tuple_mem_accessible = requires(TUP tup)
|
||||||
|
{
|
||||||
|
typename std::tuple_element_t<idx, TUP>;
|
||||||
|
{ tup.template get<idx>() } -> std::convertible_to<std::tuple_element_t<idx, TUP>&>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class TUP, std::size_t idx>
|
||||||
|
concept tuple_element_accessible = tuple_mem_accessible<TUP,idx> or tuple_adl_accessible<TUP,idx>;
|
||||||
|
|
||||||
|
template<class TUP>
|
||||||
|
concept tuple_accessible =
|
||||||
|
tuple_sized<TUP> and
|
||||||
|
WithIdxSeq<std::tuple_size_v<TUP>>::andAll([](auto idx)
|
||||||
|
{
|
||||||
|
return tuple_element_accessible<TUP,idx>;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
template<class TUP>
|
||||||
|
concept tuple_like = not is_reference_v<TUP>
|
||||||
|
and tuple_sized<remove_cv_t<TUP>>
|
||||||
|
and tuple_accessible<remove_cv_t<TUP>>;
|
||||||
|
|
||||||
|
|
||||||
|
template<std::size_t idx, class TUP>
|
||||||
|
requires(tuple_like<std::remove_reference_t<TUP>>)
|
||||||
|
decltype(auto)
|
||||||
|
getElm (TUP&& tup)
|
||||||
|
{
|
||||||
|
using Tup = std::remove_reference_t<TUP>;
|
||||||
|
static_assert (0 < std::tuple_size_v<Tup>);
|
||||||
|
if constexpr (tuple_mem_accessible<Tup,0>)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_reference_v<TUP>)
|
||||||
|
return tup.template get<idx>();
|
||||||
|
else
|
||||||
|
{ // return value copy when tuple given as RValue
|
||||||
|
using Elm = std::tuple_element_t<idx, TUP>;
|
||||||
|
Elm elm(tup.template get<idx>());
|
||||||
|
return elm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // ▽▽▽ ADL
|
||||||
|
using std::get;
|
||||||
|
return get<idx> (std::forward<TUP> (tup));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace { // apply to tuple-like : helpers...
|
namespace { // apply to tuple-like : helpers...
|
||||||
|
|
@ -86,8 +155,7 @@ namespace meta {
|
||||||
template<typename FUN, typename TUP, size_t...Idx>
|
template<typename FUN, typename TUP, size_t...Idx>
|
||||||
constexpr decltype(auto)
|
constexpr decltype(auto)
|
||||||
__unpack_and_apply (FUN&& f, TUP&& tup, std::index_sequence<Idx...>)
|
__unpack_and_apply (FUN&& f, TUP&& tup, std::index_sequence<Idx...>)
|
||||||
{ // ▽▽▽ ADL
|
{
|
||||||
using std::get;
|
|
||||||
return std::invoke (std::forward<FUN> (f)
|
return std::invoke (std::forward<FUN> (f)
|
||||||
,get<Idx> (std::forward<TUP>(tup))...
|
,get<Idx> (std::forward<TUP>(tup))...
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -66,9 +66,8 @@ namespace test {
|
||||||
/* ===== printing Tuple types and contents ===== */
|
/* ===== printing Tuple types and contents ===== */
|
||||||
|
|
||||||
|
|
||||||
template<typename TUP>
|
template<tuple_like TUP>
|
||||||
inline enable_if<is_Tuple<TUP>,
|
inline string
|
||||||
string >
|
|
||||||
showType ()
|
showType ()
|
||||||
{
|
{
|
||||||
using TypeList = typename RebindTupleTypes<TUP>::List;
|
using TypeList = typename RebindTupleTypes<TUP>::List;
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@
|
||||||
#include "meta/tuple-diagnostics.hpp"
|
#include "meta/tuple-diagnostics.hpp"
|
||||||
#include "lib/format-string.hpp"
|
#include "lib/format-string.hpp"
|
||||||
#include "lib/format-cout.hpp"
|
#include "lib/format-cout.hpp"
|
||||||
|
#include "lib/hetero-data.hpp"
|
||||||
#include "lib/test/diagnostic-output.hpp"////////////////TODO
|
#include "lib/test/diagnostic-output.hpp"////////////////TODO
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -79,6 +80,7 @@ namespace test {
|
||||||
check_diagnostics();
|
check_diagnostics();
|
||||||
check_tuple_from_Typelist();
|
check_tuple_from_Typelist();
|
||||||
demonstrate_generic_iteration();
|
demonstrate_generic_iteration();
|
||||||
|
verify_tuple_like_concept();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -227,6 +229,106 @@ namespace test {
|
||||||
CHECK (not ElmTypes<Tup>::AndAll<std::is_integral>()); // ... __and_<is_integral<int>,is_integral<double>,is_integral<char>>
|
CHECK (not ElmTypes<Tup>::AndAll<std::is_integral>()); // ... __and_<is_integral<int>,is_integral<double>,is_integral<char>>
|
||||||
CHECK ( ElmTypes<Tup>::OrAll<std::is_integral>());
|
CHECK ( ElmTypes<Tup>::OrAll<std::is_integral>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template<typename X>
|
||||||
|
string
|
||||||
|
render()
|
||||||
|
{
|
||||||
|
return lib::test::showType<X>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<tuple_like X>
|
||||||
|
string
|
||||||
|
render()
|
||||||
|
{
|
||||||
|
string res{"Tup"};
|
||||||
|
res +="("+toString(std::tuple_size_v<X>)+") : "+ lib::test::showType<X>();
|
||||||
|
lib::meta::forEachIDX<X> ([&](auto i)
|
||||||
|
{
|
||||||
|
using Elm = std::tuple_element_t<i, X>;
|
||||||
|
res += " ▷"+ toString(uint(i)) + ": " + lib::test::showType<Elm>();
|
||||||
|
});
|
||||||
|
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<long,short>;
|
||||||
|
using Arr = std::array<int,3>;
|
||||||
|
using Het = lib::HeteroData<int,string>::Chain<short>::ChainExtent<bool,lib::meta::Nil>::ChainType;
|
||||||
|
|
||||||
|
CHECK ( tuple_sized<Tup> );
|
||||||
|
CHECK ( tuple_sized<Arr> );
|
||||||
|
CHECK ( tuple_sized<Het> );
|
||||||
|
CHECK (not tuple_sized<int> );
|
||||||
|
|
||||||
|
CHECK ( (tuple_element_accessible<Tup,0>));
|
||||||
|
// CHECK ( (tuple_element_accessible<Tup,2>));
|
||||||
|
CHECK ( (tuple_element_accessible<Het,0>));
|
||||||
|
CHECK ( tuple_accessible<Tup> );
|
||||||
|
CHECK ( tuple_accessible<Arr> );
|
||||||
|
CHECK ( tuple_accessible<Het> );
|
||||||
|
CHECK (not tuple_accessible<int> );
|
||||||
|
|
||||||
|
CHECK ( tuple_like<Tup> );
|
||||||
|
CHECK ( tuple_like<Arr> );
|
||||||
|
CHECK ( tuple_like<Het> );
|
||||||
|
CHECK (not tuple_like<int> );
|
||||||
|
|
||||||
|
// 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>() == "Tup(2) : tuple<long, short> ▷0: long ▷1: short"_expect);
|
||||||
|
CHECK (render<Arr>() == "Tup(3) : array<int, 3ul> ▷0: int ▷1: int ▷2: int"_expect);
|
||||||
|
CHECK (render<Het>() == "Tup(5) : HeteroData<Node<StorageFrame<0ul, int, string>, "
|
||||||
|
"Node<StorageFrame<1ul, short>, "
|
||||||
|
"Node<StorageFrame<2ul, bool, Nil>, 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>() == "int"_expect);
|
||||||
|
|
||||||
|
CHECK (std::tuple_size_v<const Tup> == 2 );
|
||||||
|
|
||||||
|
using Elm1 = std::tuple_element_t<1, const Tup>;
|
||||||
|
CHECK (lib::test::showType<Elm1>() == "const short"_expect);
|
||||||
|
|
||||||
|
// note: a const tuple will add const qualification to each element type
|
||||||
|
using TupConstSeq = lib::meta::ElmTypes<const Tup>::Seq;
|
||||||
|
CHECK (lib::test::showType<TupConstSeq>() == "Types<long const, short const>"_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<Tup>()));
|
||||||
|
CHECK (lib::test::showType<T1>() == "long &&"_expect);
|
||||||
|
|
||||||
|
using T2 = decltype(lib::meta::getElm<0> (std::declval<Tup&>()));
|
||||||
|
CHECK (lib::test::showType<T2>() == "long&"_expect);
|
||||||
|
|
||||||
|
using T3 = decltype(lib::meta::getElm<0> (std::declval<Tup const&>()));
|
||||||
|
CHECK (lib::test::showType<T3>() == "long const&"_expect);
|
||||||
|
|
||||||
|
using H1 = decltype(lib::meta::getElm<4> (std::declval<Het>()));
|
||||||
|
CHECK (lib::test::showType<H1>() == "Nil"_expect);
|
||||||
|
|
||||||
|
using H2 = decltype(lib::meta::getElm<4> (std::declval<Het&>()));
|
||||||
|
CHECK (lib::test::showType<H2>() == "Nil&"_expect);
|
||||||
|
|
||||||
|
using H3 = decltype(lib::meta::getElm<4> (std::declval<Het const&>()));
|
||||||
|
CHECK (lib::test::showType<H3>() == "Nil const&"_expect);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,7 @@ namespace meta {
|
||||||
return printSublist<TypeList>();
|
return printSublist<TypeList>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue