This is follow-up to Wrap each base class in a template class .
Playing around, I figured out a way to convert a tuple of distinct types to a struct inheriting from each of them:
#include <cstdio>
#include <tuple>
#include <type_traits>
namespace detail {
template <typename... Ts>
struct tuple_to_struct_impl : Ts... {};
}
template <typename>
struct tuple_to_struct;
template <typename... Ts>
struct tuple_to_struct<std::tuple<Ts...>> :
detail::tuple_to_struct_impl<Ts...> {};
struct x { int i; };
struct y { int j; };
struct z { int k; };
using t = std::tuple<x, y, z>;
using xyz = tuple_to_struct<t>;
int main() {
xyz s{ 1, 2, 3 };
std::printf("%d, %d, %d", s.i, s.j, s.k);
}
Is there a way to reverse this, i.e. implement struct_to_tuple
? std::is_base_of
requires to know both types up front, so I would have to run it with a list of all possible candidates, e.g. struct_to_tuple<xyz, x, y, z, std::string...>
.
Get base class for a type in class hierarchy touches on the topic, but looks out of date with some speculation on C++14. It also lists multiple inheritance as a problem, while it is exactly what I want.
The answer you link is still correct. There are more related questions that suggest std::bases
type trait was proposed but rejected (see eg Status of std::bases and std::direct_bases). However, gcc supports it as tr2
:
A rather simplistic demo:
#include <tr2/type_traits>
#include <type_traits>
struct foo {};
struct bar {};
struct derived : foo,bar {};
struct derived2 : foo,bar {};
int main() {
using x = std::tr2::direct_bases<derived>::type;
using y = std::tr2::direct_bases<derived2>::type;
static_assert(std::is_same<x,y>::value);
static_assert(std::is_same<x,std::tr2::__reflection_typelist<foo,bar>>::value);
}
For a portable way you will have to wait until proper reflection is in.
Combining Jarod42's version from a comment with your trait, we can see that it actually works, by constructing xyz
from the tuple, get its bases, put them in a tuple, turn that tuple again to a struct with base classes and see that it is the same type as the original xyz
:
#include <tr2/type_traits>
#include <type_traits>
#include <cstdio>
#include <tuple>
#include <type_traits>
template <typename> struct tuple_to_struct;
template <typename... Ts> struct tuple_to_struct<std::tuple<Ts...>> : Ts... {};
template <typename> struct typelist_to_tuple;
template <typename... Ts> struct typelist_to_tuple<std::tr2::__reflection_typelist<Ts...>> {
using type = std::tuple<Ts...>;
};
template <typename T> using typelist_to_tuple_t = typename typelist_to_tuple<T>::type;
struct x { int i; };
struct y { int j; };
struct z { int k; };
using t = std::tuple<x, y, z>;
using xyz = tuple_to_struct<t>;
int main() {
xyz s{ 1, 2, 3 };
std::printf("%d, %d, %d", s.i, s.j, s.k);
using bases = std::tr2::direct_bases<xyz>::type;
using base_tuple = typelist_to_tuple_t<bases>;
using abc = tuple_to_struct<base_tuple>;
static_assert(std::is_same_v<abc,xyz>);
}