Robbie
Robbie

Reputation: 297

Nested Conceptual Polymorphic Templates

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

Answers (2)

RedFog
RedFog

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;

Example.

Upvotes: 2

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

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 );

Live example.

Using e1xe2s you can make a std::tuple<std::function<void(Derived<a, b>)...> or a variant of same, for example.

Upvotes: 2

Related Questions