Metaprogramming: detect structured types

This solution checks only the minimal precondition,
which is that a type supports `std::tuple_size<T>`.

A more complete implementation turns out to be surprisingly complex,
since a direct check likely requires compile-time reflection capabilities
at the level of at least C++23
 - `std::tuple_element<i,T>` typically implements limits checks,
   which interfere with the detection of empty structured types
 - the situation regarding `std::get<i>()` is even more complicated,
   since we might have to probe for ADL-based solutions, or member templates

The check for minimal necessary precondition however allows us to
single out std::tuple, std::array and our own structured types in
compilation branching, which suffices to fulfils actual needs.
This commit is contained in:
Fischlurch 2024-12-17 16:56:21 +01:00
parent 03b17c78da
commit 23b4a54e79
4 changed files with 131 additions and 18 deletions

View file

@ -189,6 +189,34 @@ namespace meta {
namespace {
/**
* check for the necessary precondition, not sufficient.
* @remark Detecting the possibility of structured binding reliably will be possible with C++23.
* Even a partial implementation, covering only `std::tuple_element` is surprisingly
* complicated, due to the built-in limit checks. What do do with a `tuple<>`?
*/
template<class TUP, size_t siz =std::tuple_size<TUP>::value>
struct _Probe_TupleProtocol
{ };
}
template<class TUP>
using enable_if_TupleProtocol = std::void_t<_Probe_TupleProtocol<std::decay_t<TUP>>>;
/** Trait template to detect a »tuple-like« type,
* which can be used in structured bindings.
* @note we check only one precondition: the support for `std::tuple_size`
*/
template<class X, typename =void>
struct is_Structured
: std::false_type
{ };
template<class TUP>
struct is_Structured<TUP, enable_if_TupleProtocol<TUP>>
: std::true_type
{ };
/** Trait template for detecting a typelist type.
* For example, this allows to write specialisations

View file

@ -107,23 +107,19 @@ namespace engine {
using lib::meta::is_TernaryFun;
using std::remove_reference_t;
using lib::meta::enable_if;
using lib::meta::is_Structured;
using std::is_pointer;
using std::is_reference;
using std::tuple_size_v;
using std::void_t;
using std::__and_;
using std::__not_;
template<class X, typename SEL=void>
struct is_Structured
: std::false_type
{ };
template<class TUP>
struct is_Structured<TUP, void_t<std::tuple_size<TUP>>>
: std::true_type
{ };
template<typename V>
struct is_Value
: __and_<__not_<std::is_pointer<V>>
,__not_<std::is_reference<V>>
: __and_<__not_<is_pointer<V>>
,__not_<is_reference<V>>
,__not_<is_Structured<V>>
,std::is_default_constructible<V>
,std::is_copy_assignable<V>
@ -132,13 +128,17 @@ namespace engine {
template<typename B>
struct is_Buffer
: __and_<__not_<std::is_pointer<B>>
,__not_<std::is_reference<B>>
: __and_<__not_<is_pointer<B>>
,__not_<is_reference<B>>
,std::is_default_constructible<B>
,__not_<_Fun<B>>
>
{ };
/** Helper to pick up the parameter dimensions from the processing function
* @remark this is the rather simple yet common case that media processing
* is done by a function, which takes an array of input and output

View file

@ -20,20 +20,21 @@
#include "lib/test/run.hpp"
#include "lib/meta/util.hpp"
#include "lib/meta/typelist.hpp"
#include "lib/hetero-data.hpp"
#include "lib/test/diagnostic-output.hpp"
#include <string>
#include <iostream>
using std::cout;
using std::endl;/////////////////////////TODO
#include <array>
#include <tuple>
namespace lib {
namespace meta {
namespace test {
using std::string;
using std::array;
using std::tuple;
using std::pair;
@ -42,6 +43,7 @@ namespace test {
* @test verify basic type trait and metaprogramming helpers.
* - marker types to tell which overload the compiler picks
* - simple trait to detect the possibility of a string conversion
* - trait to detect (possibly) structured types (»tuple-like«)
* - trait to detect a typelist type
*/
class MetaUtils_test : public Test
@ -53,6 +55,7 @@ namespace test {
verify_genericTypeDisplay();
detect_stringConversion();
detect_tupleProtocol();
detect_typeList();
}
@ -162,6 +165,42 @@ namespace test {
}
void
detect_tupleProtocol()
{
// verify arbitrary non-structured types
CHECK ((not is_Structured<void >()));
CHECK ((not is_Structured<void* >()));
CHECK ((not is_Structured<const void* >()));
CHECK ((not is_Structured<const int >()));
CHECK ((not is_Structured<int >()));
CHECK ((not is_Structured<int & >()));
CHECK ((not is_Structured<int const & >()));
CHECK ((not is_Structured<int const * >()));
CHECK ((not is_Structured<int * >()));
CHECK ((not is_Structured<int * const >()));
CHECK ((not is_Structured<int * const & >()));
CHECK ((not is_Structured<int * & >()));
CHECK ((not is_Structured<int * && >()));
CHECK ((not is_Structured<int && >()));
CHECK ((not is_Structured<int const && >()));
CHECK ((not is_Structured<double >()));
CHECK ((not is_Structured<string >()));
CHECK ((not is_Structured<Node<short,NullType> >()));
// the following indeed support C++ tuple protocol
CHECK (( is_Structured<tuple<int> >()));
CHECK (( is_Structured<tuple<int,char,long> >()));
CHECK (( is_Structured<tuple<> >()));
CHECK (( is_Structured<pair<short,long> >()));
CHECK (( is_Structured<array<short,5> >()));
CHECK (( is_Structured<array<long,0> >()));
CHECK (( is_Structured<HeteroData<size_t> >()));
CHECK (( is_Structured<HeteroData<int,char> >()));
CHECK (( is_Structured<HeteroData<> >()));
}
//-------------------------------------------------TEST-types--
typedef Types< int

View file

@ -92049,6 +92049,52 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1734300380799" ID="ID_782708625" MODIFIED="1734300402299" TEXT="dann die Erweiterung auf strukturierte Typen dazunehmen">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1734397892641" ID="ID_23248062" MODIFIED="1734397907072" TEXT="praktisch-technisch m&#xfc;hsam...">
<icon BUILTIN="flag-yellow"/>
<node CREATED="1734397915749" ID="ID_1226245371" MODIFIED="1734397929207" TEXT="mu&#xdf; Pr&#xe4;dikate auf alle Typen anwenden"/>
<node CREATED="1734397930454" ID="ID_1067248557" MODIFIED="1734397961120" TEXT="brauche Operation &#xfc;ber Elemente eines strukturierten Typs"/>
<node CREATED="1734403556414" ID="ID_971023481" MODIFIED="1734403580870" TEXT="mu&#xdf; dazu den Einzelfall-Typ auf einen structured-Type &#xbb;heben&#xab;">
<node CREATED="1734403583091" ID="ID_1710377001" MODIFIED="1734403593253" TEXT="StructType&lt;TY&gt;">
<node COLOR="#435e98" CREATED="1734403595048" ID="ID_496079267" MODIFIED="1734450365546" TEXT="spricht falsch an im Compile">
<icon BUILTIN="broken-line"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#a6086b" CREATED="1734403634332" ID="ID_817342705" MODIFIED="1734450390281" TEXT="funktioniert enable_if&lt;is_Structured&lt;long*&gt; &gt; korrekt?"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1734450087903" ID="ID_1588306692" MODIFIED="1734450379412" TEXT="is_Structured war naiv implementiert">
<icon BUILTIN="broken-line"/>
</node>
<node COLOR="#338800" CREATED="1734450102857" ID="ID_1962318483" MODIFIED="1734450109137" TEXT="versuche es besser...">
<icon BUILTIN="button_ok"/>
<node COLOR="#5b280f" CREATED="1734450111282" ID="ID_1778075632" MODIFIED="1734450135378" TEXT="das ganze &#xbb;tuple-protocol&#xab; abpr&#xfc;fen">
<icon BUILTIN="button_cancel"/>
<node CREATED="1734450137253" ID="ID_1390904069" MODIFIED="1734450144643" TEXT="unverh&#xe4;ltnism&#xe4;&#xdf;ig aufwendig"/>
<node CREATED="1734450146232" ID="ID_1193364290" MODIFIED="1734450167735" TEXT="ein Problem sind die size-Limits, die bei leeren Tuples ansprechen"/>
<node CREATED="1734450169097" ID="ID_1451956704" MODIFIED="1734450194401" TEXT="und f&#xfc;r std::get g&#xe4;be es diverse Varianten (siehe die Probleme mit lib::HeteroData)"/>
</node>
<node COLOR="#435e98" CREATED="1734450197246" ID="ID_1518898869" MODIFIED="1734450228376" TEXT="die notwendige Bedingung: mu&#xdf; std::tuple_size&lt;T&gt; unterst&#xfc;tzen">
<icon BUILTIN="yes"/>
</node>
<node COLOR="#338800" CREATED="1734450249909" ID="ID_1798931733" MODIFIED="1734450323184" TEXT="extrahiert nach lib/meta/util.hpp">
<icon BUILTIN="button_ok"/>
<node COLOR="#435e98" CREATED="1734450269831" ID="ID_1495231260" MODIFIED="1734450319900" TEXT="sauber formuliert mit separatem Probe-Template"/>
<node COLOR="#435e98" CREATED="1734450281377" ID="ID_258396157" MODIFIED="1734450319901" TEXT="ein enable_if-Variante dazu"/>
<node COLOR="#435e98" CREATED="1734450289604" ID="ID_1035452314" MODIFIED="1734450357273" TEXT="getestet mit verschiedenen Typen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
MetaUtils_test::detect_tupleProtocol()
</p>
</body>
</html>
</richcontent>
</node>
</node>
</node>
</node>
</node>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1734133400400" ID="ID_1364724277" MODIFIED="1734141875620" TEXT="zus&#xe4;tzlichen Funktor f&#xfc;r die Parameter akzeptieren">