Reputation: 297
Suppose I have compile time polymorphic inheritance structure:
enum class Enum1 {
Undefined = 0;
/* ... */
};
enum class Enum2 {
Undefined = 0;
/* ... */
};
template<Enum1 A, Enum2 B>
struct Base {
int state;
};
struct Derived : public Base <Enum1::Undefined, Enum2::Undefined>> {
int statederived;
};
Is there a way to do something like:
template<Base<Enum1, Enum2> DerivedTemplate>
using Function = std::function<void (DerivedTemplate&)>;
to accomplish:
Function<Derived> & function;
And or create a class based upon these derived types with metaprogramming?
I could just create a class for each of these derived types, but I'd like to avoid that since I have around 50 of them.
Essentially, I would like to avoid clashes of different derived types with the same template arguments, while still enforcing the conceptual constraints.
Upvotes: 4
Views: 102
Reputation: 1015
if you want to add a constraint on Function
to let DerivedTemplate
to be derived from Base<someEnum1, someEnum2>
, you can check if DeriveTemplate&&
can be converted to Base<someEnum1, someEnum2>&&
with template parameter someEnum1
and someEnum2
:
template<Enum1 e1, Enum2 e2>
std::int32_t test(Base<e1, e2>&);
template<Enum1 e1, Enum2 e2>
std::int32_t test(Base<e1, e2> const&);
template<Enum1 e1, Enum2 e2>
std::int32_t test(Base<e1, e2>&&);
template<Enum1 e1, Enum2 e2>
std::int32_t test(Base<e1, e2> const&&);
template<typename T>
std::int8_t helper(...);
template<typename T>
std::int32_t helper(decltype(test(std::declval<T&&>()))*);
template<typename D>
inline constexpr bool is_base_of_Base_with_some_Enum = sizeof(helper<D>(nullptr)) == sizeof(std::int32_t);
and then you can change the type alias Function
to:
template<typename D>
struct FunctionImpl : std::enable_if<is_base_of_Base_with_some_Enum<D>, std::function<void(D&)>>{};
template<typename Derive>
using Function = typename FunctionImpl<Derive>::type;
Upvotes: 2
Reputation: 275310
struct Derived : public Base <Enum1::Undefined, Enum2::Undefined>> {
int statederived;
};
This creates a mapping from Derived
to Base<Enum1::Undefined, Enum2::Undefined>
, but does not create a mapping the other way around.
Now you could do this
template<Enum e1, Enum e2, class D>
struct DerivedExtra:Base<e1, e2> {
// can static cast to D to get Derived stuff
};
template<Enum1 e1, Enum2 e2>
struct Derived:
DerivedExtra<e1, e2, Derived<e1, e2>>
{
constexpr auto E1 = e1;
constexpr auto E2 = e2;
// common codes goes here
};
Write any custom Derived state by specializing DerivedExtra
:
template<class D>
struct DerivedExtra<
Derived<Enum1::Undefined, Enum2::Undefined>,
D
>:
Base<Enum1::Undefined, Enum2::Undefined>
{
int statederived;
};
Now we can take a list of Enum1
and Enum2
states and make the cartesian product.
template<auto x>
using constant_t = std::integral_constant<std::decay_t<decltype(x)>, x>;
template<auto x>
constexpr constant_t<x> constant = {};
constexpr auto enum1s = std::make_tuple(
constant<Enum1::Undefined>,
constant<Enum1::Defined>
);
constexpr auto enum2s = std::make_tuple(
constant<Enum2::Undefined>,
constant<Enum2::Defined>
);
constexpr auto e1xe2 = std::apply( [](auto...e1s) {
return std::tuple_cat(
std::apply([e1=e1s](auto...e2s) {
return std::make_tuple(
std::make_tuple(
e1,
e2s
)...
);
}, enum2s )...
);
}, enum1s );
Using e1xe2s
you can make a std::tuple<std::function<void(Derived<a, b>)...>
or a variant of same, for example.
Upvotes: 2