Reputation: 21
In my project I played with the following design :
enum {
A = 1, B = 2, C = 4
};
struct Foo { int foo; };
template <int> struct Bar;
template <> struct Bar<A> : public virtual Foo { int a; };
template <> struct Bar<B> : public virtual Foo { int b; };
template <> struct Bar<C> : public virtual Foo { int c; };
Now I can do something fun like :
template <> struct Bar<A|B> : public A, public B {};
template <> struct Bar<A|C> : public A, public C {};
template <> struct Bar<B|C> : public B, public C {};
template <> struct Bar<A|B|C> : public A, public B, public C {};
So that I can write :
Bar<A|C> bar;
bar.foo = 2;
bar.a = 1;
bar.c = 2;
Now I would like the generation of the combination classes Bar<X|Y|Z|..>
to be automatically done when the user creates such an instance.
Is this possible using some template magic ?
Something along the lines of :
template <int N, class ...Classes> struct Bar<N> : public Classes... {};
template <int N> struct Bar<N> : public Bar<N, generate_classes<N> > {};
where generate_classes
would be able to generate the list of classes Bar<N>
should inherit from.
Upvotes: 2
Views: 111
Reputation: 120079
No need in fancy schmancy SFINAE, parameter packs or any such dark wizardry.
enum {
A = 1, B = 2, C = 4
};
struct Foo { int foo; };
template <unsigned int> struct Bar; // unsigned because bit fiddling
template <> struct Bar<A> : public virtual Foo { int a; };
template <> struct Bar<B> : public virtual Foo { int b; };
template <> struct Bar<C> : public virtual Foo { int c; };
template <unsigned int i> struct Bar :
public Bar<i & ~(i-1)>, // only least significant set bit
public Bar<i & (i-1)> // all other set bits
{ };
// checking
int main ()
{
Bar<A|B|C> abc;
abc.a = 0; // ok
abc.b = 0; // ok
abc.c = 0; // ok
Bar<A|B> ab;
ab.a = 0; // ok
ab.b = 0; // ok
ab.c = 0; // error
Bar<A|C> ac;
ac.a = 0; // ok
ac.b = 0; // error
ac.c = 0; // ok
Bar<9> x; // error
}
Upvotes: 3
Reputation: 275936
Some machinery to do compile-time unpacking of flags:
enum flag_e { None = 0, A = 1, B = 1<<1, C = 1<<2 };
template<flag_e...>
struct flags {using type=flags; constexpr flags(){}};
template<flag_e>
struct flag{using type=flag; constexpr flag(){}};
constexpr flags<A,B,C> all_flags{};
template<flag_e...lhs, flag_e...rhs>
constexpr flags<lhs...,rhs...> operator+(flags<lhs...>, flags<rhs...>)
{ return {}; }
template<flag_e lhs, flag_e...rhs>
inline constexpr flags<lhs, rhs...> operator+(flag<lhs>, flags<rhs...>)
{ return {}; }
template<flag_e...lhs, flag_e rhs>
inline constexpr flags<lhs..., rhs> operator+(flags<lhs...>, flag<rhs>)
{ return {}; }
template<flag_e...fs>
inline constexpr flags<fs...> operator+(flag<None>, flags<fs...>)
{ return {}; }
template<flag_e...fs>
inline constexpr flags<fs...> operator+(flags<fs...>, flag<None>)
{ return {}; }
template<flag_e f, flag_e...fs>
inline constexpr auto unpack( flag<f>, flags<fs...> x, flags<> )
-> flags<fs...>
{ return {}; }
template<flag_e f, flag_e...fs, flag_e c0, flag_e...checks>
inline constexpr auto unpack( flag<f> fin, flags<fs...> x, flags<c0, checks...> )
-> decltype( unpack( fin, x+flag<flag_e(f&c0)>{}, flags<checks...>{} ) )
{ return {}; }
template<flag_e f>
inline constexpr auto unpack( flag<f> fin )
-> decltype( unpack( flag<f>{}, flags<>{}, all_flags ) )
{ return {}; }
Then we use it:
template <int> struct Bar;
template <class flags> struct BarImpl;
template <flag_e...fs> struct BarImpl<flags<fs...>>:
Bar<fs>...
{};
template <int flags> struct Bar:
BarImpl<decltype(unpack(flag<flag_e(flags)>{}))>
{};
struct Foo { int foo; };
template <> struct Bar<A> : public virtual Foo { int a; };
template <> struct Bar<B> : public virtual Foo { int b; };
template <> struct Bar<C> : public virtual Foo { int c; };
The code that lets you have bundles of flags and individual flags can be made more generic at the cost of mentioning the flag_e
type more often.
I made it overly slick, with the ability to say flags<A>+flags<B>
and get flags<A,B>
, because I like that notation.
I then wrote unpack
, which takes flag<A|B>
and produces flags<A,B>
.
In C++14 and 17 things get slicker, which folds and return type deduction and the like.
Upvotes: 0
Reputation: 1219
Instead of thinking about somehow generating a list of classes to be inherited you can think of it as choosing them. This one should be inherited, this one shouldn't. This can be achieved by static dispatching through template parameter. So we get a template that based on a bool parameter gives either T or an empty class. It is likely to be optimized (it may even be guaranteed in this case, but I'm not sure what exact requirements given by the standard are) by empty base optimization so there is no memory overhead.
#include <iostream>
enum {
A = 1, B = 2, C = 4
};
template <class T, bool Enable>
struct or_empty;
template <class T>
struct or_empty<T, false>
{
struct empty {};
using type = empty;
};
template <class T>
struct or_empty<T, true>
{
using type = T;
};
template <class T, bool Enable>
using or_empty_t = typename or_empty<T, Enable>::type;
struct Foo { int foo; };
template <int I> struct Bar :
public or_empty_t<Bar<A>, I&A>,
public or_empty_t<Bar<B>, I&B>,
public or_empty_t<Bar<C>, I&C> {};
template <> struct Bar<A> : public virtual Foo { int a; };
template <> struct Bar<B> : public virtual Foo { int b; };
template <> struct Bar<C> : public virtual Foo { int c; };
int main()
{
Bar<A|C> bar;
bar.foo = 2;
bar.a = 1;
// bar.b = 2; error
bar.c = 2;
std::cout << bar.foo << ' ' << bar.a << ' ' << bar.c << '\n';
std::cout << sizeof(Bar<A>) << ' ' << sizeof(Bar<A|B>) << ' ' << sizeof(Bar<A|B|C>) << '\n';
}
Demo: http://coliru.stacked-crooked.com/a/f170fbd873739c38
Upvotes: 0