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.
Related
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
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, "");
}
Suppose I have an boost::mpl::vector "myvec", defined for example like this:
using myvec = boost::mpl::vector<int, double, double>;
Now I want to define another type, myvecex, that transforms each myvec member into std::tuple with added string. I want to get a type defined like this:
using myvecex = boost::mpl::vector<std::tuple<int, std::string>,
std::tuple<double, std::string>,
std::tuple<double, std::string> >;
But I don't want to repeat myself and name all the vector members. Instead I want to define some_smart_template template type where I will somehow put the logic of converting each member type into a tuple.
using myvecex2 = some_smart_template<myvec>;
static_assert(std::is_same<myvecex, myvecex2>::value);
Is it doable in C++ at all?
Boost.MPL doesn't just give you containers, it gives you algorithms over those containers too. In this case, what you want is transform:
template<
typename Sequence
, typename Op
, typename In = unspecified
>
struct transform
{
typedef unspecified type;
};
The semantics are you give it a sequence and what MPL refers to as a Lambda Expression and you get out another sequence. Specifically:
using B = mpl::transform<A,
std::tuple<mpl::_1, std::string>
>::type;
Or at least that would work if apply supported variadic class templates like std::tuple. So you'll need to just write an operation that's either a metafunction class:
struct tuple_of_strings {
template <class T>
struct apply {
using type = std::tuple<T, std::string>;
};
};
using B = mpl::transform<A, tuple_of_strings>::type;
or a metafunction:
template <class T>
struct tuple_of_strings {
using type = std::tuple<T, std::string>;
};
using B = mpl::transform<A, tuple_of_strings<_1>>::type;
If I have a variant, like so:
using my_variant = boost::variant<int, bool, std::string>;
Is there an easy way to extract the types the variant can contain into a Boost.Hana tuple so that the following holds:
using boost::hana::type;
static_assert(std::is_same<my_tuple, boost::hana::tuple<type<int>, type<bool>, type<std::string>>>{});
The following will work on develop (since e13d826):
#include <boost/hana.hpp>
#include <boost/hana/ext/boost/mpl.hpp>
#include <boost/variant.hpp>
#include <string>
namespace hana = boost::hana;
using my_variant = boost::variant<int, bool, std::string>;
constexpr auto my_tuple = hana::to<hana::tuple_tag>(my_variant::types{});
// Note:
// In general, don't use std::is_same to compare hana::tuple; use == in
// because it will also work if the tuple contains hana::basic_types.
static_assert(my_tuple == hana::tuple_t<int, bool, std::string>, "");
What e13d826 did was add support for mpl::list; only mpl::vector was supported before, and boost::variant<>::types is a mpl::list. This is why my answer took a while to come; I was implementing that :-).
Edit
I did not explain why I'm using constexpr auto my_tuple = ... instead of using my_tuple = decltype(...). Well, the reason is simply because knowing the type of my_tuple is not really useful, since you can't rely on it. Indeed, if you look at the documentation of hana::type, it's written that you can't rely on hana::type<T> being anything specific. There are good reasons for this, but from a usability point of view it means that you can't rely on the type of hana::tuple<hana::type<...>, ...> being anything specific either. When doing type-level computations, prefer value-level encoding (thus auto my_tuple = ...) to type-level encoding. This is the specificity of Hana over MPL & friends, and you should try to stick with it as much as possible, or you'll find Hana to be really clunky (because not written with that in mind).
This doesn't use any hana features, but should work.
First, a transcribe type function that takes a template, and an instance of a different template, and transcribes the types in the second into the first:
template<template<class...>class To, class From>
struct transcribe;
template<template<class...>class To, class From>
using transcribe_t=typename transcribe<To,From>::type;
template<template<class...>class Z, template<class...>class Src, class...Ts>
struct transcribe<Z, Src<Ts...>> {
using type=Z<Ts...>;
};
Now, a template that takes types, and returns a hana tuple of hana types:
template<class...Ts>
using tuple_of_types = boost::hana::tuple<boost::hana::type<Ts>...>;
And we are done:
template<class Src>
using get_types_from = transcribe_t< tuple_of_types, Src >;
using result = get_types_from< my_variant >;
get_types_from is because a type function that extracts the template arguments of an arbitrary template seems useful.
Some time back I had come across something similar to this for such conversion. Not able to find the actual source of this idea though or it may be even a pretty common practice:
template<class From, template<class...> class To> struct convert_impl;
template<template<class...> class From, class... Types,
template<class...> class To>
struct convert_impl<From<Types...>, To>
{
using converted_type = To<Types...>;
};
template<class From, template<class...> class To>
using convert = typename convert_impl<From, To>::converted_type;
Since, I am not sure about boost hana tuple, I will show an example with std::tuple
convert<boost::variant<int, bool, string>, std::tuple>
If I use boost::mpl, lets look at the following code:
typedef fold<
vector<long,float,long>
, set0<>
, insert<_1,_2>
>::type s;
BOOST_MPL_ASSERT_RELATION( size<s>::value, ==, 2 );
How can I turn s into a type t = boost::mpl::set<long,float> again, such that I can use t for selecting a partially specialized template function (on boost::mpl::set``) which extracts the element types and turns it into a std::tuple<long,float> . It is something else
Here's a full example. I called the metafunction Unify but obviously you can call it whatever you want.
How it works is quite simple, it simply removes elements from the input sequence one at a time and builds up a variadic list and dumps them into the desired sequence type at the end.
#include <boost/mpl/set.hpp>
#include <boost/mpl/front.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/insert.hpp>
#include <boost/mpl/erase_key.hpp>
#include <tuple>
template <template <class...> class OutSeqType,
class Sequence,
std::size_t nSeqSize,
class ... Elements>
struct Unify
{
typedef typename boost::mpl::front<Sequence>::type Next;
typedef typename Unify<
OutSeqType,
typename boost::mpl::erase_key<Sequence, Next>::type,
nSeqSize - 1, Next, Elements...>::type type;
};
template <template <class...> class OutSeqType,
class Sequence,
class ... Elements>
struct Unify<OutSeqType, Sequence, 0ul, Elements...>
{
typedef OutSeqType<Elements...> type;
};
int main()
{
typedef boost::mpl::insert<
boost::mpl::insert<
boost::mpl::insert<
boost::mpl::set<>,
int>::type,
float>::type,
int*>::type Set;
typedef Unify<
std::tuple,
Set,
boost::mpl::size<Set>::type::value
>::type Set2;
//This compile error will print the type of Set2
Set2::asdfl;
}
For some reason I'm not sure about I couldn't use pop_front on a set so I used erase_key instead. This restricts it to associative containers, but it could be generalized to any type of container with a little more work. I'll leave that as an exercise.