Motivation:
Given a class hierarchy (and using CRTP technique with mixin tp. classes, but it is omitted here for the sake of simplicity), I would like to generically address a nested type with a known identifier, but possibly with "unknown" parent classes.
Minimal example with my first (unsuccessful) attempt:
#include <type_traits>
using namespace std;
struct A {
using Type_x = int;
using Type_y = char;
};
struct B {
using Type_x = float;
using Type_y = double;
};
template <typename T> struct C {
using Type_x = typename T::Type_x;
using Type_y = typename T::Type_y;
};
// not possible
// template <typename T, typename U> using Tp_type = typename T::typename U;
//
// static_assert(is_same_v<C<A>::Type_x, Tp_type<C<A>, Type_x>>);
I came up with a workaround solution, which seems to be functional, but is not too pretty. Here is an example where a derived class also introduces further nested type, but it is still handled in a quite generic way:
/// given the code above ...
struct None {};
constexpr int type_x_id = 1;
constexpr int type_y_id = 2;
template <typename T>
struct D1 : C<T> {
using typename C<T>::Type_x;
using typename C<T>::Type_y;
template <int typeId>
using Type = conditional_t<typeId == type_x_id, Type_x,
conditional_t<typeId == type_y_id, Type_y,
None>>;
};
constexpr int type_z_id = 3;
template <typename T>
struct D2 : D1<T> {
using Type_z = long;
template <int typeId>
using Type = conditional_t<!is_same_v<typename D1<T>::template Type<typeId>, None>,
typename D1<T>::template Type<typeId>,
conditional_t<typeId == type_z_id, Type_z,
None>>;
};
template <typename T, int typeId> using Tp_type = typename T::template Type<typeId>;
Usage/tests:
static_assert(is_same_v<D1<A>::Type_x, Tp_type<D1<A>, type_x_id>>);
static_assert(is_same_v<D1<A>::Type_y, Tp_type<D1<A>, type_y_id>>);
// static_assert(!is_same_v<D1<A>::Type_z, Tp_type<D1<A>, type_z_id>>);
static_assert(is_same_v<D2<A>::Type_x, Tp_type<D1<A>, type_x_id>>);
static_assert(is_same_v<D2<A>::Type_y, Tp_type<D1<A>, type_y_id>>);
static_assert(!is_same_v<D2<A>::Type_z, Tp_type<D1<A>, type_z_id>>);
static_assert(is_same_v<D1<A>::Type_x, Tp_type<D2<A>, type_x_id>>);
static_assert(is_same_v<D1<A>::Type_y, Tp_type<D2<A>, type_y_id>>);
// static_assert(!is_same_v<D1<A>::Type_z, Tp_type<D2<A>, type_z_id>>);
static_assert(is_same_v<D2<A>::Type_x, Tp_type<D2<A>, type_x_id>>);
static_assert(is_same_v<D2<A>::Type_y, Tp_type<D2<A>, type_y_id>>);
static_assert(is_same_v<D2<A>::Type_z, Tp_type<D2<A>, type_z_id>>);
static_assert(is_same_v<D1<B>::Type_x, Tp_type<D1<B>, type_x_id>>);
static_assert(is_same_v<D1<B>::Type_y, Tp_type<D1<B>, type_y_id>>);
// static_assert(!is_same_v<D1<B>::Type_z, Tp_type<D1<B>, type_z_id>>);
static_assert(is_same_v<D2<B>::Type_x, Tp_type<D1<B>, type_x_id>>);
static_assert(is_same_v<D2<B>::Type_y, Tp_type<D1<B>, type_y_id>>);
static_assert(!is_same_v<D2<B>::Type_z, Tp_type<D1<B>, type_z_id>>);
static_assert(is_same_v<D1<B>::Type_x, Tp_type<D2<B>, type_x_id>>);
static_assert(is_same_v<D1<B>::Type_y, Tp_type<D2<B>, type_y_id>>);
// static_assert(!is_same_v<D1<B>::Type_z, Tp_type<D2<B>, type_z_id>>);
static_assert(is_same_v<D2<B>::Type_x, Tp_type<D2<B>, type_x_id>>);
static_assert(is_same_v<D2<B>::Type_y, Tp_type<D2<B>, type_y_id>>);
static_assert(is_same_v<D2<B>::Type_z, Tp_type<D2<B>, type_z_id>>);
static_assert(!is_same_v<D1<A>::Type_x, Tp_type<D1<B>, type_x_id>>);
static_assert(!is_same_v<D1<A>::Type_y, Tp_type<D1<B>, type_y_id>>);
// static_assert(!is_same_v<D1<A>::Type_z, Tp_type<D1<B>, type_z_id>>);
static_assert(!is_same_v<D2<A>::Type_x, Tp_type<D1<B>, type_x_id>>);
static_assert(!is_same_v<D2<A>::Type_y, Tp_type<D1<B>, type_y_id>>);
static_assert(!is_same_v<D2<A>::Type_z, Tp_type<D1<B>, type_z_id>>);
static_assert(!is_same_v<D1<A>::Type_x, Tp_type<D2<B>, type_x_id>>);
static_assert(!is_same_v<D1<A>::Type_y, Tp_type<D2<B>, type_y_id>>);
// static_assert(!is_same_v<D1<A>::Type_z, Tp_type<D2<B>, type_z_id>>);
static_assert(!is_same_v<D2<A>::Type_x, Tp_type<D2<B>, type_x_id>>);
static_assert(!is_same_v<D2<A>::Type_y, Tp_type<D2<B>, type_y_id>>);
static_assert(is_same_v<D2<A>::Type_z, Tp_type<D2<B>, type_z_id>>);
Is there a better solution? For example, a one that would not require to define Type
every time a new nested type identifier comes along.
CodePudding user response:
You can't pass names around and look up types based on them without writing entire libraries or using compile-time reflection (which looks like c 26 at this point).
But you want to be able to pass around identifiers at compile time. Your int
solution is a bit awkward; I'd suggest passing around templates that do the lookup:
template<class T>
using Type_x_t = typename T::Type_x;
template<class T>
using Type_y_t = typename T::Type_y;
etc
Then you can do:
template <typename T, template<class...> class U_t>
using Tp_type_t = U_t<T>;
static_assert(
std::is_same_v<D1<A>::Type_x, Tp_type_t<D1<A>, Type_x_t>);
);
or:
static_assert(
std::is_same_v<D1<A>::Type_x, Type_x_t<D1<A>>);
);
We pass around template Foo_t<>
that maps T
to T::Foo
, then that acts as a "name" for a subtype.