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
Related
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>
>);
How can I cascade variadic types? I.e.:
template <typename... T>
using Cascade = ???; // T1<T2<T3<...>>>
Example:
using Vector2D = Cascade<std::vector, std::vector, double>;
static_assert(std::is_same_v<Vector2D, std::vector<std::vector<double>>>);
You cannot have CascadeRight. T1 is not a typename, it is a template, and so are most of the others, but the last one is a typename. You cannot have different parameter kinds (both types and templates) in the same parameter pack. You also cannot have anything after a parameter pack.
You can have CascadeLeft like this:
template <typename K, template <typename...> class ... T>
class CascadeLeft;
template <typename K>
class CascadeLeft<K>
{
using type = K;
};
template <typename K,
template <typename...> class T0,
template <typename...> class... T>
class CascadeLeft<K, T0, T...>
{
using type = typename CascadeLeft<T0<K>, T...>::type;
};
Frankly, std::vector<std::vector<double>> is much more transparent than CascadeLeft<double, std::vector, std::vector>, so I wouldn't bother.
Expanding on the accepted answer with CascadeRight and support for multiple types for the innermost template:
template<template<typename...> typename Head, template<typename...> typename... Tail>
struct CascadeRight {
template<typename... T>
using type = Head<typename CascadeRight<Tail...>::type<T...>>;
};
template<template<typename...> typename Head>
struct CascadeRight<Head> {
template<typename... T>
using type = Head<T...>;
};
template<template<typename...> typename Head, template<typename...> typename... Tail>
struct CascadeLeft {
template<typename... T>
using type = typename CascadeLeft<Tail...>::type<Head<T...>>;
};
template<template<typename...> typename Head>
struct CascadeLeft<Head> {
template<typename... T>
using type = Head<T...>;
};
using T1 = CascadeRight<std::vector, std::map>::type<int, double>;
using T2 = CascadeLeft<std::map, std::vector>::type<int, double>;
I've created a simple template class called tuple_tag which is identical to std::tuple but only acts as a tag.
// tuple_tag
template <typename...> struct tuple_tag {};
// tuple_tag_element
template <size_t I, typename T>
struct tuple_tag_element;
template <size_t I, typename Head, typename... Tail>
struct tuple_tag_element<I, tuple_tag<Head, Tail...>>
: tuple_tag_element<I - 1, tuple_tag<Tail...>> {};
template <typename Head, typename... Tail>
struct tuple_tag_element<0, tuple_tag<Head, Tail...>>
: std::type_identity<Head> {};
// tuple_tag_element_t
template <size_t I, typename T>
using tuple_tag_element_t = tuple_tag_element<I, T>::type;
// tuple_tag_size
template <typename T>
struct tuple_tag_size;
template <typename T> requires (std::is_reference_v<T> || std::is_const_v<T>)
struct tuple_tag_size<T> : tuple_tag_size<std::remove_cvref_t<T>> {};
template <typename... Ts>
struct tuple_tag_size<tuple_tag<Ts...>>
: std::integral_constant<size_t, sizeof...(Ts)> {};
// tuple_tag_size_v
template <typename T>
inline constexpr size_t tuple_tag_size_v = tuple_tag_size<T>::value;
Here:
using new_type_1 = to_tuple_type<tuple_tag<int, double>>::type;
// new_type_1 = std::tuple<int, double>;
using new_type_2 = to_tuple_tag_type<std::tuple<int, double>>::type;
// new_type_2 = tuple_tag<int, double>;
Where to_tuple_type takes a type template parameter tuple_tag<...> which will be converted into type std::tuple<...>, and to_tuple_tag_type takes a type template parameter std::tuple<...> which will be converted into type tuple_tag<...>.
What I am trying to achieve here is to pass all type template parameters from tuple_tag into std::tuple and vice-versa.
This is my prototype for to_tuple_type where it fails:
template <typename TupleTag>
struct to_tuple_type {
using type = std::tuple<...>;
};
Where type alias will be expanded into:
using type = std::tuple<tuple_tag_element_t<Index, TupleTag>...>;
...
using type = std::tuple<
tuple_tag_element_t<0, TupleTag>,
tuple_tag_element_t<1, TupleTag>,
...,
tuple_tag_element_t<N - 1, TupleTag>
>;
Where N is equal to tuple_tag_size_v<TupleTag>.
What I could only think of is to use std::index_sequence but I don't know where do I introduce the pack.
3 steps. First, make a pack
using indexes=std::make_index_sequence<tuple_tag_size<TupleTag>;
then have a helper that expands the pack. I like this one:
template<auto x>
using constant_t=std::integral_constant<decltype(x),x>;
template<auto x>
constexpr constant_t<x> constant={};
template<std::size_t...Is>
constexpr auto all_indexes( std::index_sequence<Is...> ){
return [](auto f){
return f(constant<Is>...);
};
}
now we can
template<class T>
struct tag_t{using type=T;};
template<class T>
constexpr tag_t<T> tag={};
using type=typename decltype(all_indexes(indexes{})([](auto...Is){
return tag<std::tuple<tuple_tag_element_t<Is, TupleTag>...>;
}))::type;
if that has no tpyos.
There is a simple solution that applies partial template specialization:
// to_tuple_type
template <typename Tup>
struct to_tuple_type;
template <typename... Ts>
struct to_tuple_type<tuple_tag<Ts...>> : std::type_identity<std::tuple<Ts...>> {};
// to_tuple_type_t
template <typename Tup>
using to_tuple_type_t = to_tuple_type<Tup>::type;
// to_tuple_tag_type
template <typename Tup>
struct to_tuple_tag_type;
template <typename... Ts>
struct to_tuple_tag_type<std::tuple<Ts...>> : std::type_identity<tuple_tag<Ts...>> {};
// to_tuple_tag_type_t
template <typename Tup>
using to_tuple_tag_type_t = to_tuple_tag_type<Tup>::type;
I have a function:
template <class T, class Array>
void DumpArrayDebug(Array& out, const T& source)
It is supposed to be used to dump data from array classes in Maya (Autodesk app) into regular vector.
Example of such array types:
MFloatArray;
MColorArray;
MIntArray;
These classes have the same interface, yet they have no base class.
Currently I use this function in a following way:
MFloatArray someInternalMayaFloatData;
...
std::vector<float> buffer;
DumpArrayDebug(buffer, someInternalMayaFloatData);
Looking at this code makes me wonder is it possible to somehow tie 2 classes inside the template through something like lookup table?
So that result should look something like this:
template <class T>
void dumpArrayDbg(const T& source, ClassLookupTable<T>& out)
Thus far I was able to come up with the following monstrosity:
template <typename T>
struct ClassLookupTable
{
T classname;
};
template <>
struct ClassLookupTable<MIntArray>
{
std::vector<int> classname;
};
template <>
struct ClassLookupTable<MFloatArray>
{
std::vector<float> classname;
};
template <>
struct ClassLookupTable<MColorArray>
{
std::vector<MColor> classname;
};
template <class T>
void dumpArrayDbg(const T& source, decltype(ClassLookupTable<T>::classname)& out)
{
int length = source.length();
out.clear();
out.resize(length);
source.get(out.data());
}
Is there a more elegant solution?
This is a standard template metaprogramming technique: a traits type. The only things I would change are using standard template metaprogramming idioms (type is the standard name for a type trait, not classname) and avoid having the trait specify vector:
template <typename T>
struct MayaArrayBaseElementTrait; //The primary template should never be used.
template <>
struct MayaArrayBaseElementTrait<MIntArray>
{
using type = int;
};
template <>
struct MayaArrayBaseElementTrait<MFloatArray>
{
using type = float;
};
template <>
struct MayaArrayBaseElementTrait<MColorArray>
{
using type = MColor;
};
template<typename T>
using MayaArrayBaseElementTrait_t = typename MayaArrayBaseElementTrait<T>::type;
template <class T>
void dumpArrayDbg(const T& source, std::vector<MayaArrayBaseElementTrait_t<T>>& out)
{
int length = source.length();
out.clear();
out.resize(length);
source.get(out.data());
}
This way, the mapping is from the Maya array type to the base element type. This gives you the freedom to create mappings to other types besides vector. You could create a std::array or std::list or whatever you like. Also, if you ever want to change the allocator type for the vector, you are free to do so, unlike your original code.
Is there a more elegant solution?
I propose the following...
Given a simple template type list container
template <typename ...>
struct typeList
{ };
and a recursive template as follows
template <typename, typename>
struct lookForType
{ };
template <typename T, typename V, typename ... Ts>
struct lookForType<T, typeList<T, V, Ts...>>
{ using type = V; };
template <typename T, typename U, typename V, typename ... Ts>
struct lookForType<T, typeList<U, V, Ts...>>
: lookForType<T, typeList<Ts...>>
{ };
with an helper using to simplify the extraction of type
template <typename T, typename L>
using lookForType_t = typename lookForType<T, L>::type;
you can create the mapping as follows
using myList = typeList<MIntArray, std::vector<int>,
MFloatArray, std::vector<float>,
MColorArray, std::vector<Color>>;
and get the required type using lookForType or lookForType_t
using l1 = lookForType_t<MIntArray, myList>;
The following is a full compiling example
#include <vector>
#include <type_traits>
template <typename ...>
struct typeList
{ };
template <typename, typename>
struct lookForType
{ };
template <typename T, typename V, typename ... Ts>
struct lookForType<T, typeList<T, V, Ts...>>
{ using type = V; };
template <typename T, typename U, typename V, typename ... Ts>
struct lookForType<T, typeList<U, V, Ts...>>
: lookForType<T, typeList<Ts...>>
{ };
template <typename T, typename L>
using lookForType_t = typename lookForType<T, L>::type;
struct Color {};
struct MFloatArray {};
struct MColorArray {};
struct MIntArray {};
int main()
{
using myList = typeList<MIntArray, std::vector<int>,
MFloatArray, std::vector<float>,
MColorArray, std::vector<Color>>;
using l1 = lookForType_t<MIntArray, myList>;
using l2 = lookForType_t<MFloatArray, myList>;
using l3 = lookForType_t<MColorArray, myList>;
static_assert( std::is_same_v<std::vector<int>, l1>, "!" );
static_assert( std::is_same_v<std::vector<float>, l2>, "!" );
static_assert( std::is_same_v<std::vector<Color>, l3>, "!" );
}
Using the following metafunction front to get the first type of a typelist I try to write a similar metafunction to extract the first template of a list of templates.
namescpace detail {
template<typename L> struct front_impl;
template<template<typename, typename...> typename L, typename F, typename... I>
struct front_impl<L<F, I...>> {
typedef F type;
};
}
template<typename L> struct front_impl;
template<template<typename, typename...> typename L, typename F, typename... I>
struct front_impl<L<F, I...>> {
typedef F type;
};
One can use this as follows:
template<typename... T>
struct List {
inline static constexpr size_t size = sizeof...(T);
};
using l1 = List<A, B, C>;
using f1 = front<l1>;
Now I try to do the same with a list of templates. Therefore I use a list of templates TList:
template<template<typename> typename... TT>
struct TList {
inline static constexpr size_t size = sizeof...(TT);
};
and the metafunction tfront:
template<typename T> struct tfront;
template<template<typename> typename F, template<typename> typename... R>
struct tfront<TList<F, R...>> {
template<typename T> using type = F<T>;
};
Then I can extract the first of a list of templates A, B, ... (not shown here):
using tlist = TList<A, B, C>;
template<typename T>
using f = typename tfront<tlist>::template type<T>;
f<int> xx;
Then xx ist of type A<int>.
The Question is: can I write the metafunction tfront in the same way as front, that is not as a partial specialization for the list of templates TList but for every variadic template of templates? So I would like to introduce a parameter L for tfront as a template-template-parameter with a variadic list of templates, so that the compiler must also deduce the type of the type L as in the case of front.
I would like to write something like (but what to use as ???):
template<template<typename> typename F, ??? TL, template<typename> typename... R>
struct tfront<TL<F, R...>> {
template<typename T> using type = F<T>;
};
You need an extra layer of template<typename> typename...:
template<typename>
struct tfront;
template<template<typename> typename F,
template<template<typename> typename...> typename TL,
template<typename> typename... R>
struct tfront<TL<F, R...>>
{
};
live example on wandbox