Flag-based multiple inheritance

I have several classes, for example A , B and C , as well as the corresponding flags HAS_A=1 , HAS_B=2 and HAS_C=4 . Is it possible to write a class so that its parents (from A , B and C ) are defined by a combination of these flags?

Example:

 ParentsFromFlags<HAS_A | HAS_C> x; // x ends up having the features of A and C 

I know that I can have variables of several parents with <typename... Parents> , but I would like this because I want to make sure that if A , B and C are parents of this class, they will always appear in a certain okay.

+4
source share
4 answers

This does the work by introducing some extra class in the hierarchy ...

 enum ParentFlags { HAS_A = 1, HAS_B = 2, HAS_C = 4 }; class A{}; class B{}; class C{}; template <int M> class ParentClass {}; template <> class ParentClass<0>{}; template <> class ParentClass<HAS_A> : public A {}; template <> class ParentClass<HAS_B> : public B{}; template <> class ParentClass<HAS_C> : public C{}; template <int F, int M> class ParentTraits : public ParentClass<F & M>, public ParentTraits<F & ~M, M << 1> {}; template <int M> class ParentTraits<0, M> {}; template <int F> class ParentFromFlags : public ParentTraits<F, 1> { }; int main() { ParentFromFlags<HAS_A | HAS_B> ab; ParentFromFlags<HAS_A | HAS_C> ac; ParentFromFlags<HAS_A | HAS_B | HAS_C> abc; return 0; } 
+7
source

This is a little more general than the one you are looking for, but now you need to replace filtered_list with a meta function that filters classes based on your flags.

 template<typename... T> struct type_list; template<typename T> struct filtered_list; template<typename T, typename... U> struct filtered_list<type_list<T,U...>> { using type = type_list<U...>; }; template<typename TypeList> using filtered_list_t = typename filtered_list<TypeList>::type; template<typename T> struct collect_base_classes; template<typename... T> struct collect_base_classes<type_list<T...>> : public T... {}; struct A { void test_a() {} }; struct B { void test_b() {} }; struct C { void test_c() {} }; class Test : public collect_base_classes<filtered_list_t<type_list<A,B,C>>> {}; int main() { Test t; t.test_a(); //error, we dropped A from our base class list t.test_b(); t.test_c(); } 
+4
source

There is a simple way and the best way to solve this problem.

The simplest solution is to simply use a compilation type selector of type conditional_t ​​in combination with a class with an empty base:

 template <int M> struct empty_base {}; template <int flags> struct Foo : std::conditional_t<flags & Has_A, A, empty_base<1>> , std::conditional_t<flags & Has_B, B, empty_base<2>> , std::conditional_t<flags & Has_C, C, empty_base<3>> { int x; }; 

The problem with this approach is that it will not be able to start empty optimization of the base class in C ++, due to the use of multiple inheritance. As a result, the values ​​of Foo will be one word more than necessary.

You can solve this by linking the databases like this boost.compressed_pair :

 template <class T1, class T2> struct compressed_pair_of_bases: T1, T2 {}; template <class T1, int N> struct compressed_pair_of_bases<T1, empty_base<N>>: T1 {}; template <bool Predicate, class T, class Next> using predicated_parent_chain_t = typename std::conditional_t<Predicate, compressed_pair_of_bases<T, Next>, Next>; template <int flags> struct Bar : predicated_parent_chain_t<!!(flags & Has_A), A, predicated_parent_chain_t<!!(flags & Has_B), B, predicated_parent_chain_t<!!(flags & Has_C), C, empty_base<1>>>> { int x; }; 

This solution allows you to fully optimize the basic types if they are not selected:

  std::cout << sizeof(Bar<0>); // prints 4 on a 32-bit target 
+2
source

This is actually what I came across. This is basically the same as marom's solution , but a bit more readable (at least for me)

 enum ParentFlags { HAS_A = 1, HAS_B = 2, HAS_C = 4 }; class A { int a; }; class B { int b[2]; }; class C { int c[3]; }; template <int Flags> class ParentA {}; template <> class ParentA<HAS_A> : public A {}; template <int Flags, int ThisFlag = Flags & HAS_B> class ParentB : public ParentA<Flags & ~HAS_B> {}; template <int Flags> class ParentB<Flags, HAS_B> : public ParentA<Flags & ~HAS_B>, public B {}; template <int Flags, int ThisFlag = Flags & HAS_C> class ParentC : public ParentB<Flags & ~HAS_C> {}; template <int Flags> class ParentC<Flags, HAS_C> : public ParentB<Flags & ~HAS_C>, public C { }; template <int Flags> class ParentFromFlags : public ParentC<Flags> {}; 
0
source

All Articles