In some contexts, it could be useful/necessary to have a for loop evaluated/unrolled at compile time. For example, to iterate over the elements of a tuple, one needs to use std::get<I>, which depends on a template int parameter I, hence it has to be evaluated at compile time.
Using compile recursion one can solve a specific problem, as for instance discussed here, here, and, specifically for std::tuple here.
I am interested, however, on how to implement a generic compile-time for loop.
The following c++17 code implements this idea
#include <utility>
#include <tuple>
#include <string>
#include <iostream>
template <int start, int end, template <int> class OperatorType, typename... Args>
void compile_time_for(Args... args)
{
if constexpr (start < end)
{
OperatorType<start>()(std::forward<Args>(args)...);
compile_time_for<start + 1, end, OperatorType>(std::forward<Args>(args)...);
}
}
template <int I>
struct print_tuple_i {
template <typename... U>
void operator()(const std::tuple<U...>& x) { std::cout << std::get<I>(x) << " "; }
};
int main()
{
std::tuple<int, int, std::string> x{1, 2, "hello"};
compile_time_for<0, 3, print_tuple_i>(x);
return 0;
}
While the code works, it would be nicer to be able to simply provide a template function to the routine compile_time_for, rather than a template class to be instantiated at each iteration.
A code like the following, however, does not compile in c++17
#include <utility>
#include <tuple>
#include <string>
#include <iostream>
template <int start, int end, template <int, typename...> class F, typename... Args>
void compile_time_for(F f, Args... args)
{
if constexpr (start < end)
{
f<start>(std::forward<Args>(args)...);
compile_time_for<start + 1, end>(f, std::forward<Args>(args)...);
}
}
template <int I, typename... U>
void myprint(const std::tuple<U...>& x) { std::cout << std::get<I>(x) << " "; }
int main()
{
std::tuple<int, int, std::string> x{1, 2, "hello"};
compile_time_for<0, 3>(myprint, x);
return 0;
}
With gcc 7.3.0 and option std=c++17 the first error is
for2.cpp:7:25: error: ‘auto’ parameter not permitted in this context
void compile_time_for(F f, Args... args)
The questions are:
Is there a way to write compile_time_for such that it accepts a template function as its first argument?
If question 1. is positive, is there an overhead in the first working code, due to the fact that the routine create an object of type OperatorType<start> at every loop iteration?
Are there plans to introduce a feature like a compile-time for loop in the upcoming c++20?
Is there a way to write compile_time_for such that it accepts a template function as its first argument?
Short answer: no.
Long answer: a template function isn't an object, is a collection of objects and you can pass to a function, as an argument, an object, non a collection of objects.
The usual solution to this type of problem is wrap the template function inside a class and pass an object of the class (or simply the type, if the function is wrapped as a static method). That is exactly the solution you have adopted in your working code.
If question 1. is positive, is there an overhead in the first working code, due to the fact that the routine create an object of type OperatorType at every loop iteration?
Question 1 is negative.
Are there plans to introduce a feature like a compile-time for loop in the upcoming c++20?
I don't know C++20 enough to respond this question but I suppose not passing a set of function.
Anyway, you can do a sort of compile-time for loop using std::make_index_sequence/std::index_sequence starting from C++14.
By example, if you accept to extract the touple value outside your myprint() function, you can wrap it inside a lambda and write something as follows (using also C++17 template folding; in C++14 is a little more complicated)
#include <utility>
#include <tuple>
#include <string>
#include <iostream>
template <typename T>
void myprint (T const & t)
{ std::cout << t << " "; }
template <std::size_t start, std::size_t ... Is, typename F, typename ... Ts>
void ctf_helper (std::index_sequence<Is...>, F f, std::tuple<Ts...> const & t)
{ (f(std::get<start + Is>(t)), ...); }
template <std::size_t start, std::size_t end, typename F, typename ... Ts>
void compile_time_for (F f, std::tuple<Ts...> const & t)
{ ctf_helper<start>(std::make_index_sequence<end-start>{}, f, t); }
int main()
{
std::tuple<int, int, std::string> x{1, 2, "hello"};
compile_time_for<0, 3>([](auto const & v){ myprint(v); }, x);
return 0;
}
If you really want extract the tuple element (or tuples elements) inside the function, the best I can imagine is transform your first example as follows
#include <utility>
#include <tuple>
#include <string>
#include <iostream>
template <std::size_t start, template <std::size_t> class OT,
std::size_t ... Is, typename... Args>
void ctf_helper (std::index_sequence<Is...> const &, Args && ... args)
{ (OT<start+Is>{}(std::forward<Args>(args)...), ...); }
template <std::size_t start, std::size_t end,
template <std::size_t> class OT, typename... Args>
void compile_time_for (Args && ... args)
{ ctf_helper<start, OT>(std::make_index_sequence<end-start>{},
std::forward<Args>(args)...); }
template <std::size_t I>
struct print_tuple_i
{
template <typename ... U>
void operator() (std::tuple<U...> const & x)
{ std::cout << std::get<I>(x) << " "; }
};
int main()
{
std::tuple<int, int, std::string> x{1, 2, "hello"};
compile_time_for<0u, 3u, print_tuple_i>(x);
return 0;
}
-- EDIT --
The OP asks
Is there some advantage of using index_sequence over my first code?
I'm not an expert but this way you avoid recursion.
Compilers have recursion limits, from the template point of view, that can be strict. This way you avoid they.
Also, your code does not compile if you set the template parameters end > start. (One can imagine a situation where you want the compiler to determine if a loop is instantiated at all)
I suppose you mean that my code does not compile if start > end.
The bad part is that there aren't check about this problem so the compiler try to compile my code also in this case; so encounter
std::make_index_sequence<end-start>{}
where end - start is a negative number but used by a template that expect an unsigned number. So end - start become a very great positive number and this can cause problems.
You can avoid this problem imposing a static_assert() inside compile_time_for()
template <std::size_t start, std::size_t end,
template <std::size_t> class OT, typename... Args>
void compile_time_for (Args && ... args)
{
static_assert( end >= start, "start is bigger than end");
ctf_helper<start, OT>(std::make_index_sequence<end-start>{},
std::forward<Args>(args)...);
}
Or maybe you can use SFINAE to disable the function
template <std::size_t start, std::size_t end,
template <std::size_t> class OT, typename... Args>
std::enable_if_t<(start <= end)> compile_time_for (Args && ... args)
{ ctf_helper<start, OT>(std::make_index_sequence<end-start>{},
std::forward<Args>(args)...); }
If you want, using SFINAE you can add an overloaded compile_time_for() version to manage the end < start case
template <std::size_t start, std::size_t end,
template <std::size_t> class OT, typename ... Args>
std::enable_if_t<(start > end)> compile_time_for (Args && ...)
{ /* manage the end < start case in some way */ }
I'll answer on the question how to fix your last code sample.
The reason why it doesn't compile is here:
template <int start, int end, template <int, typename...> class F, typename... Args>
void compile_time_for(F f, Args... args)
/\
F is a template, you can't have an object of a template class without template parameters being substituted. E.g. you can't have on object of std::vector type, but can have object of std::vector<int>. I suggest you to make F functor with a template operator() :
#include <utility>
#include <tuple>
#include <string>
#include <iostream>
template <int start, int end, typename F, typename... Args>
void compile_time_for(F f, Args... args)
{
if constexpr (start < end)
{
f.template operator()<start>(std::forward<Args>(args)...);
compile_time_for<start + 1, end>(f, std::forward<Args>(args)...);
}
}
struct myprint
{
template <int I, typename... U>
void operator()(const std::tuple<U...>& x) { std::cout << std::get<I>(x) << " "; }
};
int main()
{
std::tuple<int, int, std::string> x{1, 2, "hello"};
compile_time_for<0, 3>(myprint(), x);
return 0;
}
Related
I am trying to create a variadic template function which would call a function to consecutive pairs of arguments.
The desired function signature would be:
template <typename ...Ts>
void apply(Ts &...args);
When called with apply(t1, t2, t3) the function should make a sequence of calls func(t1, t2)and func(t2, t3), where func is a function with signature:
template <typename L, typename R>
void func(L &left, R &right);
The order of operations is not really relevant in my context. The function has to be able to modify objects left and right, hence passed by reference. I cannot simply use polymorphic access through a base class pointer, since the objects have different class templates, a shared class cannot really be taken out.
Is it possible to achieve such a sequence of calls via a variadic template function? None of the pack expansion and fold expression examples that I've seen seem to cover such a scenario. Or should I pass my objects in a different fashion?
My initial attempt, included below (with some details omitted), packed all template parameters into a tuple, and then used a ‘const for-loop’ to ‘loop’ through the tuple elements. However, I soon came to realize that this approach would not work, because the lambda in the const-for loop invokes operator() const and therefore cannot modify the passed objects.
The code I was using does make the desired sequence of calls, but the objects are not modified (set_something() is not a const function). I had to resort to using a wrapper function with different numbers of template parameters, and making the calls to func manually.
template <std::size_t Begin, typename Callable, std::size_t... I>
constexpr void const_for_impl(Callable &&func, std::index_sequence<I...>) {
(func(std::integral_constant<std::size_t, Begin + I>{}), ...);
}
template <std::size_t Begin, std::size_t End, typename Callable>
constexpr void const_for(Callable &&func) {
const_for_impl<Begin>(std::forward<Callable>(func),
std::make_index_sequence<End - Begin>{});
};
template <typename... Ts>
void apply(Ts *... args) {
auto tuple = std::make_tuple(std::forward<Ts>(args)...);
const_for<0, sizeof...(args) - 1>(
[&](auto I) { func((std::get<I>(tuple)), (std::get<I + 1>(tuple))); });
};
template <typename L, typename R>
void func(L &l, R &r) {
// Validate with some type traits
static_assert(has_some_property<L>::value);
static_assert(has_another_property<R>::value);
// Get a shared pointer to something common
auto common = std::make_shared<typename something_common<L, R>::type>();
l.set_something(common);
r.set_something(common);
};
// Application scenario
int main() {
ComplexObjectA<SomeType, SomeParameter> a;
ComplexObjectB<AnotherType, AnotherParameter> b;
ComplexObjectC c;
apply(a, b, c);
return 0;
}
So, what's the problem? Simple fold-like template (and remember that template pattern-matching goes in reverse!)
template<typename T1, typename T2>
void apply(T1 &&t1, T2 &&t2) { func(t1, t2); }
template<typename T1, typename T2, typename... Ts>
void apply(T1 &&t1, T2 &&t2, Ts &&...ts) {
func(t1, t2);
return apply(t2, ts...);
}
Or, more precise, it should actually look as (thanks #MaxLanghof):
void apply(T1 &&t1, T2 &&t2) {
func(std::forward<T1>(t1), std::forward<T2>(t2));
}
template<typename T1, typename T2, typename... Ts>
void apply(T1 &&t1, T2 &&t2, Ts &&...ts) {
func(std::forward<T1>(t1), t2);
return apply(std::forward<T2>(t2), std::forward<TS>(ts)...);
}
An alternative (c++14) approach:
#include <utility>
#include <utility>
#include <tuple>
#include <iostream>
template <typename L, typename R>
void func(L &left, R &right) {
std::cout << left << " " << right << std::endl;
}
template <typename Tup, std::size_t... Is>
void apply_impl(Tup&& tup, std::index_sequence<Is...>) {
int dummy[] = { 0, (static_cast<void>(func(std::get<Is>(tup), std::get<Is + 1>(tup))), 0)... };
static_cast<void>(dummy);
}
template <typename ...Ts>
void apply(Ts &...args) {
apply_impl(std::forward_as_tuple(args...), std::make_index_sequence<sizeof...(Ts) - 1>{});
}
int main() {
int arr[] = {0, 1, 2, 3};
apply(arr[0], arr[1], arr[2], arr[3]);
}
Output:
0 1
1 2
2 3
[online example]
To make it c++11 compliant one would need to use one of the available integer_sequence implementations.
In what way can I express that each parameter in a parameter pack to a variadic template is itself a parameter pack?
Consider this code:
template <typename... TS>
void use_tuple(std::tuple<TS...> arg0);
template <typename... TS0, typename... TS1>
void use_tuple(std::tuple<TS0...> arg0, std::tuple<TS1...> arg1);
I want use_tuple to be able to take any number of tuples. Right now I have to write it like this:
template <typename... TS0, typename... REST>
void use_tuple(std::tuple<TS0...> arg0, REST... rest);
void use_tuple(); // Terminates the recursion.
But I want to write it like this:
// Each ELEMENT in PACK_OF_PACKS is a parameter pack.
template <(typename...)... PACK_OF_PACKS>
void use_tuple(std::tuple<PACK_OF_PACKS...>... args);
Is this even possible? If so, how? If not, what else can I do? My goal for this code is to get at the types contained in all the tuples.
My ultimate goal is something like this:
template <typename...> void foo();
use_tuple(std::tuple<int, float, char>{},
std::tuple<double, short, std::string>{},
std::tuple<std::function<void()>, std::vector<int>>{});
// Results in a call to
// foo<int, float, char, double, short, std::string,
// std::function<void()>, std::vector<int>>();
But I want to implement this in a small, constant number of indirections that does not depend on the number of tuples passed or the number of elements in each. So no recursion.
If not, what else can I do?
Why not just pass the tuples themselves as Tuples..., and then extract the types in the body of use_tuple?
template <typename...>
using example = int;
template <typename... Ts>
void foo(Ts...) { }
template <typename... Tuples>
void use_tuple(Tuples...)
{
foo(typename types<Tuples>::template apply<example>{}...);
}
You can extract the types with an helper types class:
template <typename Tuple>
struct types;
template <typename... Ts>
struct types<std::tuple<Ts...>>
{
template <template <typename...> class T>
using apply = T<Ts...>;
};
live example on wandbox
My goal for this code is to get at the types contained in all the tuples.
Not sure if you want all of them as an unique pack or not. PACK_OF_PACKS suggests it actually. In this case, you can just use std::tuple_cat.
Something along this line should work:
template<typename... T>
void do_use_tuple(std::tuple<T...>) {
// T are the types you are looking for
}
template <typename... T>
void use_tuple(T&&... tuple) {
return do_use_tuple(std::tuple_cat(std::forward<T>(tuple)...));
}
If you are not interested in forwarding also the values, just use decltype and you can easily get it.
See here for more details.
If you don't want all the parameters packed together and you want to explore them one tuple at a time, you can do something like this instead:
template<typename T>
struct tag { using type = T; };
template<typename... T, typename F>
void use_one(std::tuple<T...> &&, F f) {
F(tag<T>{}...);
}
template <typename... T>
void use_tuple(T&&... tuple) {
int arr = { 0,
(use_one(std::forward<T>(tuple), [](auto... tag) {
// tag::type contains the types of each
// parameter of a tuple, a tuple at a time
}), 0)...
};
(void)arr;
}
It's easy to adjust the lambda and the other function involved if you want also to access the values.
I guess you have something like the following (C++17) in mind. Let me stress that I do not recommend it. One should clearly prefer std::tuple_cat.
http://coliru.stacked-crooked.com/a/c64f2cb9a79af3e2
#include <array>
#include <iostream>
#include <tuple>
#include <utility>
////////////////////////////////////////////////////////////////////////////////
template<class... Ts>
void foo() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
////////////////////////////////////////////////////////////////////////////////
namespace detail {
struct Index {
std::size_t outer{};// identify the tuple
std::size_t inner{};// identify the element in the tuple
};
template<std::size_t... ns, class... Tuples>
constexpr void use_tuple(std::index_sequence<ns...>, Tuples...) {
constexpr auto inds = [&] () {
std::array<Index, sizeof...(ns)> inds{};
std::size_t n = 0;
for(std::size_t outer=0; outer<sizeof...(Tuples); ++outer) {
std::size_t size = std::begin({std::tuple_size<Tuples>::value...})[outer];
for(std::size_t inner=0; inner<size; ++inner) inds[n++] = {outer, inner};
}
return inds;
}();
using TupleTuple = std::tuple<Tuples...>;
foo<
std::tuple_element_t<
inds[ns].inner,
std::tuple_element_t<inds[ns].outer, TupleTuple>
>...
>();
}
}// detail
template<class... Tuples>
constexpr void use_tuple(Tuples... tuples) {
constexpr std::size_t N = (std::tuple_size<Tuples>{} + ...);
detail::use_tuple(std::make_index_sequence<N>{}, std::move(tuples)...);
}
////////////////////////////////////////////////////////////////////////////////
int main() {
std::tuple<int, unsigned, int> t0;
std::tuple<double, float> t1;
std::tuple<char, bool, bool, int> t2;
use_tuple(t0, t1, t2);
return 0;
}
I'm trying to do some "template metaprogramming" stuff to make exposing c++ functions to python a bit easier. What I'd like to do is take an existing function and generating a string containing info about its return type and arguments (a typeinfo would be fine too).
I'm using a function traits class based off (stolen from) this wordpress article, but rather than hard code accesses to the first few arguments I'd like to iterate through them all.
I gather that I need make a template function that takes a size_t value for the argument index (since it must be constant), but that's where I get a bit lost.
I've written some code, but I can't get it to work in the most basic of cases (let alone the generic case that I'm after.)
// The stolen function_traits struct...thing
template<typename T>
struct function_traits;
template<typename R, typename ...Args>
struct function_traits<std::function<R(Args...)>>
{
static const size_t nargs = sizeof...(Args);
using result_type = R;
template <size_t i>
struct arg
{
using type = typename std::tuple_element<i, std::tuple<Args...>>::type;
};
};
// The function of interest
int foo(float x) {
return int(x);
}
// Recurse until one argument is left, appending the type name
// to the referenced string being passed in
template<size_t argIdx, typename R, typename ... Args>
void getArgTypes(std::string& ref)
{
using fun = function_traits<std::function<R(Args...)> >;
if (argIdx == 1)
ref.append(typeid(fun::arg<0>).name()).append("\n");
else {
ref.append(typeid(fun::arg<argIdx-1>).name()).append("\n");
getArgTypes<argIdx - 1, R, Args...>(ref);
}
}
// My test of the template function
void test() {
std::string f = "";
// What I'd like to do
using fun = function_traits<std::function<decltype(foo)> >;
getArgTypes<fun::nargs, fun::result_type, ? ? ? >;
// But I can't even do this!
getArgTypes<1, float, int>(f);
}
In the first case, where I use my function_traits struct when calling getArgTypes, I don't know what to designate as the ... Args template parameter. In the second case MSVC throws the error:
Error C1202 recursive type or function dependency context too complex
I'm completely new to this metaprogramming / variadic templates stuff so sorry if this is a dumb question. If there's a less roundabout solution I'd also be interested.
Thank you for reading!
if (argIdx == 1) can't be a runtime condtion. It must be changed to a compile time one with std::enable_if. This is where the error comes from: a compiler tries to instantiate endlessly (recursively without a stop condition) the getArgType function template.
All dependent type names must be announced with a typename keyword, and those that refer to templates must be announced with a template keyword, e.g. typename fun::template arg<0> in place of fun::arg<0>.
fun::arg<0> itself is a struct with a nested type definition. To access it, use typename fun::template arg<0>::type syntax.
Expansion of function_traits::arg<N>::type can be done with the indices trick, in particular typename F::template arg<Is>::type....
#include <string>
#include <typeinfo>
#include <functional>
#include <utility>
#include <cstddef>
template <size_t argIdx, typename R, typename... Args>
auto getArgTypes(std::string& ref)
-> typename std::enable_if<argIdx == 1>::type
{
using fun = function_traits<std::function<R(Args...)> >;
ref.append(typeid(typename fun::template arg<0>::type).name()).append(" ");
}
template <size_t argIdx, typename R, typename... Args>
auto getArgTypes(std::string& ref)
-> typename std::enable_if<argIdx != 1>::type
{
using fun = function_traits<std::function<R(Args...)> >;
ref.append(typeid(typename fun::template arg<argIdx-1>::type).name()).append(" ");
getArgTypes<argIdx - 1, R, Args...>(ref);
}
template <typename F, std::size_t... Is>
void test2(std::index_sequence<Is...>)
{
std::string s;
getArgTypes<F::nargs, typename F::result_type, typename F::template arg<Is>::type...>(s);
std::cout << s;
}
void test()
{
using F = function_traits<std::function<decltype(foo)>>;
test2<F>(std::make_index_sequence<F::nargs>{});
}
DEMO
Very basic index_sequence implementation goes as follows:
template <std::size_t...> struct index_sequence {};
template <std::size_t N, std::size_t... Is> struct make_index_sequence : make_index_sequence<N-1, N-1, Is...> {};
template <std::size_t... Is> struct make_index_sequence<0, Is...> : index_sequence<Is...> {};
Consider the following struct:
struct Test {
char a;
short b;
int c;
long long d;
void transformTest() {
// Pseudo
foreach datamember (regardless of type) of Test
call someTransform(datamember)
}
};
We could also pass a lambda, function pointer, functor, whatever into the transformTest(), that's not my concern as of right now.
What's the best way to do this?
The best way is to do it explicitly:
someTransform(a);
someTransform(b);
someTransform(c);
someTransform(d);
Of course, you'll need an appropriate number of overloads of someTransform().
If you really, really don't like that, there's always Boost Fusion. With that you can put your values together in a structure which the library understands and then can iterate over. This won't be worth doing for simple use cases.
Sounds like a case for Boost Fusion and its for_each() function combined with BOOST_FUSION_ADAPT_STRUCT. This stuff can work some miracles! Here's an example on how you can do it:
#include <boost/fusion/include/algorithm.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <iostream>
using namespace boost::fusion;
struct Print
{
template <typename T>
void operator()( T && t ) const
{
std::cout << t << ' ';
}
};
struct Test {
char a;
short b;
int c;
long long d;
void printTest() const;
};
BOOST_FUSION_ADAPT_STRUCT(
Test,
(char, a)
(short, b)
(int, c)
(long long, d)
)
void Test::printTest() const
{
for_each ( *this, Print() );
}
int main()
{
const auto t = Test();
t.printTest();
}
Step 1: Wrap your data in a tuple. Possibly a temporary one.
Step 2: Wrap your callable in a functor.
Step 3: Write a tuple_foreach that applies a functor to each element of a tuple.
For step 1, I'd advise leaving the data where it is, and just using a std::tie to create a tuple of references.
For step 2, a simple perfect forwarding functor looks like:
#define RETURNS(X) ->decltype(X) { return (X); }
struct foo_functor {
template<typename... Args>
auto operator()(Args&&... args) const
RETURNS( foo( std::forward<Args>(args)... ) )
};
which represents the override set of functions called foo, and wraps that into a single object which automatically dispatches any call to the appropriate overload of foo.
For step 3, it isn't hard. Just use the indexes trick to run code on each element of a tuple:
void do_in_order() {}
template<typename Lambda, typename... Lambdas>
void do_in_order( Lambda&& closure, Lambdas&&... closures ) {
std::forward<Lambda>(closure)();
do_in_order( std::forward<Lambdas>(closures)... );
}
template<unsigned... Is>
struct seq { typedef seq<Is> type; }
template<unsigned Max, unsigned... Is>
struct make_seq:make_seq<Max-1, Max-1, Is...> {};
template<unsigned... Is>
struct make_seq<0,Is...>:seq<Is...> {};
template<typename Tuple, typename Functor, unsigned... Is>
void foreach_tuple_helper( seq<Is...>, Tuple&& t, Functor&& f ) {
do_in_order(
[&]{ std::forward<Functor>(f)(std::get<Is>(std::forward<Tuple>(t))); }...
);
}
template<typename Tuple, typename Functor>
void foreach_tuple( Tuple&& t, Functor&& f ) {
foreach_tuple_helper( make_seq<std::tuple_size< typename std::decay<Tuple>::type >::value>(), std::forward<Tuple>(t), std::forward<Functor>(f) );
}
do_in_order doesn't work in clang last I checked, but an equivalent indexes trick that works on your compiler shouldn't be hard to google.
While Boost.Fusion is a good solution, I thought I'd add that in C++11 you could use std::tuple like this:
template <unsigned ... indices>
struct sequence
{
typedef sequence type;
};
template <unsigned size, unsigned ... indices>
struct static_range : static_range<size-1,size-1,indices...> {};
template <unsigned ... indices>
struct static_range<0, indices...> : sequence<indices...> {};
template <class Function, class Tuple, unsigned ... indices>
auto transform_impl(const Tuple & t, Function f, sequence<indices...>) ->
std::tuple<decltype(f(std::get<indices>(t)))...>
{
return std::make_tuple(f(std::get<indices>(t))...);
}
template <class Function, class Tuple>
auto transform_tuple(const Tuple & t, Function f) ->
decltype(transform_impl(t, f, static_range<std::tuple_size<Tuple>::value>()))
{
return transform_impl(t, f, static_range<std::tuple_size<Tuple>::value>());
}
The sequence/static_range classes have been invaluable in my code in expanding classes (not just std::tuples) by indices so that I can std::get them.
Other than that, I think the code is fairly straightforward, it should be noted however that with this method the order in which f is invoked on each tuple element is undefined.
Usage would look like:
std::tuple<char, short, int, long long> t;
struct addone
{ template <class T> auto operator()(T t) -> decltype(t+1) {return t + 1;}};
auto t2 = transform_tuple(t, addone());
The resulting tuple will not have the same types as the input tuple due to integral promotion, each will have type typename std::common_type<T,int>::type.
Is it possible to store a parameter pack somehow for a later use?
template <typename... T>
class Action {
private:
std::function<void(T...)> f;
T... args; // <--- something like this
public:
Action(std::function<void(T...)> f, T... args) : f(f), args(args) {}
void act(){
f(args); // <--- such that this will be possible
}
}
Then later on:
void main(){
Action<int,int> add([](int x, int y){std::cout << (x+y);}, 3, 4);
//...
add.act();
}
To accomplish what you want done here, you'll have to store your template arguments in a tuple:
std::tuple<Ts...> args;
Furthermore, you'll have to change up your constructor a bit. In particular, initializing args with an std::make_tuple and also allowing universal references in your parameter list:
template <typename F, typename... Args>
Action(F&& func, Args&&... args)
: f(std::forward<F>(func)),
args(std::forward<Args>(args)...)
{}
Moreover, you would have to set up a sequence generator much like this:
namespace helper
{
template <int... Is>
struct index {};
template <int N, int... Is>
struct gen_seq : gen_seq<N - 1, N - 1, Is...> {};
template <int... Is>
struct gen_seq<0, Is...> : index<Is...> {};
}
And you can implement your method in terms of one taking such a generator:
template <typename... Args, int... Is>
void func(std::tuple<Args...>& tup, helper::index<Is...>)
{
f(std::get<Is>(tup)...);
}
template <typename... Args>
void func(std::tuple<Args...>& tup)
{
func(tup, helper::gen_seq<sizeof...(Args)>{});
}
void act()
{
func(args);
}
And that it! So now your class should look like this:
template <typename... Ts>
class Action
{
private:
std::function<void (Ts...)> f;
std::tuple<Ts...> args;
public:
template <typename F, typename... Args>
Action(F&& func, Args&&... args)
: f(std::forward<F>(func)),
args(std::forward<Args>(args)...)
{}
template <typename... Args, int... Is>
void func(std::tuple<Args...>& tup, helper::index<Is...>)
{
f(std::get<Is>(tup)...);
}
template <typename... Args>
void func(std::tuple<Args...>& tup)
{
func(tup, helper::gen_seq<sizeof...(Args)>{});
}
void act()
{
func(args);
}
};
Here is your full program on Coliru.
Update: Here is a helper method by which specification of the template arguments aren't necessary:
template <typename F, typename... Args>
Action<Args...> make_action(F&& f, Args&&... args)
{
return Action<Args...>(std::forward<F>(f), std::forward<Args>(args)...);
}
int main()
{
auto add = make_action([] (int a, int b) { std::cout << a + b; }, 2, 3);
add.act();
}
And again, here is another demo.
You can use std::bind(f,args...) for this. It will generate a movable and possibly copyable object that stores a copy of the function object and of each of the arguments for later use:
#include <iostream>
#include <utility>
#include <functional>
template <typename... T>
class Action {
public:
using bind_type = decltype(std::bind(std::declval<std::function<void(T...)>>(),std::declval<T>()...));
template <typename... ConstrT>
Action(std::function<void(T...)> f, ConstrT&&... args)
: bind_(f,std::forward<ConstrT>(args)...)
{ }
void act()
{ bind_(); }
private:
bind_type bind_;
};
int main()
{
Action<int,int> add([](int x, int y)
{ std::cout << (x+y) << std::endl; },
3, 4);
add.act();
return 0;
}
Notice that std::bind is a function and you need to store, as data member, the result of calling it. The data type of that result is not easy to predict (the Standard does not even specify it precisely), so I use a combination of decltype and std::declval to compute that data type at compile time. See the definition of Action::bind_type above.
Also notice how I used universal references in the templated constructor. This ensures that you can pass arguments that do not match the class template parameters T... exactly (e.g. you can use rvalue references to some of the T and you will get them forwarded as-is to the bind call.)
Final note: If you want to store arguments as references (so that the function you pass can modify, rather than merely use, them), you need to use std::ref to wrap them in reference objects. Merely passing a T & will create a copy of the value, not a reference.
Operational code on Coliru
This question was from C++11 days. But for those finding it in search results now, some updates:
A std::tuple member is still the straightforward way to store arguments generally. (A std::bind solution similar to #jogojapan's will also work if you just want to call a specific function, but not if you want to access the arguments in other ways, or pass the arguments to more than one function, etc.)
In C++14 and later, std::make_index_sequence<N> or std::index_sequence_for<Pack...> can replace the helper::gen_seq<N> tool seen in 0x499602D2's solution:
#include <utility>
template <typename... Ts>
class Action
{
// ...
template <typename... Args, std::size_t... Is>
void func(std::tuple<Args...>& tup, std::index_sequence<Is...>)
{
f(std::get<Is>(tup)...);
}
template <typename... Args>
void func(std::tuple<Args...>& tup)
{
func(tup, std::index_sequence_for<Args...>{});
}
// ...
};
In C++17 and later, std::apply can be used to take care of unpacking the tuple:
template <typename... Ts>
class Action
{
// ...
void act() {
std::apply(f, args);
}
};
Here's a full C++17 program showing the simplified implementation. I also updated make_action to avoid reference types in the tuple, which was always bad for rvalue arguments and fairly risky for lvalue arguments.
I think you have an XY problem. Why go to all the trouble to store the parameter pack when you could just use a lambda at the callsite? i.e.,
#include <functional>
#include <iostream>
typedef std::function<void()> Action;
void callback(int n, const char* s) {
std::cout << s << ": " << n << '\n';
}
int main() {
Action a{[]{callback(13, "foo");}};
a();
}