Library: a step towards variadic type sequences (see #987)

Likely the most widely used facility, which enters into meta-programming
with type sequences, is our function-signature-detector `_Fun<X>`,
which returns a argument type-sequence.

After adding some bridges for cross-compatibility,
function-arguments are now extracted as a new-style,
''variadic sequence'' without trailing `NullType`.

Doing so required to augment some of the most widely used
sequence-processing helpers to work seamlessly also with the
new-style variadic sequences with a definition variant based
on variadics, which typically will later-on obsolete the original
solution, which at that time needed to be tediously coded as a
series of explicit specialisations for N arguments.
This commit is contained in:
Fischlurch 2025-02-16 21:10:06 +01:00
parent 3611bc94ee
commit fbf7a792a8
7 changed files with 138 additions and 19 deletions

View file

@ -116,7 +116,7 @@ namespace meta{
: std::true_type
{
using Ret = RET;
using Args = Types<ARGS...>;
using Args = TySeq<ARGS...>;
using Sig = RET(ARGS...);
using Functor = std::function<Sig>;
enum { ARITY = sizeof...(ARGS) };
@ -343,7 +343,17 @@ namespace meta{
template<typename RET, typename ARGS>
struct BuildFunType;
///////////////////////////////////////////////////////////////////////////////////TICKET #987 : this specialisation handles the variadic case and will be the only definition in future
template<typename RET, typename...ARGS>
struct BuildFunType<RET, TySeq<ARGS...>>
{
using Sig = RET(ARGS...);
using Fun = _Fun<Sig>;
using Func = function<Sig>;
using Functor = Func;
};
///////////////////////////////////////////////////////////////////////////////////TICKET #987 : the following specialisations become obsolete with the old-style type-sequence
template< typename RET>
struct BuildFunType<RET, Types<> >
{

View file

@ -338,6 +338,18 @@ namespace meta {
};
///////////////////////////////////////////////////////////////////////////////////TICKET #987 : this specialisation handles the variadic case and will be the only definition in future
template
< template<class,class,class, uint> class _X_
, class TUP
, uint i
>
class BuildTupleAccessor< _X_, TySeq<>, TUP, i>
{
public:
using Product = _X_<NullType, TUP, TUP, i>; // Note: i == tuple size
};
///////////////////////////////////////////////////////////////////////////////////TICKET #987 : the following specialisation will be obsoleted by the removal of old-style type-sequences
template
< template<class,class,class, uint> class _X_
, class TUP

View file

@ -84,10 +84,11 @@ namespace meta {
* Helper: prepend a type to an existing type sequence,
* thus shifting all elements within the sequence
* to the right, eventually dropping the last element
* @todo support variadic type-seq ////////////////////////////////////////////////////////////////TICKET #987 : make lib::meta::Types<TYPES...> variadic, then replace this by a single variadic template
*/
template<class T, class TYPES>
struct Prepend;
///////////////////////////////////////////////////////////////////////////////////TICKET #987 : the following specialisation will be obsoleted by the removal of old-style type-sequences
template< typename T01
, typename T02
, typename T03
@ -155,6 +156,21 @@ namespace meta {
using Seq = TySeq<T, TYPES...>;
using List = typename Types<T, TYPES...>::List;
};
template<class H, class T>
struct TySeq< Node<H,T> >
{
using List = Node<H,T>;
using Seq = typename Prepend< H
, typename TySeq<T>::Seq
>::Seq;
};
template<>
struct TySeq<NullType>
{
using List = NullType;
using Seq = TySeq<>;
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #987 temporary WORKAROUND -- to be obsoleted
@ -181,15 +197,62 @@ namespace meta {
struct StripNullType<Types<NullType, TYPES...>>
{
using Seq = TySeq<>; // NOTE: this causes the result to be a TySeq
};
///////////////////////////////////////////////////////////////////////////////////TICKET #987 : the following specialisation is a catch-all and becomes obsolete
template<typename...TYPES>
struct StripNullType<TySeq<TYPES...>>
{
using Seq = TySeq<TYPES...>;
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #987 temporary WORKAROUND(End) -- to be obsoleted
/** Helper: separate parts of a type sequence */
/** Helper: separate parts of a type sequence
* @todo support variadic type-seq ////////////////////////////////////////////////////////////////TICKET #987 : make lib::meta::Types<TYPES...> variadic, then replace this by a single variadic template
*/
template<class TYPES>
struct Split;
///////////////////////////////////////////////////////////////////////////////////TICKET #987 : this specialisation handles the variadic case and will be the only definition in future
template<typename T1, typename...TS>
struct Split<TySeq<T1,TS...> >
{
using List = typename TySeq<T1,TS...>::List;
using Head = T1;
using First = TySeq<T1>;
using Tail = TySeq<TS...>;
// for finding the end we need the help of typelist-util.hpp
using PrefixList = typename SplitLast<List>::List;
using TailList = typename Tail::List;
using Prefix = typename TySeq<PrefixList>::Seq;
using End = typename SplitLast<List>::Type;
using Last = TySeq<End>;
};
template<>
struct Split<TySeq<>>
{
using List = NullType;
using Head = NullType;
using First = TySeq<>;
using Tail = TySeq<>;
// for finding the end we need the help of typelist-util.hpp
using PrefixList = NullType;
using TailList = NullType;
using Prefix = TySeq<>;
using Last = TySeq<>;
using End = NullType;
};
///////////////////////////////////////////////////////////////////////////////////TICKET #987 : the following specialisation will be obsoleted by the removal of old-style type-sequences
template< typename T01
, typename T02
, typename T03
@ -275,6 +338,11 @@ namespace meta {
{
using Type = typename lib::meta::Shifted<Types<TYPES...>, i>::Head;
};
template<typename...TYPES, size_t i>
struct Pick<TySeq<TYPES...>, i>
{
using Type = typename Shifted<TySeq<TYPES...>, i>::Head;
};

View file

@ -207,7 +207,7 @@ namespace interact {
using lib::meta::_Fun;
using lib::meta::Split;
using lib::meta::Tuple;
using lib::meta::Types;
using lib::meta::TySeq;
using lib::meta::func::_Sig;
using lib::meta::func::PApply;
@ -220,7 +220,7 @@ namespace interact {
"Allocator function must accept UICoordinates (where to create/locate) as first argument");
static_assert (std::is_convertible<Ret, UICoord>::value,
"Allocator function must produce UICoordinates (of the actually allocated UI element)");
static_assert (std::is_convertible<FurtherArgs, Types<ARGS...>>::value,
static_assert (std::is_convertible<FurtherArgs, TySeq<ARGS...>>::value,
"Additional parameters of the allocator function must match the AllocSpec<ARGS> template parameters");

View file

@ -71,13 +71,28 @@ namespace control {
//
// _______________________________________________________________________________________________________________
/** @internal mix in a function operator */
/** @internal mix in a function operator
* @todo variadic type-seq //////////////////////////////////////////////////////////////////////TICKET #987 : make lib::meta::Types<TYPES...> variadic, then replace this by a single variadic template
*/
template< class TAR, class BA, class RET
, typename TYPES
>
struct AcceptArgs ;
///////////////////////////////////////////////////////////////////////////////////TICKET #987 : this specialisation handles the variadic case and will be the only definition in future
template<class TAR, class BA, class RET, typename...ARGS>
struct AcceptArgs<TAR,BA,RET, TySeq<ARGS...> >
: BA
{
RET
operator() (ARGS ...args)
{
return static_cast<TAR*> (this) -> bindArg (make_tuple (std::move(args) ...));
}
};
///////////////////////////////////////////////////////////////////////////////////TICKET #987 : the following specialisations become obsolete with the old-style type-sequence
/* specialisations for 0...9 Arguments.... */
template< class TAR, class BA, class RET
@ -262,13 +277,27 @@ namespace control {
//
// _______________________________________________________________________________________________________________
/** @internal mix in a \c bind() function
* @todo variadic type-seq //////////////////////////////////////////////////////////////////////TICKET #987 : make lib::meta::Types<TYPES...> variadic, then replace this by a single variadic template
*/
template< class TAR, class BA, class RET
, typename TYPES
>
struct AcceptBind ;
///////////////////////////////////////////////////////////////////////////////////TICKET #987 : this specialisation handles the variadic case and will be the only definition in future
template<class TAR, class BA, class RET, typename...ARGS>
struct AcceptBind<TAR,BA,RET, TySeq<ARGS...> >
: BA
{
RET
bind (ARGS ...args)
{
return static_cast<TAR*> (this) -> bindArg (make_tuple (std::move(args)...));
}
};
///////////////////////////////////////////////////////////////////////////////////TICKET #987 : the following specialisations become obsolete with the old-style type-sequence
/* specialisations for 0...9 Arguments.... */
template< class TAR, class BA, class RET

View file

@ -270,7 +270,7 @@ namespace test{
CHECK (d0.calc(0) == 11 + 22);
// The others even point into obsoleted storage holding zombie objects
CHECK (d1.calc(44) == rr + 44);
CHECK (d1.getVal() == Dummy::DEAD);
}
CHECK (0 == Dummy::checksum());

View file

@ -118,21 +118,21 @@ namespace test{
CHECK (Dummy::checksum() == dum1.getVal());
Dummy dum2{55};
CHECK (55 == dum2.getVal());
CHECK (dum2.getVal() == 55);
CHECK (Dummy::checksum() == dum1.getVal() + 55);
Dummy dum3{move (dum2)};
CHECK (55 == dum3.getVal());
CHECK (0 == dum2.getVal());
CHECK (dum3.getVal() == 55);
CHECK (dum2.getVal() == Dummy::DEFUNCT);
dum3.setVal (23);
CHECK (23 == dum3.getVal());
CHECK (dum3.getVal() == 23);
dum1 = move (dum3);
CHECK (23 == dum1.getVal());
CHECK (0 == dum2.getVal());
CHECK (0 == dum3.getVal());
CHECK (Dummy::checksum() == 23);
CHECK (dum1.getVal() == 23 );
CHECK (dum2.getVal() == Dummy::DEFUNCT);
CHECK (dum3.getVal() == Dummy::DEFUNCT);
CHECK (Dummy::checksum() == 23 );
Dummy::activateCtorFailure (true);
try {
@ -145,10 +145,10 @@ namespace test{
Dummy::checksum() -= v;
}
Dummy::activateCtorFailure (false);
CHECK (23 == dum1.getVal());
CHECK (0 == dum2.getVal());
CHECK (0 == dum3.getVal());
CHECK (Dummy::checksum() == 23);
CHECK (dum1.getVal() == 23 );
CHECK (dum2.getVal() == Dummy::DEFUNCT);
CHECK (dum3.getVal() == Dummy::DEFUNCT);
CHECK (Dummy::checksum() == 23 );
}
CHECK (Dummy::checksum() == 0);
}