I have a type build
that has a flag template, and according to the active flag bits, it inherits from those types. this allows me to "build" classes from many subclasses with a great number of configurations:
#include <type_traits>
#include <cstdint>
struct A { void a() {} };
struct B { void b() {} };
struct C { void c() {} };
struct D { void d() {} };
constexpr std::uint8_t FLAG_BIT_A = 0b1 << 0;
constexpr std::uint8_t FLAG_BIT_B = 0b1 << 1;
constexpr std::uint8_t FLAG_BIT_C = 0b1 << 2;
constexpr std::uint8_t FLAG_BIT_D = 0b1 << 3;
struct empty {};
template<std::uint8_t flags>
using flag_a_type = std::conditional_t<(flags & FLAG_BIT_A), A, empty>;
template<std::uint8_t flags>
using flag_b_type = std::conditional_t<(flags & FLAG_BIT_B), B, empty>;
template<std::uint8_t flags>
using flag_c_type = std::conditional_t<(flags & FLAG_BIT_C), C, empty>;
template<std::uint8_t flags>
using flag_d_type = std::conditional_t<(flags & FLAG_BIT_D), D, empty>;
template<std::uint8_t flags>
struct build :
flag_a_type<flags>, flag_b_type<flags>, flag_c_type<flags>, flag_d_type<flags> {
};
int main() {
build<FLAG_BIT_A | FLAG_BIT_C> foo;
}
so build<FLAG_BIT_A | FLAG_BIT_C>
should result in a class that inherits from A
and from C
.
but it doesn't compile, saying empty
is already a direct base class:
error C2500: 'build<5>': 'empty' is already a direct base class
how can I achieve this without having to make 4 different empty structs to avoid the clash?
Here's another approach using c++20 that provides much more flexibility for bitmask-based inheritance:
#include <concepts>
#include <cstdint>
template <std::integral auto, class...>
struct inherit_mask {};
template <auto flags, class Base, class... Bases>
requires((flags & 1) == 1)
struct inherit_mask<flags, Base, Bases...>
: Base, inherit_mask<(flags >> 1), Bases...> {};
template <auto flags, class Base, class... Bases>
struct inherit_mask<flags, Base, Bases...>
: inherit_mask<(flags >> 1), Bases...> {};
struct A { void a() {} };
struct B { void b() {} };
struct C { void c() {} };
struct D { void d() {} };
template <std::uint8_t flags>
using build = inherit_mask<flags, A, B, C, D>;
using foo = build<0b0101>;
static_assert(std::derived_from<foo, A>);
static_assert(not std::derived_from<foo, B>);
static_assert(std::derived_from<foo, C>);
static_assert(not std::derived_from<foo, D>);
Compiler Explorer
Works on clang, gcc, and msvc, and doesn't lead to exponential instantiation explosion.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With