Is there a way to convert each type of std::tuple into specific subtypes?
I have following code
struct Foo1
{
struct A{};
struct B{};
};
struct Foo2
{
struct A{};
struct B{};
};
using myTypes = std::tuple<Foo1, Foo2>;
Is there a way to convert myTypes into following type?
std::tuple<Foo1::A, Foo1::B, Foo2::A, Foo2::B>;
Order of types doesn't matter, but would be nice to have it like above.
If A/B name are fixed, you might do
template <typename... Ts>
using AB_Types = std::tuple<typename Ts::A..., typename Ts::B...>;
So AB_Types<Foo1, Foo2> is std::tuple<Foo1::A, Foo2::A, Foo1::B, Foo2::B>.
Having expected order would also be possible:
template <typename... Ts>
using AB_Types_ordered =
decltype(std::tuple_cat(std::tuple<typename Ts::A, typename Ts::B>{}...));
and if source is a tuple, just add extra layer
template <typename>
struct AB_impl;
template <typename... Ts>
struct AB_impl<std::tuple<Ts...>>
{
using type = AB_Types<Ts...>; // AB_Types_ordered<Ts...>
};
template <typename T>
using AB_Types_from_tuple = typename AB_impl<T>::type;
An alternative solution based on the Boost.Mp11 library:
template<class T>
using add_AB = std::tuple<typename T::A, typename T::B>;
template <typename Tuple>
using AB_Types_from_tuple =
boost::mp11::mp_flatten<boost::mp11::mp_transform<add_AB, Tuple>>;
static_assert(std::is_same_v<
AB_Types_from_tuple<myTypes>,
std::tuple<Foo1::A, Foo1::B, Foo2::A, Foo2::B>
>);
Related
I've got a typelist providing the following interface :
template <typename... Ts>
struct type_list
{
static constexpr size_t length = sizeof...(Ts);
template <typename T>
using push_front = type_list<T, Ts...>;
template <typename T>
using push_back = type_list<Ts..., T>;
// hidden implementation of complex "methods"
template <uint64_t index>
using at;
struct pop_front;
template <typename U>
using concat;
template <uint64_t index>
struct split;
template <uint64_t index, typename T>
using insert;
template <uint64_t index>
using remove;
};
In another piece of code, I have such a typelist TL of types statically inheriting a base class providing such an interface :
template<typename Derived>
struct Expression {
using type1 = typename Derived::_type1;
using type2 = typename Derived::_type2;
};
struct Exp1 : Expression<Exp1> {
template<typename> friend struct Expression;
private:
using _type1 = float;
using _type2 = int;
};
struct Exp2 : Expression<Exp2> {
template<typename> friend struct Expression;
private:
using _type1 = double;
using _type2 = short;
};
I want to make the typelist of nested types from TL, something like :
using TL = type_list<Exp1, Exp2>;
using TL2 = type_list<TL::type1...>; // type_list<float, double>
but I can't expand TL as it's not an unexpanded parameter pack.
I've thought about index_sequence but can't manage to make it work.
The question is seemingly looking for map, also called transform in C++. TL is one list of types, and the desire is to apply some type-level function (extract ::type1) and have another list of types. Writing transform is straightforward:
template <template <typename> typename fn, typename TL>
struct type_list_transform_impl;
template <template <typename> typename fn, typename... Ts>
struct type_list_transform_impl<fn, type_list<Ts...>>
{
using type = type_list<fn<Ts>...>;
};
template <template <typename> typename fn, typename TL>
using type_list_transform = type_list_transform_impl<fn, TL>::type;
And then the type-level function:
template <typename T>
using type1_of = typename T::type1;
And combine the pieces to get TL2:
using TL2 = type_list_transform<type1_of, TL>; // type_list<float, double>
Example: https://godbolt.org/z/b7TMoac5c
I want to have something like:
template <typename... Ts>
using make_variant_t = /* ??? */;
such that, for instance, make_variant_t<Foo, Bar> evaluates as the type
std::variant<Foo, std::vector<Foo>, Bar, std::vector<Bar>>
in that order. How, if it is possible, can this be achieved?
With Boost.Mp11 this is a short, one-liner (as always):
template <typename... Ts>
using make_variant_t = mp_append<variant<Ts, vector<Ts>>...>;
make_variant_t<int, char> will first produce two variants, variant<int, vector<int>> and variant<char, vector<char>>. Those each are "lists" in the Mp11 sense, and mp_append takes a bunch of lists and concatenates them together, producing variant<int, vector<int>, char, vector<char>> as desired. Demo.
Here you go:
namespace impl
{
template <typename R, typename ...P>
struct make_variant {};
template <typename ...R, typename P0, typename ...P>
struct make_variant<std::variant<R...>, P0, P...>
{
using type = typename make_variant<std::variant<R..., P0, std::vector<P0>>, P...>::type;
};
template <typename ...R>
struct make_variant<std::variant<R...>>
{
using type = std::variant<R...>;
};
}
template <typename ...P>
using make_variant_t = typename impl::make_variant<std::variant<>, P...>::type;
Now, make_variant_t<Foo, Bar> should expand to std::variant<Foo, std::vector<Foo>, Bar, std::vector<Bar>>.
Alternative solution with std::tuple under the hood:
namespace impl {
template<class> struct tuple_to_variant;
template<class... Ts>
struct tuple_to_variant<std::tuple<Ts...>> {
using type = std::variant<Ts...>;
};
}
template<class... Ts>
using make_variant_t = typename impl::tuple_to_variant<decltype(
std::tuple_cat(std::declval<std::tuple<Ts, std::vector<Ts>>>()...))>::type;
I know that the following code compile:
template<class Type>
class Foo
{
using type = Type;
};
now, I'm trying to compile the following code:
template<class Type, class... OtherTypes>
class Foo
{
using type = Type;
// using types = OtherTypes;
// using... types = OtherTypes;
// using types... = OtherTypes;
// using types = OtherTypes...;
// using types... = OtherTypes...;
};
I tried all of the options of the code in comments, but none of them compile.
How can I fix it?
You cannot have a pack of types as a type in a class.
The closest you can get is roughly:
template<class...Ts> struct types_t { constexpr types_t(){}; };
template<class...Ts> constexpr types_t<Ts...> types{};
these are values and types that represent a package of types.
template<class Type, class... OtherTypes>
class Foo
{
using type=Type;
using types=types_t<OtherTypes...>;
};
then we can write helper functions that consume bundled-up types and use them elsewhere.
template<template<class...>class Z, class Types>
struct apply_types;
template<template<class...>class Z, class...Ts>
struct apply_types<Z, types_t<Ts...>> {
using type=Z<Ts...>;
};
template<template<class...>class Z, class Types>
using apply_types_t = typename apply_types<Z,Types>::type;
now apply_types< some_template, some_types_t > takes the types in the bundle and passes them to the template.
Let's assume that you want to use the pack as template argument. Then you can try the following approach.
#include <utility>
template <class... Types>
struct Foo {};
template <template <class...> class Template,
class... Types,
template <class...> class T>
Template<Types...> foo(const T<Types...> &);
template <template <class...> class Template, class T>
using Type = decltype(foo<Template>(std::declval<T>()));
int main() {
using T = Foo<int, int>;
// As template argument
using Tuple = Type<std::tuple, T>;
static_assert(std::is_same<Tuple, std::tuple<int, int> >::value, "");
return 0;
}
For a single function it is possible to extract its parameter types like this:
template <class T>
struct Foo;
template <class Ret, class... Args>
struct Foo<Ret(*)(Args...)> { /* stuff */ };
Would it be possible to do the same for a series of function pointers? That is,
to be able to extract the arguments and then redeploy them in the same way? E.g. something like:
template <class.. T>
struct Foo;
template <class... Rets, class... Args>
struct Foo<Rets(*)(Args...)...> // I wish this worked
{
std::tuple<Rets(*)(Args...)...> fns; // Ditto
}
Late to the party, but this might be useful. First let's define some utilities:
template <class... T>
using void_t = void;
template <bool...> struct bool_pack;
template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;
Then a metafunction to check whether a type is a function pointer:
template <class F>
struct is_function_pointer : std::integral_constant<bool, false> { };
template <class Ret, class... Args>
struct is_function_pointer<Ret (*)(Args...)> : std::integral_constant<bool, true> { };
Using these, let's write the class. The SFINAE toggle is placed first because the variadic pack takes up the right side:
template <class = void_t<>, class... T>
struct Foo_impl;
template <class... FuncPtrs>
struct Foo_impl<
std::enable_if_t<all_true<is_function_pointer<FuncPtrs>{}...>{}>,
FuncPtrs...
> {
std::tuple<FuncPtrs...> fns;
};
Finally, a typedef to hide the SFINAE toggle from the user:
template <class... T>
using Foo = Foo_impl<void_t<>, T...>;
VoilĂ ! Foo<void (*)(), int (*)(double)> goes to the specialization of Foo_impl which contains the adequate tuple, Foo<int, double> goes to the main template.
See it live on Coliru
I have defined the class
template <typename... Ts> struct Bar {
using inner_type = /* whatever */;
};
Now, I need to define a templated class Foo whose template parameters are some parameter pack, and a value of type Bar::inner_type instantiated for that parameter pack. Unfortunately I can't seem to be able to do it. If I define it this way:
template <Bar<Ts...>::inner_type SomeValue, typename... Ts> struct Foo { };
the compiler doesn't recognize Ts when it's used, since it hasn't see the parameter pack yet; but if I define it this way:
template <typename... Ts, Bar<Ts...>::inner_type SomeValue> struct Foo { };
the compiler sneers at my attempt to use a parameter pack before other template parameters.
So how can I do this?
Note: In case it matters, this failed for me with GCC 4.9.3.
You can partially specialize your struct:
template<typename...>
struct Bar { using inner_type = int; };
template <typename T, typename T::inner_type T>
struct Foo;
template <typename... Ts, typename Bar<Ts...>::inner_type SomeValue>
struct Foo<Bar<Ts...>, SomeValue> { };
int main() {
Foo<Bar<int>, 3> foo;
}
This way Ts parameter pack is deduced and Foo expects the second template parameter to be of type Bar<Ts...>::inner_type.
The best thing I could come up with:
template <class Inner, Inner Val, class... Args>
struct Foo {
static_assert(std::is_same<Inner, typename Bar<Args...>::inner_type>::value, "Wrong type");
};
You need to explicitly name the type.
does this solve the issue?
#include <type_traits>
using namespace std;
template <typename... Ts> class Bar {
public:
using inner_type = int;
};
template <typename... Ts> class Foo {
using bar_inner_type = typename Bar<Ts...>::inner_type;
static_assert(is_same<int, bar_inner_type>::value,"");
};
If I understood your problem correctly, you can do something like this:
template <typename... Ts> struct Bar {
using inner_type = /* whatever */;
};
template <typename... Ts> struct Foo {
using inner_type = typename Bar<Ts...>::inner_type;
};