After fiddling around with various solutions from this thread I decided on this solution:
A two-dimensional "tuple pack", i.e. tuple< tuple< T1 >, tuple<T2,T3>, ... > etc.
This allows a couple of things:
- I'd like to not use a container, but there is no such thing as "default variadic parameters" so the tuple system allows default parameters for a 2D tuple pack merely by not being a variadic type-pack.
- Each sub-tuple can have a different number of arguments if desired. This allows any default template parameters to come into play - i.e. in cases where the sub-tuple is less specified than the referenced template-template (t_tempMPlex).
- Also I'd like to place the result in any holder that might be capable of holding the result - i.e. tuple, variant, or any other variadic holder that a user might want to fill with types (t_tempVarHolder).
// Multiplex templates with 2-dimensional tuple-types and contain them in some type of variant/tuple/etc container.
template < template<class... > class t_tempMPlex, class t_TyTpTPack >
struct _MPlexTPack2DHelp_2;
template < template<class... > class t_tempMPlex, class ... t_TysExtractTPack >
struct _MPlexTPack2DHelp_2< t_tempMPlex, tuple< t_TysExtractTPack ... > >
{
typedef t_tempMPlex< t_TysExtractTPack ... > type;
};
template< template<class... > class t_tempMPlex, class t_TyTp2DTPack,
template < class ... > class t_tempVarHolder >
struct _MPlexTPack2DHelp;
template< template<class... > class t_tempMPlex, class ... t_TyTpsExtractTPack,
template < class ... > class t_tempVarHolder >
struct _MPlexTPack2DHelp<t_tempMPlex, tuple< t_TyTpsExtractTPack ... >, t_tempVarHolder >
{
using type = t_tempVarHolder< typename _MPlexTPack2DHelp_2< t_tempMPlex, t_TyTpsExtractTPack >::type ... >;
};
template< template<class... > class t_tempMPlex, class t_TyTp2DTPack,
template < class ... > class t_tempVarHolder = tuple >
struct MPlexTPack2D
{
using type = typename _MPlexTPack2DHelp< t_tempMPlex, t_TyTp2DTPack, t_tempVarHolder >::type;
};
template< template<class... > class t_tempMPlex, class t_TyTp2DTPack,
template < class ... > class t_tempVarHolder = tuple >
using MPlexTPack2D_t = typename MPlexTPack2D< t_tempMPlex, t_TyTp2DTPack, t_tempVarHolder >::type;
Usage: Here's my usage scenario: I am writing an XML parser that works natively in any character type. I also want to support switching the endian nature of the file in the scenarios that matter - i.e. for UTF32BE, and UTF16BE - of course when I am on a little endian machine.
So I have these transport types:
template < class t_TyChar, class t_TyBoolSwitchEndian = false_type >
class _l_transport_file;
template < class t_TyChar, class t_TyBoolSwitchEndian = false_type >
class _l_transport_fixedmem;
template < class t_TyChar, class t_TyBoolSwitchEndian = false_type >
class _l_transport_mapped;
I want to provide a default argument for my variant parser that implements all character types and endian-switching possibilities, but I also want the user to be able to specify that they want less than that.
Here is the declaration of my xml_parser_var:
template < template < class ... > class t_tempTyTransport,
class t_TyTp2DCharPack >
class xml_parser_var;
Where t_tempTyTransport is one of the above _l_transport_* templates.
I will provide a default argument of this for t_TyTp2DCharPack:
tuple< tuple< char32_t, true_type >,
tuple< char32_t, false_type >,
tuple< char16_t, true_type >,
tuple< char16_t, false_type >,
tuple< char8_t, false_type > >
But I'd like the user to be able to specify less - i.e. perhaps the user programmer doesn't care about UTF32 files, only UTF16 and UTF8. A significant amount of binary space will be saved in the variant if the UTF32 character types are removed.
Anyway, short story long, this is what I came up with. I like it. It allows default arguments to come into play, i.e. this is the same as the above given the default arguments:
tuple< tuple< char32_t, true_type >,
tuple< char32_t >,
tuple< char16_t, true_type >,
tuple< char16_t >,
tuple< char8_t > >
Here is my usage sequence:
typedef MPlexTPack2D_t< t_tempTyTransport, t_TyTp2DCharPack > _TyTpTransports;
_TyTpTransports ends up being:
tuple< t_tempTyTransport< char32_t, true_type >, t_tempTyTransport< char32_t >
t_tempTyTransport< char16_t, true_type >, t_tempTyTransport< char16_t >,
t_tempTyTransport< char8_t > >
Then that "tuple pack" can be use to produce further typedefs, etc.
Also, if I want a variant instead of a tuple, i.e.:
variant< t_tempTyTransport< char32_t, true_type >, t_tempTyTransport< char32_t >
t_tempTyTransport< char16_t, true_type >, t_tempTyTransport< char16_t >,
t_tempTyTransport< char8_t > >
Then I use this instead:
typedef MPlexTPack2D_t< t_tempTyTransport, t_TyTp2DCharPack, variant > _TyTpTransports;
Synposis:
typedef MPlexTPack2D_t< variant,
tuple< tuple< char32_t, true_type >,
tuple< char32_t >,
tuple< char16_t, true_type >,
tuple< char16_t >,
tuple< char8_t, false_type > > > _TyTuple2D;
static_assert( is_same_v< _TyTuple2D,
tuple< variant< char32_t, true_type >,
variant< char32_t >,
variant< char16_t, true_type >,
variant< char16_t >,
variant< char8_t, false_type > > > );
Let me know what y'all think.