I get a tuple of types (e.g. std::tuple) and I want to create tuple of vectors given by these types. I wrote this, but when I use it as I want, it doesn't work, I get this error:
Error (active) E0730 type "types" is not a class template
I am pretty new to variadic templates so I don't know how to do it properly.
using namespace std;
template <template <typename...> typename Tuple, typename... Types>
class vertices
{
public:
tuple<vector<Types...> > v;
};
int main{
using types = tuple<int, string, double>;
vertices<types> ver;
}
In your code, Tuple is a template template parameter, so it expects a template. types is not a template but a concrete type, so it is not usable. Instead what you can do is just take in the tuple, and then using a helper meta function get the type for the member like
// no definition since it is only used in unevaluated contexts, just used as a helper to get the type converted
template <typename... Types>
std::tuple<std::vector<Types>...> tuple_to_tuple_of_vectors(std::tuple<Types...>);
template <typename Tuple>
class vertices
{
public:
using tuple_type = decltype(tuple_to_tuple_of_vectors(std::declval<Tuple>()));
tuple_type v;
};
int main ()
{
using types = tuple<int, string, double>;
vertices<types> ver;
}
From your question, it appears your requirement is just to be able to conveniently express the type
tuple<vector<int>, vector<string>, vector<double>>
as
vertices<tuple<int, string, double>>
This can be achieved using a variable template. All we need is to take a type (that's a tuple), and unpack that tuple into a vector. Since a type is not a variadic pack, we need another level of indirection to get the types within the tuple. #NathanOliver's answer shows a nice way to do that, using the declaration of a function-template. As pointed out, since all we need is type transformations, the function doesn't need a definition, the declaration says it all: the argument type is the input type, and the return type is the output type.
template <typename... Types>
auto unpack(tuple<Types...>) -> tuple<vector<Types>...> ;
template <typename Tuple>
using vertices = decltype(unpack(declval<Tuple>()));
static_assert(std::is_same<
vertices<tuple<int, string, double>>,
tuple<vector<int>, vector<string>, vector<double>>>{});
Your first template parameter is a template template parameter, but types is not a template, it is a type. Also you have the ... in the wrong place. You need tuple<vector<Types>...>, otherwise Types is expanded as parameters for vector.
The following comes with a disclaimer: I know a bit of tempaltes with C++11, but I am rather ignorant about newer features, so there might be more elegant ways to write the same.
As base template you can use:
template <typename... Types>
struct vertices
{
using tuple_of_vectors_t = tuple<vector<Types>... >;
tuple_of_vectors_t v;
};
And then for tuples:
template <typename... Types>
struct vertices<std::tuple<Types...>> : vertices<Types...> {};
Complete example:
#include <tuple>
#include <vector>
#include <iostream>
#include <type_traits>
using namespace std;
template <typename... Types>
struct vertices
{
using tuple_of_vectors_t = tuple<vector<Types>... >;
tuple_of_vectors_t v;
};
template <typename... Types>
struct vertices<std::tuple<Types...>> : vertices<Types...> {};
int main () {
using types = tuple<int, string, double>;
vertices<types> ver;
std::cout << std::is_same_v< vertices<types>::tuple_of_vectors_t,
std::tuple< std::vector<int>,
std::vector<std::string>,
std::vector<double>
>
>;
}
Output:
1
Related
I have a type list that I've been using but I want to make it cleaner.
My current implementation relies on all the types to be listed when declaring the type list.
template<typename... Types>
struct TypeList {};
using MyCustomTypeList = TypeList<Type1, Type2, Type3>; // Currently listed in one place. This is what I want to avoid.
I would like to build a type list is such a way where I could just write: ADD_TYPE(type) and it would add to MyCustomTypeList.
One of the requirements is that a function call whose signature looks like
template<typename... Types>
void MyFunction(TypeList<Types...>&&);
is still callable with MyFunction( MyCustomTypeList{} );
C++ 17 is available; boost or other libraries aren't.
If you want to update your list like so:
using list1 = pack<int>;
using list2 = push_back_t<list1, char>; // pack<int, char>
using list3 = push_back_t<list2, double>; // pack<int, char, double>
We can write push_back like this:
template <class T>
struct tag
{
using type = T;
};
template <class T>
using detag = typename T::type;
////////////////////////////////
template <class, class>
struct push_back {};
template <template <class...> class C, class ... Ts, class NewT>
struct push_back <C<Ts...>, NewT> : tag<C<Ts..., NewT>> {};
template <class List, class NewT>
using push_back_t = detag<push_back<List, NewT>>;
Demo
Note: I renamed TypeList to pack, as lowercase is standard for type traits.
I'm looking for a way to recursively replace a type in a template. More specifically, turn T[N] into std::array<T, N>. The problem is doing this recursively if non-type template parameters are included.
Currently, I have the following code:
template<typename T>
struct replaced {
using type = T;
};
template<typename T, std::size_t N>
struct replaced<T[N]> {
using type = std::array<typename replaced<T>::type, N>;
};
template<template<typename...> typename T, typename... Ts>
struct replaced<T<Ts...>> {
using type = T<typename replaced<Ts>::type...>;
};
Test cases without non-type template parameters work fine, but as soon as a non-type template parameter is introduced, it no longer works:
// this works (simple replacement)
static_assert(std::is_same_v<replaced<int[3]>::type, std::array<int, 3>>);
// this also works (nested types)
static_assert(std::is_same_v<
replaced<std::tuple<int[2], std::pair<int, float[3]>>>::type,
std::tuple<std::array<int, 2>, std::pair<int, std::array<float, 3>>>>);
// this doesn't work (non-type template parameters)
static_assert(std::is_same_v<
replaced<std::array<int[2], 2>>::type,
std::array<std::array<int, 2>, 2>>);
// instead this works, but shouldn't
static_assert(std::is_same_v<
replaced<std::array<int[2], 2>>::type,
std::array<int[2], 2>>);
Link to compiler explorer
I understand that the variadic template template parameter doesn't work for non-type templates parameters. Is there any way of modifying the definitions of replaced to achieve my goal?
First off, cool idea!
But as much as it sucks, as of C++20 there's no way to handle a generic, variadic mix of type and non-type elements. You just have to start carving out corner cases like with std::array.
This seems to fix that specific problem, but of course it only helps for classes that follow the same pattern as std::array
template <template<typename, auto> class TC, typename T, auto N>
struct replaced<TC<T, N>> {
using type = TC<typename replaced<T>::type, N>;
};
https://godbolt.org/z/3j13W3
Having a list of types as a variadic template argument, it's pretty easy to perform arbitrary type manipulation on them, to get a tuple of modified types as a result. E.g. to wrap each element with a custom wrapper class, one could do:
template<typename T> class Wrapper {};
template<typename ...Values>
using WrappedValues = std::tuple<Wrapper<Values>...>;
using NewTuple = WrappedValues<int, std::string, char>;
static_assert(std::is_same<NewTuple, std::tuple<Wrapper<int>, Wrapper<std::string>, Wrapper<char>>>::value, "");
How to do the same, when having a specialization of std::tuple as an "input"? E.g. what should be placed instead of "???" to make following code compileable:
template<typename T> class Wrapper {};
template<typename Tuple>
using WrappedTupleElements = ???;
using NewTuple = WrappedTupleElements<std::tuple<int, std::string, char>>;
static_assert(std::is_same<NewTuple, std::tuple<Wrapper<int>, Wrapper<std::string>, Wrapper<char>>>::value, "");
I know about the possibility to access types of tuple elements using std::tuple_element and recursive template instantiation, but I don't know how to gather types created this way into one tuple.
Preferred would be pure C++14 answer, but proposals that use C++17, widely available TSes, or external libraries (e.g. boost) are also welcome.
Why not to use additional struct template which allow specialization:
#include <string>
#include <tuple>
#include <type_traits>
template<typename T> class Wrapper {};
template<typename Tuple>
struct WrappedTupleElements;
template <class... Values>
struct WrappedTupleElements<std::tuple<Values...>> {
using type = std::tuple<Wrapper<Values>...>;
};
int main() {
using NewTuple = WrappedTupleElements<std::tuple<int, std::string, char>>::type;
static_assert(std::is_same<NewTuple, std::tuple<Wrapper<int>, Wrapper<std::string>, Wrapper<char>>>::value, "");
}
[live demo]
A common idiom (e.g. boost mpl) is to employ the concept of metafunctions.
A metafunction is a template class which declares a type called result which yields the result type of applying the metafunction on the inputs.
#include <string>
#include <tuple>
#include <type_traits>
template<typename T> class Wrapper {};
namespace metafunction
{
template<class MetaFunction> using result_of = typename MetaFunction::result;
// meta-function which yields Wrapper<Element> from Element
// type: unary metafunction
// arg1 = the type to wrap
// returns Wrapper<arg1>
//
template<class Element>
struct apply_wrapper
{
using result = Wrapper<Element>;
};
template<class Tuple, template<class> class Function>
struct transform_elements;
// meta-function which takes a tuple and a unary metafunction
// and yields a tuple of the result of applying the metafunction
// to each element_type of the tuple.
// type: binary metafunction
// arg1 = the tuple of types to be wrapped
// arg2 = the unary metafunction to apply to each element_type
// returns tuple<result_of<arg2<element>>...> for each element in arg1
template<class...Elements, template<class> class UnaryMetaFunction>
struct transform_elements<std::tuple<Elements...>, UnaryMetaFunction>
{
template<class Arg> using function = UnaryMetaFunction<Arg>;
using result = std::tuple
<
result_of<function<Elements>>...
>;
};
}
int main() {
using input_type = std::tuple<int, std::string, char>;
using namespace metafunction;
using wrapped_tuple = result_of<transform_elements<input_type, apply_wrapper>>;
static_assert(std::is_same<wrapped_tuple, std::tuple<Wrapper<int>, Wrapper<std::string>, Wrapper<char>>>::value, "");
}
I am having a hard time understanding why the following simple program won't compile. I have a variadic template class (my_type below) which I want to use to transform an mpl vector. The following snippet leads to a compilation error "/boost/mpl/aux_/preprocessed/gcc/apply_wrap.hpp:38:19: 'apply' following the 'template' keyword does not refer to a template".
#include <boost/mpl/vector.hpp>
#include <boost/mpl/transform.hpp>
template <class... T>
struct my_type{};
using namespace boost::mpl;
using test_type = vector<int, double>;
// expected result is vector< my_type<int>, my_type<double> >
using result_type = transform< test_type, my_type<_> >::type;
int main() {
}
Making my_type take a single template parameter works fine, but I would like to understand why the variadic version does not work. Thank you in advance!
The second argument expected by transform is a unary operation which you are not passing.
In your case, my_type is not a metafunction that mpl expects since it is making use of a variadic parameter list.
A metafunction in the simplest case exposes a type typedef or a ststic bool value. For eg:
template <typename T>
struct add_pointer {
using type = T*;
};
Note how add_pointer converts the provided template parameter. This is an example of unary operation since it takes only one template parameter T.
In your example my_type is purely a type and cannot be used as a metafunction or operation as it does not satisfy the criteria of the metafunction as required by the transform metafunction which is have a type field to indicate the transformed type.
In some cases, a simple temmplate type is converted into a metafunction as explained in the Detailed Reasoning section below.
Doc reference : http://www.boost.org/doc/libs/1_31_0/libs/mpl/doc/ref/Reference/transform.html
Code:
#include <boost/mpl/vector.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/equal.hpp>
#include <type_traits>
#include <typeindex>
#include <iostream>
template <class... T>
struct my_type{};
using namespace boost::mpl;
using test_type = vector<int, double>;
template <typename T>
struct add_my_type {
using type = my_type<T>;
};
using result_type = typename transform< test_type, add_my_type<_1> >::type;
int main() {
static_assert (equal<result_type, vector< my_type<int>, my_type<double> >>::value, "Nope!!");
std::cout << typeid(result_type).name() << std::endl;
}
LIVE DEMO
Detailed Reason
The reason explained above is pretty brief in should be enough to answer the question. But lets dig into the details as much as I can.
DISCLAIMER: I am no expert in boost::mpl.
As per the comment below by the OP, the original code works if we change my_type to:
template <class T>
struct my_type{};
But that doesn't go well with what I mentioned previously i.e operation needs a type identifier. So, lets see, what mpl is doing under the hood:
struct transform somewhat looks like:
template<
typename Seq1 = mpl::na
, typename Seq2OrOperation = mpl::na
, typename OperationOrInserter = mpl::na
, typename Inserter = mpl::na
>
struct transform {
boost::mpl::eval_if<
boost::mpl::or_<
boost::mpl::is_na<OperationOrInserter>,
boost::mpl::is_lambda_expression<my_type<mpl_::arg<1> > >,
boost::mpl::not_<boost::mpl::is_sequence<my_type<mpl_::arg<1> > > >,
mpl_::bool_<false>,
mpl_::bool_<false>
>,
boost::mpl::transform1<
boost::mpl::vector<int, double>,
my_type<mpl_::arg<1>>,
mpl_::na
>,
boost::mpl::transform2<boost::mpl::vector<int, double>,
my_type<mpl_::arg<1> >,
mpl_::na, mpl_::na>
>
};
The important part to look here is the is_lambda_expression metafunction which basically checks if your Operation meets the requirement of a metafunction.
After applying some heavy macro and template machinery and specializations, the above check synthesises below struct:
template<
typename IsLE, typename Tag
, template< typename P1 > class F
, typename L1
>
struct le_result1
{
typedef F<
typename L1::type
> result_;
typedef result_ type;
};
Here, F is your my_type and L1 is the placeholder . So, in essence the above struct is nothing but add_my_type which I had shown in my initial response.
If I am correct till now, le_result1 is the operation that would be performed on your sequence.
I have a function with a template parameter which I know to be a std::tuple of several standard C++ containers of varying element types.
How can I extract, out of this, a type that is a std::tuple of the element types?
For example, suppose I have the following function
template <typename TupOfCtrs>
void doStuff(const TupOfCtrs& tupOfCtrs) {
using TupOfElements = /*extract a tuple type by applying CtrT::value_type to each container in tupOfCtrs and combining the results into an std::tuple*/;
MyHelperClass<TupOfElements> helper;
}
and I know it is being called like this:
std::list<Foo> l {/*...*/};
std::vector<Bar> v {/*...*/};
std::deque<Baz> d {/*...*/};
auto tup = std::make_tuple(l, v, d);
In this case, I want the TupOfElements helper type to be defined as std::tuple<Foo, Bar, Baz>.
Note that I do not need to actually create the tuple, only to get its type.
How can this be achieved, possibly using the Boost::Fusion library?
You can do this even in a more simple manner without Boost Fusion like this:
// Template which takes one type argument:
template <typename Tuple> struct TupOfValueTypes;
// Only provide a definition for this template for std::tuple arguments:
// (i.e. the domain of this template metafunction is any std::tuple)
template <typename ... Ts>
struct TupOfValueTypes<std::tuple<Ts...> > {
// This definition is only valid, if all types in the tuple have a
// value_type type member, i.e. the metafunction returns a type only
// if all types of the members in the std::tuple have a value_type
// type member, and a std::tuple can be constructed from these:
using type = std::tuple<typename Ts::value_type...>;
};
template <typename TupOfCtrs>
void doStuff(const TupOfCtrs& tupOfCtrs) {
using TupOfElements = typename TupOfValueTypes<TupOfCtrs>::type;
// ...
}
But it is of course easier to specify doStuff for the std::tuple explicitly:
template <typename ... Ts>
void doStuff(const std::tuple<Ts...> & tupOfCtrs) {
using TupOfElements = std::tuple<typename Ts::value_type...>;
// ...
}
PS: Also note, that in many cases if you need to just have a list of types, the std::tuple class is an overkill, and might slightly hurt compilation times. Personally, I've always instead used a simple TypeList struct:
template <typename ... Ts> struct TypeList
{ using type = TypeList<Ts...>; };
If you want doStuff to take a std::tuple, make that explicit:
template <class... Ts>
void doStuff(std::tuple<Ts...> const& tupOfCtr) { ... }
Once you have that parameter pack, it's just a matter of pulling out the value_type:
template <class... Ts>
void doStuff(std::tuple<Ts...> const& tupOfCtr)
{
using value_tuple = std::tuple<typename Ts::value_type...>;
// ...
}