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.
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, "");
}
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.
Say I have a class Foo which uses two different generic types, one is _Type and the other is _Comparator. _Type is known to be a std::vector, std::list, or std::string, so it will have a type within it: T will be within vector and list; char will be within string.
My other generic type _Comparator is an optional template parameter, by which the user may specify her own less than function, functor, or lambda function. If no argument is provided as the second template parameter, it should default to the std::less<M> functor, whereby type M shall be the type of elements contained within _Type.
I do not know the syntax for how to do this.
I've tried:
template <typename _Type<T>, typename _Comparator = less<T> >
to no avail.
Using the approach mentioned by #Joachim Pileborg in the comments, I was able to come up with the following, which allowed me to access the inner type of _Type:
template <typename _Type,
typename _Comparator = less<typename _Type:: value_type> >
class Foo
{
public:
// some methods
private:
_Type sequence;
_Comparator comparator;
};
and now std::less compares the correct types without complaining.
if I understood your question correctly, you might try something like:
#include <iostream>
#include <vector>
#include <list>
#include <typeinfo>
template <typename T>
class holder
{
public:
template <typename Type = std::vector<T>, typename Comparator = std::less<T> >
class impl
{
public:
impl() {std::cout << typeid(s).name() << std::endl; }
Type s;
};
} ;
int main()
{
holder<int>::impl<> holder_of_int_vectors;
holder<int>::impl<std::list<int> > holder_of_int_lists;
holder<int>::impl<std::list<int>, std::greater<int> > holder_of_int_lists_with_greater;
}
ie, use an external class to hold the "basic" type (T) and an internal for the container (Type) and comparator.
As you said you only want to support vector, list, and string, you can use this:
template <typename T, typename Compare = std::less<typename T::value_type>>
This will support all types which have a member typedef value_type, which vector, list, and string all do.
It's possible to support other types using variadic template template parameters, but that's getting much more complicated.
Assume I have a template (called ExampleTemplate) that takes two arguments: a container type (e.g. list, vector) and a contained type (e.g. float, bool, etc). Since containers are in fact templates, this template has a template param. This is what I had to write:
#include <vector>
#include <list>
using namespace std;
template < template <class,class> class C, typename T>
class ExampleTemplate {
C<T,allocator<T> > items;
public:
....
};
main()
{
ExampleTemplate<list,int> a;
ExampleTemplate<vector,float> b;
}
You may ask what is the "allocator" thing about. Well, Initially, I tried the obvious thing...
template < template <class> class C, typename T>
class ExampleTemplate {
C<T> items;
};
...but I unfortunately found out that the default argument of the allocator...
vector<T, Alloc>
list<T, Alloc>
etc
...had to be explicitely "reserved" in the template declaration.
This, as you can see, makes the code uglier, and forces me to reproduce the default values of the template arguments (in this case, the allocator).
Which is BAD.
EDIT: The question is not about the specific problem of containers - it is about "Default values in templates with template arguments", and the above is just an example. Answers depending on the knowledge that STL containers have a "::value_type" are not what I am after. Think of the generic problem: if I need to use a template argument C in a template ExampleTemplate, then in the body of ExampleTemplate, do I have to reproduce the default arguments of C when I use it? If I have to, doesn't that introduce unnecessary repetition and other problems (in this case, where C is an STL container, portability issues - e.g. "allocator" )?
Perhaps you'd prefer this:
#include <vector>
#include <list>
using namespace std;
template <class Container>
class ForExamplePurposes {
typedef typename Container::value_type T;
Container items;
public:
};
int main()
{
ForExamplePurposes< list<int> > a;
ForExamplePurposes< vector<float> > b;
}
This uses "static duck typing". It is also a bit more flexible as it doesn't force the Container type to support STL's Allocator concept.
Perhaps using the type traits idiom can give you a way out:
#include <vector>
#include <list>
using namespace std;
struct MyFunkyContainer
{
typedef int funky_type;
// ... rest of custom container declaration
};
// General case assumes STL-compatible container
template <class Container>
struct ValueTypeOf
{
typedef typename Container::value_type type;
};
// Specialization for MyFunkyContainer
template <>
struct ValueTypeOf<MyFunkyContainer>
{
typedef MyFunkyContainer::funky_type type;
};
template <class Container>
class ForExamplePurposes {
typedef typename ValueTypeOf<Container>::type T;
Container items;
public:
};
int main()
{
ForExamplePurposes< list<int> > a;
ForExamplePurposes< vector<float> > b;
ForExamplePurposes< MyFunkyContainer > c;
}
Someone who wants to use ForExamplePurposes with a non-STL-compliant container would need to specialize the ValueTypeOf traits class.
I would propose to create adapters.
Your class should be created with the exact level of personalization that is required by the class:
template <template <class> C, template T>
class Example
{
typedef T Type;
typedef C<T> Container;
};
EDIT: attempting to provide more is nice, but doomed to fail, look at the various expansions:
std::vector<T>: std::vector<T, std::allocator<T>>
std::stack<T>: std::stack<T, std::deque<T>>
std::set<T>: std::set<T, std::less<T>, std::allocator<T>>
The second is an adapter, and so does not take an allocator, and the third does not have the same arity. You need therefore to put the onus on the user.
If a user wishes to use it with a type that does not respect the expressed arity, then the simplest way for him is to provide (locally) an adapter:
template <typename T>
using Vector = std::vector<T>; // C++0x
Example<Vector, bool> example;
I am wondering about the use of parameter packs (variadic templates) here... I don't know if declaring C as template <class...> C would do the trick or if the compiler would require a variadic class then.
You have to give the full template signature, including default parameters, if you want to be able to use the template template parameter the usual way.
template <typename T, template <class U, class V = allocator<U> > class C>
class ExampleTemplate {
C<T> items;
public:
....
};
If you want to handle other containers that the one from the STL, you can delegate container construction to a helper.
// Other specialization failed. Instantiate a std::vector.
template <typename T, typename C>
struct make_container_
{
typedef std::vector<T> result;
};
// STL containers
template <typename T, template <class U, class V = allocator<U> > class C>
struct make_container_<T,C>
{
typedef C<T> result;
};
// Other specializations
...
template <typename T, typename C>
class ExampleTemplate {
make_container_<T,C>::result items;
public:
....
};
I think, it is required to reproduce all template parameters, even default. Note, that Standard itself does not use template template parameters for containter adaptors, and prefers to use regular template parameters:
template < class T , class Container = deque <T > > class queue { ... };
template < class T , class Container = vector <T>, class Compare = less < typename Container :: value_type > > class priority_queue { ... };
The following code will allow you to do something like you're asking for. Of course, this won't work with standard containers, since this has to already be part of the template class that's being passed into the template.
/* Allows you to create template classes that allow users to specify only some
* of the default parameters, and some not.
*
* Example:
* template <typename A = use_default, typename B = use_default>
* class foo
* {
* typedef use_default_param<A, int> a_type;
* typedef use_default_param<B, double> b_type;
* ...
* };
*
* foo<use_default, bool> x;
* foo<char, use_default> y;
*/
struct use_default;
template<class param, class default_type>
struct default_param
{
typedef param type;
};
template<class default_type>
struct default_param<use_default, default_type>
{
typedef default_type type;
};
But I don't really think this is what you're looking for. What you're doing with the containers is unlikely to be applicable to arbitrary containers as many of them will have the problem you're having with multiple default parameters with non-obvious types as defaults.
As the question exactly described the problem I had in my code (--I'm using Visual Studio 2015), I figured out an alternative solution which I wanted to share.
The idea is the following: instead of passing a template template parameter to the ExampleTemplate class template, one can also pass a normal typename which contains a type DummyType as dummy parameter, say std::vector<DummyType>.
Then, inside the class, one replace this dummy parameter by something reasonable. For replacement of the typethe following helper classes can be used:
// this is simply the replacement for a normal type:
// it takes a type T, and possibly replaces it with ReplaceByType
template<typename T, typename ReplaceWhatType, typename ReplaceByType>
struct replace_type
{
using type = std::conditional_t<std::is_same<T, ReplaceWhatType>::value, ReplaceByType, T>;
};
// this sets up the recursion, such that replacement also happens
// in contained nested types
// example: in "std::vector<T, allocator<T> >", both T's are replaced
template<template<typename ...> class C, typename ... Args, typename ReplaceWhatType, typename ReplaceByType>
struct replace_type<C<Args ...>, ReplaceWhatType, ReplaceByType>
{
using type = C<typename replace_type<Args, ReplaceWhatType, ReplaceByType>::type ...>;
};
// an alias for convenience
template<typename ... Args>
using replace_type_t = typename replace_type<Args ...>::type;
Note the recursive step in replace_type, which takes care that types nested in other classes are replaced as well -- with this, for example, in std::vector<T, allocator<T> >, both T's are replaced and not only the first one. The same goes for more than one nesting hierarchy.
Next, you can use this in your ExampleTemplate-class,
struct DummyType {};
template <typename C, typename T>
struct ExampleTemplate
{
replace_type_t<C, DummyType, T> items;
};
and call it via
int main()
{
ExampleTemplate<std::vector<DummyType>, float> a;
a.items.push_back(1.0);
//a.items.push_back("Hello"); // prints an error message which shows that DummyType is replaced correctly
ExampleTemplate<std::list<DummyType>, float> b;
b.items.push_back(1.0);
//b.items.push_back("Hello"); // prints an error message which shows that DummyType is replaced correctly
ExampleTemplate<std::map<int, DummyType>, float> c;
c.items[0]=1.0;
//c.items[0]="Hello"; // prints an error message which shows that DummyType is replaced correctly
}
DEMO
Beside the not-that-nice syntac, this has the advantage that
It works with any number of default template parameters -- for instance, consider the case with std::map in the example.
There is no need to explicitly specify any default template parameters whatsoever.
It can be easily extended to more dummy parameters (whereas then it probably should not be called by users ...).
By the way: Instead of the dummy type you can also use the std::placeholder's ... just realized that it might be a bit nicer.