Eliminate duplicate entries from Variable template arguments with C ++ 11

I use variational patterns with multiple virtual inheritance in C ++ to combine types into a single structure definition.

Here is an example of a set of structures:

struct meas { int i; }; struct meas2 : public virtual meas { int j; }; struct meas3 : public virtual meas { int k; }; 

Then I aggregate them using several virtual inheritances:

 template <typename... Args> struct zipper : public virtual Args... {}; 

Then I can:

 typedef zipper<meas, meas2> meas_type; meas* m = new meas_type; 

Then they can be cascaded:

 typedef zipper<meas3, meas_type> meas_type2; 

The resulting object, however, is rather cumbersome:

 $46 = (zipper<meas3, zipper<meas, meas2> >) { <meas3> = { <meas> = { i = 0 }, members of meas3: _vptr.meas3 = 0x400ec8, k = 0 }, <zipper<meas, meas2>> = { <meas2> = { members of meas2: _vptr.meas2 = 0x400ee0, j = 6299120 }, members of zipper<meas, meas2>: _vptr.zipper = 0x400eb0 }, <No data fields>} 

according to gdb.

There is also a secondary problem when trying to archive the same base type:

 typedef zipper<meas, meas> meas_type2; 

The above compiler error "duplicate base class" meas "is not valid" in g ++ 4.6.3.

So the question is twofold:

  • Is there any way to convert zipper<meas3, zipper<meas, meas2>> to zipper<meas3, meas2> ?
  • Is there a way, when doing # 1, to remove duplicate entries in the type list?

Thanks!

+7
source share
1 answer

My strategy for solving this problem is to use several levels of indirection.

  • lightning <Args ...> sends the processing of its arguments to the process_zipper_arguments function by inheritance:

Example:

 template < typename... Args > struct zipper : zipper < typename process_zipper_arguments < Args... >::type > {}; 
  • use template < typename... Args > struct typelist {} to track the types of objects you want to inherit from.
  • Specialize struct zipper < typelist < Args... > >: public virtual Args... to do the actual inheritance

To get rid of duplicate parent types, process_zipper_arguments uses two helper functions:

  • is_in < CandidateType, typelist< Args... > >::type - either true_type or false_type and can be defined recursively
  • add_unique < CandidateType, typelist< Args... > >::type is a typelist <...> to which either CandidateType is added or not. It calls is_in to determine this.

Here is the complete code that compiles with at least g ++ (GCC) 4.6.3 with -std = C ++ 0x. Criticism of this is welcome.

 // Forward declarations template < typename... Args > struct zipper; // Two types meaning true and false struct true_type {}; struct false_type {}; // The only purpose of this struct is to be associated with Types... template < typename... Types > struct typelist {}; // =================================================== // is_in < type, typelist<...> >::type // is true_type if type is in typelist // is false_type if type is not in typelist // Assume TElement is not in the list unless proven otherwise template < typename TElement, typename TList > struct is_in { typedef false_type type; }; // If it matches the first type, it is definitely in the list template < typename TElement, typename... TTail > struct is_in < TElement, typelist < TElement, TTail... > > { typedef true_type type; }; // If it is not the first element, check the remaining list template < typename TElement, typename THead, typename... TTail > struct is_in < TElement, typelist < THead, TTail... > > { typedef typename is_in < TElement, typelist < TTail... > >::type type; }; // =================================================== // add_unique < TNew, typelist<...> >::type // is typelist < TNew, ... > if TNew is not already in the list // is typelist <...> otherwise // Append a type to a type_list unless it already exists template < typename TNew, typename TList, typename Tis_duplicate = typename is_in < TNew, TList >::type > struct add_unique; // If TNew is in the list, return the list unmodified template < typename TNew, typename... TList > struct add_unique < TNew, typelist < TList... >, true_type > { typedef typelist < TList... > type; }; // If TNew is not in the list, append it template < typename TNew, typename... TList > struct add_unique < TNew, typelist < TList... >, false_type > { typedef typelist < TNew, TList... > type; }; // =================================================== // process_zipper_arguments < Args... >::type // returns a typelist of types to be inherited from. // // It performs the following actions: // a) Unpack zipper<...> and typelist <...> arguments // b) Ignore values that are already in the list template < typename... Args > struct process_zipper_arguments; // Unpack a zipper in the first argument template < typename... ZipperArgs, typename... Args > struct process_zipper_arguments < zipper < ZipperArgs... >, Args... > { typedef typename process_zipper_arguments < ZipperArgs..., Args... >::type type; }; // Unpack a typelist in the first argument template < typename... TypeListArgs, typename... Args > struct process_zipper_arguments < typelist < TypeListArgs... >, Args... > { typedef typename process_zipper_arguments < TypeListArgs..., Args... >::type type; }; // End the recursion if the list is empty template < > struct process_zipper_arguments < > { typedef typelist < > type; }; // Construct the list of unique types by appending them one by one template < typename THead, typename... TTail > struct process_zipper_arguments < THead, TTail... > { typedef typename add_unique < THead, typename process_zipper_arguments < TTail... >::type >::type type; }; // =================================================== // The zipper class that you might want // If the list of types is not yet known, process it. // The inheritance is ugly, but there is a workaround template < typename... Args > struct zipper : zipper < typename process_zipper_arguments < Args... >::type > { // // Instead of inheriting, you can use zipper as a factory. // // So this: // typedef zipper < meas2, zipper < meas1, meas > > mymeas; // // Turns to: // typedef typename zipper < meas2, zipper < meas1, meas > >::type mymeas; typedef zipper < typename process_zipper_arguments < Args... >::type > type; }; // If the list of types is known, inherit from each type template < typename... Args > struct zipper < typelist < Args... > > : public virtual Args... {}; // =================================================== // Short usage demo, replace with your own code struct meas { int i; }; struct meas2 { int j; }; struct meas3 { int k; }; typedef zipper < meas, meas, meas3 > meas_type; typedef zipper < meas2, meas_type, meas2 > meas_type2; typedef typename zipper < meas_type2 >::type nicer_meas_type2; int main ( int, char** ) { meas * m = new meas_type2; meas_type2 n; nicer_meas_type2 o; return 0; } 

Debugging gives the following result (breakpoint on line return 0; ):

 (gdb) print *m $1 = {i = 0} (gdb) print n $2 = {<zipper<typelist<meas, meas3, meas2> >> = {<meas> = {i = 4196320}, <meas3> = {k = 0}, <meas2> = {j = 0}, _vptr.zipper = 0x400928}, <No data fields>} (gdb) print o $3 = {<meas> = {i = 4195719}, <meas3> = {k = 0}, <meas2> = {j = 1}, _vptr.zipper = 0x4009a8 <VTT for zipper<typelist<meas, meas3, meas2> >>} 
+7
source

All Articles