Get variadic template variadic template parameter variadic parameters - c++

Yes.
Let's say I have a simple variadic struct that holds a typedef:
template<typename... TArgs> struct TupleTypeHolder {
using TupleType = std::tuple<TArgs*...>;
};
I want to pass TupleTypeHolder<something> as a template parameter to another class, and get that typedef.
All of my tries do not compile.
// None of these is valid
template<template<typename...> class TTupleTypeHolder> struct TupleMaker {
using MyTupleType = TTupleTypeHolder::TupleType; // Not valid
using MyTupleType = typename TTupleTypeHolder::TupleType; // Not valid
};
template<template<typename... A> class TTupleTypeHolder> struct TupleMaker2 {
// A is not a valid name here
using MyTupleType = TTupleTypeHolder<A...>::TupleType; // Not valid
using MyTupleType = typename TTupleTypeHolder<A...>::TupleType; // Not valid
};
Is there a way to use the variadic template parameters (in this case, TupleTypeHolder's TArgs...) of a variadic template class from a class that uses the aforementioned class as a template variadic template parameter?
Usage example:
template<typename... TArgs> struct TupleTypeHolder {
using TupleType = std::tuple<TArgs*...>;
};
template<typename... TArgs> static int getSomeValue() { ... }
template<??? T1, ??? T2> class TupleMaker
{
std::pair<int, int> someValues;
using TupleType1 = T1::TupleType;
using TupleType2 = T2::TupleType;
TupleMaker() : someValues{getSomeValue<T1's TArgs...>(),
getSomeValue<T2's TArgs...>()} { }
};
class MyTupleMaker : TupleMaker<TupleTypeHolder<int, char>,
TupleTypeHolder<int, float>>
{ };
MyTupleMaker::TupleType1 tuple1{new int(1), new char('a')};
MyTupleMaker::TupleType2 tuple1{new int(35), new float(12.f)};

Working usage example:
#include <tuple>
template<typename... TArgs> struct TupleTypeHolder {
using TupleType = std::tuple<TArgs*...>;
};
template<typename... TArgs> static int getSomeValue() { return 42; }
// primary template:
template<class T1, class T2>
struct TupleMaker;
// partial specialization:
template<template<class...> class TT1, template<class...> class TT2,
class... T1, class... T2>
struct TupleMaker < TT1<T1...>, TT2<T2...> >
{
std::pair<int, int> someValues;
using TupleType1 = typename TT1<T1...>::TupleType;
using TupleType2 = typename TT2<T2...>::TupleType;
TupleMaker() : someValues{getSomeValue<T1...>(),
getSomeValue<T2...>()} { }
};
struct MyTupleMaker : TupleMaker<TupleTypeHolder<int, char>,
TupleTypeHolder<int, float>>
{ };
MyTupleMaker::TupleType1 tuple1{new int(1), new char('a')};
MyTupleMaker::TupleType2 tuple2{new int(35), new float(12.f)};
int main() {}
The primary template takes two types, as you're passing types. TupleTypeHolder<int, char> is a type, a specialization of a template, not a template itself. Template template-parameters however take templates as arguments (not types), such as:
template<template<class...> class Foo>
struct Bar
{
using type = Foo<int, double, char>;
};
Bar< std::tuple > b; // note: no template arguments for `std::tuple`!
With partial specialization, you can split a template specialization into the template and the parameters, that's how the above works.

Template-template parameters are not really type parameters, are parameters to specify a template. That means what you pass through a template-template parameter is not a type, is a template:
template<template<typename> class TPARAM>
struct give_me_a_template
{
using param = TPARAM; //Error TPARAM is not a type, is a template.
using param_bool = TPARAM<bool>; //OK, thats a type
};
As you can see, the first alias is invalid, because TPARAM is not a type, is a template. But the second is a type (Is an instance of the template).
That said, examine your problem: What you called TupleTypeHolder could be viewed as a variadic-template typelist. So your goal is to make tuples of the types specified with typelists, right?
You can use partial specialization to extract the content of a typelist:
template<typename TupleTypeHolder>
struct tuple_maker;
template<typename... Ts>
struct tuple_maker<TupleTypeHolder<Ts...>>
{
using tuple_type = std::tuple<Ts...>;
};
An example of its usage could be:
using my_types = TupleTypeHolder<int,int,int>;
using my_tuple_type = typename tuple_maker<my_types>::tuple_type;
Of course this is not the exactly solution to your implementation, you need to extend the concept to multiple typelists (As your question showed). What I have provided is the guide to understand the problem and its solution.

Related

Explicitly specify template template types

I'm learning templates. If I mix up the concepts template / template-type / template-argument, please correct me.
I'm trying to write a template function that creates an object and returns it. The type of the object comes from the template argument that has to be explicitly specified.
result = createObject<ObjectType>();
This object though is supposed to be a template. A container for example. And the function is supposed to know the type of the object and its template arguments. Ex:
result = createObject<Container<ElementType>>();
I've tried to solve it with template template parameter:
template <template<class> class ContainerType, class ElementType>
auto createObject()
{
ContainerType<ElementType> result;
//do stuff...
return result;
}
//...
template<typename T>
struct Vector{};
//...
//const auto random_vec = createObject<Vector<float>>(); // ERROR.
const auto random_vec = createObject<Vector, float>();
The second case works, the first doesn't. It says candidate template ignored: invalid explicitly-specified argument for template parameter 'ContainerType'.
Is it possible to make it work like the first case? Give it something like Vector<float> and it can deduce the ContainerType to Vector and ElementType to float? Is it possible to overload or specialize this function so that it handles certain types of containers differently? Should I use concepts?
The usual way to do decomposition like this is via partial specialization, which requires a helper class template:
namespace detail {
template<class> struct create; // undefined
template<template<class T> class C,class T>
struct create<C<T>> {
static C<T> make() {/* … */}
};
}
template<class T>
T createObject() {return detail::create<T>::make();}
The primary template can be defined if you want to support the general case, and other specializations may be added for other kinds of templates like std::array.
You could create a type trait to check if the type is instantiated from a template:
#include <type_traits>
// trait to check if the type is instantiated from a template
template<typename T>
struct is_template_instance_type : std::false_type {};
template<template<class,class...> class C, class T, class... Rest>
struct is_template_instance_type<C<T,Rest...>> : std::true_type {
using class_type = C<T,Rest...>;
using value_type = T;
// using rest_types = std::tuple<Rest...>;
};
// Helper variable template - if needed for something later
template<class T>
inline constexpr bool is_template_instance_type_v = is_template_instance_type<T>::value;
You could then add overloads:
template<class T, class C = is_template_instance_type<T>, class U = typename C::class_type>
auto createObject() {
U result;
// typename C::value_type x; // if you need the value type
return result;
}
template<template<class,class...> class C, class T, class... Rest>
auto createObject() {
return createObject< C<T,Rest...> >();
}
And it would then work with Vector<float>, Vector, float but not float for example.
Demo
You can simply go like this:
template<typename T, typename V = typename T::value_type>
T createObject()
{
T t {}; // T will be e.g std::vector<int>
V v {}; // V will be int
// do work...
t.push_back(v++);
t.push_back(v++);
// ...work done
return t;
}
Than you can use it like this:
int main ()
{
auto obj1 = createObject<std::vector<int>>();
auto obj2 = createObject<std::list<double>>();
return 0;
}

Is there any way to store the template parameter packs and reuse it later?

I have very long parameter pack. I wonder is there any way to store the parameter pack and reuse it later. For example, if there are 2 templates:
template<class ... Types> struct A {};
template<class ... Types> struct B {};
I have a specialized type:
typedef A<int, int, float> A1_t;
Is there any operation can let me create a specialized type B which use the same parameter pack as A1_t? (B<int, int, float>). Is there any method to retrieve the <int, int, float> from A1_t or store it?
I want obtain a specialized type B1_t instead of creating the object of B1_t.
A and B describes completely different concept, so I cannot make B nested inside A.
moreover, I would also like to feed the parameter packs to specialize function templates.
template<class ...Ts>
C<Ts...> func1()
{
}
so I can directly call func1<int, int, float>()
It will be nice if I can do something like this:
template<typename T>
transform<B, T> func1()
{
}
next step would be something similar to this:
template<template<class...Ts> templ>
B<Ts...> func2(Ts ...args)
{
}
So I can do func2<A1_t>(1, 2, 3.0f) directly.
Something like this? Using a type transformation based on partial specialization:
#include<type_traits>
template<template<typename...> class, typename>
struct with_tmpl_args_of;
template<template<typename...> class OtherTmpl, template<typename...> class Tmpl, typename... Args>
struct with_tmpl_args_of<OtherTmpl, Tmpl<Args...>> {
using type = OtherTmpl<Args...>;
};
template<template<typename...> class OtherTmpl, typename T>
using with_tmpl_args_of_t = typename with_tmpl_args_of<OtherTmpl, T>::type;
// example
template<class ... Types> struct A {};
template<class ... Types> struct B {};
using A1_t = A<int, int, float>;
using B1_t = with_tmpl_args_of_t<B, A1_t>;
// test
static_assert(std::is_same_v<B1_t, B<int, int, float>>);
This is limited to class templates that do not use non-type template arguments. There is currently unfortunately no way to define template template parameters which accept both type and non-type template parameters in the same template template parameter's parameter.
Also beware of default arguments. This will not use OtherTmpl's default arguments, if one of Tmpl's default arguments matches that position in the template list and will fail if Tmpl's template list (including defaulted arguments) is larger than OtherTmpls.
Regarding the additional examples in your edit:
The second example works directly with the type transform I defined above:
template<typename T>
with_tmpl_args_of_t<B, T> func1()
{
}
The third one can be done like this:
template<typename A, typename... Ts>
with_tmpl_args_of_t<B, A> func2(Ts... args)
{
}
It guarantees that the return type has the same template arguments as A1_t, but it does accept all types as arguments, even if they don't match the types in the template arguments of A1_t. This should not usually be a problem. If the types are not convertible to the correct ones you will get an error at the point where you try the conversion.
If you must take the exact same types as in the template arguments of A1_t for function parameters, you can do something like (untested):
template<typename T>
struct func_with_tmpl_args_of;
template<template<typename...> class Tmpl, typename... Args>
struct func_with_tmpl_args_of<Tmpl<Args...>> {
template<typename F>
struct inner {
constexpr inner(F f) : f(std::move(f)) {}
constexpr static decltype(auto) operator()(Args... args) const {
return f(std::forward<Args>(args)...);
}
private:
F f;
};
};
// example
template<typename T>
constexpr inline auto func2 = func_with_tmpl_args_of<T>::inner{[](auto... args)
-> with_tmpl_args_of_t<B, T> {
// actual function body
}};

How to expand the content of an mpl::set as template parameters of a function template

I have a function template with a variadic number of template parameters:
template <typename T1, typename... Ts>
void doSomething()
{
...
}
Furthermore I have an mpl set defined as follows:
template <typename... Ts>
struct MyContainerCreator
{
using type = boost::mpl::set<Ts...>;
};
using MyContainer= MyContainerCreator<T1, T2>;
Now I want to write a function doSomethingForAll() that calls doSomething() with the types in the mpl set as template parameters. Something like:
void doSomethingForAll()
{
//pseudocode:
doSomething<expandTypesToTemplateParameters<MyContainer::type>>();
}
Is this possible?
Essentially you want a lifting function that maps a template<class...> class TT with a mpl::set<Ts...> to TT<Ts...>.
To do this generically, write a lifter with the help of Foldable:
template<template<class...> class TT>
struct lifter {
template<class Foldable>
struct apply {
template <class Left, class Current> struct one_stepper;
template<class... Ts, class Current>
struct one_stepper<TT<Ts...>, Current> {
using type = TT<Ts..., Current>;
};
using type = typename mpl::fold<Foldable, TT<>,
one_stepper<mpl::_1, mpl::_2>
>::type;
};
};
Then you can use lift mpl container in this way:
template<class... Ts> struct foo {};
using u = lifter<foo>::apply<mpl::vector<int, long>>::type;
using v = lifter<foo>::apply<mpl::set<int, long>>::type;
Then u is foo<int, long>, v is foo<int, long> or foo<long, int> depends on mpl implementation.
With this tool, your task can be done by:
template<class... Ts>
struct doSomething_helper {
static void do_() { doSomething<Ts...>(); }
};
void doSomethingForAll()
{
//pseudocode:
// doSomething<expandTypesToTemplateParameters<MyContainer::type>>();
lifter<doSomething_helper>::apply<typename MyContainer::type>::type::do_();
}

C++ variadic template type filtering transformation

Problem.
I am new to template metaprogramming and am unsure of how to implement
a type filtering transformation on a tuple where a type is produced when provided
with a filtering description. I believe the following code snippet demonstrates
the behaviour I want.
enum : int {
INCLUDE,
EXCLUDE
};
template <int filter_val, class T>
struct filter {
};
int
main() {
struct A {};
struct B {};
struct C {};
typedef std::tuple<filter<INCLUDE, A>,
filter<EXCLUDE, B>,
filter<INCLUDE, C>> filters;
typedef filter_all<filters>::type filtered;
static_assert(std::is_same<filtered,
std::tuple<A, C>
>::value,
":(");
return 0;
}
What I've tried
To my knowledge, you cannot unpack more than 1 independent variadic templates so the way I thought about approaching the problem is to maintain two tuples during the recursive template specialization process where one of them represents where we are in the recursion while the other represents the so-far included T's.
template <template <class ...> class, class ...Unfiltered_Ts>
struct filter_all {
private:
template <class Unfiltered_Ts_Tuple, class Included_Ts_Tuple>
struct filter_all_impl;
// CASE 1: Include T in the result
template <
template <int, class> class, int filter_val, class T, class ...Unfiltered_Ts_impl, // Unfiltered input
template <class ...> class, class ...Included_Ts // Result so far
>
struct filter_all_impl<std::tuple<filter<INCLUDE, T>,
Unfiltered_Ts_impl...>,
std::tuple<Included_Ts...>> {
typedef typename
filter_all_impl<std::tuple<Unfiltered_Ts_impl...>,
std::tuple<Included_Ts..., T> // Append T to result
>::type type;
};
// CASE 2: Don't include T in the result
template <
template <int, class> class, int filter_val, class T, class ...Unfiltered_Ts_impl, // Unfiltered input
template <class ...> class, class ...Included_Ts // Result so far
>
struct filter_all_impl<std::tuple<filter<EXCLUDE, T>,
Unfiltered_Ts_impl...>,
std::tuple<Included_Ts...>
> {
typedef typename
filter_all_impl<std::tuple<Unfiltered_Ts_impl...>,
std::tuple<Included_Ts...> // Don't append T to result
>::type type;
};
// CASE 3: Filtering finished - set the final type as the included T's
template <
template <int, class> class, int filter_val, class T, class ...Unfiltered_Ts_impl,
template <class ...> class, class ...Included_Ts
>
struct filter_all_impl<<>, // empty
std::tuple<Included_Ts...>
> {
// Final step, set type as a tuple of all included Ts
typedef std::tuple<Included_Ts...> type;
};
public:
typedef typename filter_all_impl<
std::tuple<Unfiltered_Ts...>, // Initially contains all unfiltered Ts
std::tuple<> // Initially empty filtered Ts which eventually becomes the return type
>::type type;
};
I hope there is a simpler way to do this transformation but this is what I have arrived at so far but it's far from compiling and complains about the template specializations not being valid. Any guidance is appreciated.
Firstly, define a pick<T> helper that returns an empty tuple for excluded items, and a tuple containing the item otherwise:
template <typename>
struct pick;
template <typename T>
struct pick<filter<INCLUDE, T>> { using type = std::tuple<T>; };
template <typename T>
struct pick<filter<EXCLUDE, T>> { using type = std::tuple<>; };
Then, implement your filtering logic in terms of std::tuple_cat:
template <typename>
struct filter_all;
template <typename... Ts>
struct filter_all<std::tuple<Ts...>>
{
using type = decltype(std::tuple_cat(typename pick<Ts>::type{}...));
};
Done!
live example on wandbox.org
If you can modify the implementation of filter to expose a constexpr bool, it can be even simpler:
template <int filter_val, class T>
struct filter
{
static constexpr bool pick = filter_val == INCLUDE;
using type = T;
};
template <typename T>
struct filter_all;
template <typename... Ts>
struct filter_all<std::tuple<Ts...>>
{
using type = decltype(std::tuple_cat(
std::conditional_t<Ts::pick,
std::tuple<typename Ts::type>, std::tuple<>>{}...
));
};
live example on wandbox.org

Using with variadic template

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;
}