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();
}
Related
I can't figure out how to implement a function with a variable number of arguments of the same type.
I'm writing for a microcontroller with little stack and memory, so I can't use recursion or the STL (the parts with exceptions).
Is it possible to make such a function?
struct S{
int r1;
int r2;
};
template<S* s, int... args> fun(int arg1, int arg2);
which expands to something like this:
for(int arg:args){
s->r1+=7*arg;
}
example of invocation:
S s;
const int mode=3, speed=1;
fun<&s,1,2,7,4>(mode,speed);
Having all parameters in a variadic pack be of same type can be required with C++20 concepts.
Unfortunately, as of C++20, the standard library doesn't have a concept for all_same (there is only std::same_as for two types), but it can be easily defined:
template<class... Ts>
concept all_same =
sizeof...(Ts) < 2 ||
std::conjunction_v<
std::is_same<std::tuple_element_t<0, std::tuple<Ts...>>, Ts>...
>;
template<typename... Ts> requires all_same<Ts...>
void foo(Ts&&... ts) {}
Code: https://godbolt.org/z/dH9t-N
Note that in many cases requiring same type is not necessary, you may instead require that all arguments have a common type. To require common type, based on test if common type exists you can have the following concept:
template <typename AlwaysVoid, typename... Ts>
struct has_common_type_impl : std::false_type {};
template <typename... Ts>
struct has_common_type_impl<std::void_t<std::common_type_t<Ts...>>, Ts...>
: std::true_type {};
template <typename... Ts>
concept has_common_type =
sizeof...(Ts) < 2 ||
has_common_type_impl<void, Ts...>::value;
template<typename... Ts> requires has_common_type<Ts...>
void foo(Ts&&... ts) {}
Code: https://godbolt.org/z/5M6dLp
You can easily do this with fold expression (c++17) and concepts (c++20) features.
The concept will look like this:
template<typename T, typename... Types>
concept is_all_same = (... && std::is_same<T, Types>::value);
If you want them to be just the same type, you can use it this way:
template<typename... Types> requires is_all_same<Types...>
void fun();
If you want a function to take a specific type, you can use it this way:
template<is_all_same<int>... Types>
void fun();
I can't figure out how to implement a function with a variable number of arguments of the same type.
Template argument of the same type or ordinary function arguments of the same type?
The first case is simple (if the type is one admitted for template value types), exactly as you have written
template<S* s, int... args>
fun (int arg1, int arg2);
and you can use they using template folding, if you can use C++17,
template <S* s, int... args>
auto fun (int arg1, int arg2)
{ ((s->r1 += 7 * args), ...); }
or in a little more complicated way before (C++11/C++14)
template <S* s, int... args>
auto fun (int arg1, int arg2)
{
using unused = int[];
(void)unused { 0, s->r1 += 7 * args ... };
}
Unfortunately you can call this type of function with compile time known integers so, by example, not with variables
int a = 7;
fun<&s,1,2,a,4>(mode,speed); // compilation error
In this case you need a variadic list of ordinary function arguments of the same type; unfortunately this is a little more complicated.
You can create a typical variadic list of template parameter
template <typename ... Args>
auto fun (Args ... args)
imposing, through SFINAE, that all Args... are deduced or explicated as int (see Michael Kenzel's answer).
Unfortunately this require that every argument is exactly if type int so calling func with (by example) a long int gives a compilation error
fun(1, 2, 3l); // compilation error (3l is a long int, not an int)
Obviously you can relax the SFINAE condition imposing (by example) that all Args... types are convertible (std::is_convertible) to int but isn't exactly has developing a function receiving a variadic number of arguments of the same type.
If you can accept a superior limit to the number of arguments (64, in the following example) and that the function is method (maybe static) of a class, you can create a foo class containing a method f() that receive zero int, one f() that receive one int, one f() that receive two ints, etc, until an f() that receive 63 ints.
The following is a full compiling C++17 example
#include <utility>
#include <type_traits>
struct S
{
int r1;
int r2;
};
S s;
const int mode=3, speed=1;
template <typename T, std::size_t>
using getType = T;
template <std::size_t N, typename = std::make_index_sequence<N>>
struct bar;
template <std::size_t N, std::size_t ... Is>
struct bar<N, std::index_sequence<Is...>>
{
static constexpr auto f (getType<int, Is> ... args)
{ ((s.r1 += 7 * args), ...); }
};
template <S &, std::size_t N = 64u, typename = std::make_index_sequence<N>>
struct foo;
template <S & s, std::size_t N, std::size_t ... Is>
struct foo<s, N, std::index_sequence<Is...>> : public bar<Is>...
{ using bar<Is>::f...; };
int main ()
{
foo<s>::f(mode, speed);
}
In C++14 is a little more complicated because there isn't variadic using so you have to write the foo class in a recursive way.
In C++11 you have also to develop a substitute for std::make_index_sequence/std::index_sequence.
Unfortunately, there is currently no way to specify a function parameter pack where each parameter is of the same type. The next best thing you can get (as far as I'm aware) would be a function that takes a variable number of arguments of any type as long as the type of all of them is int:
#include <type_traits>
template <typename... Args>
auto f(Args... args) -> std::enable_if_t<(std::is_same_v<Args, int> && ...)>
{
…
}
void test()
{
f(1, 2, 3); // OK
f(1, 2, 3.0f); // error
}
live example here
The trick here is to rely on SFINAE to effectively remove all versions of the function where not all the args end up being of type int…
For your concrete example, you could do, e.g.:
#include <type_traits>
struct S
{
int r1;
int r2;
};
template <S& s, typename... Args>
auto f(Args... args) -> std::enable_if_t<(std::is_same_v<Args, int> && ...)>
{
((s.r1 += 7 * args), ...);
}
S s;
const int mode=3, speed=1;
void test()
{
f<s>(mode, speed);
}
live demo here
More expanded example with multiple arguments.
From this answer here > Is there a way to define a variadic number of arguments of the same type?
Shorter initialization (std::is_same_v) and example of using >
template<class T, class... Types>
concept is_all_same = (... && std::is_same_v<T, Types>);
// "requires" to prevent function instance without arguments
template<class... Types> requires is_all_same<Types...>
void fun1(const Types&... types)
{
for (const auto &t : {types...})
std::cout << t << std::endl;
}
template<is_all_same<int>... Types> requires is_all_same<Types...>
void fun2(const Types&... types)
{
for (const int &t : {types...})
std::cout << t << std::endl;
}
And some usable examples
// check same type
template<class T, class... Types>
concept is_all_same = (... && std::is_same_v<T, Types>);
// different amount of arguments, different one type
template<class... Types> requires is_all_same<Types...>
void fun1(const Types&... types)
{
for (const auto &t : {types...})
std::cout << t << std::endl;
}
// different amount of arguments one type - const char*
template<is_all_same<const char*>... Types> requires is_all_same<Types...>
void fun2(const Types&... types)
{
for (const char *t : {types...})
std::cout << t << std::endl;
}
//
// check c-array of chars
template<class T, class... Types>
concept is_c_arr_char =
std::is_bounded_array_v<T> &&
std::is_same_v<std::remove_all_extents_t<T>, char> &&
(... && (
std::is_bounded_array_v<Types> &&
std::is_same_v<std::remove_all_extents_t<Types>, char>
));
// different amount of arguments
// different type based on "signature" c-array chars - const char[x]
template<class... Types> requires is_c_arr_char<Types...>
void fun3(const Types&... types)
{
for (const char *t : {types...})
std::cout << t << std::endl;
}
//
int main()
{
fun1(1.1, 2.2);
const char* a = "a1";
const char* b = "b22";
fun2(a, b);
fun2((const char*)"a1", (const char*)"b22");
fun3("c3", "d44");
const char c[] = "c3";
const char d[] = "d44";
fun3(c, d);
}
And consequence at all for me - better use "init list" - there we see size() function - access to amount of arguments
const std::initializer_list<const char*> &args
I would like to create a wrapper for class methods so I used a variable template and std::tuple to wrap the method. It works as I need, but I cannot figure out how to be able to wrap a method with parameters called by reference. Please see the main function below.
#include <iostream>
#include <string>
#include <functional>
using namespace std::placeholders;
namespace helper {
template <std::size_t... Ts> struct index {};
template <std::size_t N, std::size_t... Ts> struct gen_seq : gen_seq<N - 1, N - 1, Ts...> {};
template <std::size_t... Ts> struct gen_seq<0, Ts...> : index<Ts...> {};
template<class F, class... Ts, std::size_t... Is> void for_each_in_tuple(std::tuple<Ts...> &tuple, F func, std::index_sequence<Is...>)
{
using expander = int[];
(void)expander {
0, ((void)func(std::get<Is>(tuple)), 0)...
};
}
template<class F, class...Ts> void for_each_in_tuple(std::tuple<Ts...> &tuple, F func)
{
for_each_in_tuple(tuple, func, std::make_index_sequence<sizeof...(Ts)>());
}
}
enum class Result : std::int32_t {
Result1 = 1,
Result2 = 2
};
template<typename... Args> class Procedure {
private:
std::function<Result(Args...)> m_method;
std::tuple<Args...> args;
template <typename... Args, std::size_t... Is> Result callMethod(std::tuple<Args...> &tup, helper::index<Is...>)
{
return m_method(std::get<Is>(tup)...);
}
template <typename... Args> Result callMethod(std::tuple<Args...> &tup)
{
return callMethod(tup, helper::gen_seq<sizeof...(Args)> {});
}
public:
template <typename ...Args> void setMethod(const std::function<Result(Args &&... args)> &method)
{
m_method = method;
}
};
class MethodsClass {
public:
Result doSomething(std::int32_t input, double output)
{
return Result::Result1;
}
};
int main()
{
**// This works fine**
MethodsClass methods;
Procedure <std::int32_t, double> procedure1;
procedure1.setMethod<std::int32_t, double>(std::bind(&MethodsClass::doSomething1, methods, _1, _2));
**// This end with compiler error std::tuple<int32_t,double &>::tuple': no appropriate default constructor available**
Procedure <std::int32_t, double &> procedure2;
procedure2.setMethod<std::int32_t, double &>(std::bind(&MethodsClass::doSomething2, methods, _1, _2));
return 0;
}
I know why the second case does not compile, the message is clear and it is obvious that a reference has to be initialized. But how to do it in this case? How to initialize std:tuple, how and where to expand the arguments pack? I am missing something and can't realize what.
Well, in C++17 it is quite easy. The tuple has to be declared:
std::tuple<typename std::decay<Args>::type...> m_args;
Then all the recursive tuple unpacking can be simply removed and replaced by:
std::apply(m_method, m_args);
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;
}
namespace details {
template <std::size_t I = 0, typename Tuple, typename Function, typename... Args>
typename std::enable_if<I == std::tuple_size<Tuple>::value, void>::type ForEach(Tuple &t, Function f, Args &... args) {}
template <std::size_t I = 0, typename Tuple, typename Function, typename... Args>
typename std::enable_if<(I < std::tuple_size<Tuple>::value), void>::type ForEach(Tuple &t, Function f, Args &... args) {
f(std::get<I>(t), args...);
ForEach<I + 1>(t, f, args...);
}
}
An implementation for ForEach functionality for all types of a tuple is above. It calls f(tuple_type, args...)
However I want something like tuple_type.f(args...) where f and args are template arguments.
f would be a member function of all the types in the tuple, taking args... as arguments.
template <typename... Types>
class TupleManager {
std::tuple<Types...> t;
template <typename Function, typename... Args>
void ForEach(Function f, Args& ... args) {
details::ForEach<>(t, f, args...);
}
}
Clarification: f needs to be a member function, i.e a function that takes the same name for all types in the tuple.
Eg:
struct A {
void foo() {
std::cout << "A's foo\n";
}
};
struct B : A {
void foo() {
std::cout << "B's foo\n";
}
};
struct C : A {
void foo() {
std::cout << "C's foo\n";
}
};
But now I can't pass foo. Passing &A::foo print's A's foo. The requirement is to print A'foo for object of A in the tuple, B's foo for object of B in the tuple, and C's foo for object of C in the tuple.
Demo
The beauty of std::for_each is in its disassociation of function application (on the elements of the range) from iteration (of the range). As such, one doesn't pass the arguments of the call to std::for_each (except for the callee itself that is already passed via the range). Having that in mind, you might want to consider using a different version of for_each that is closer to the std one. Check this one for example: iterate over tuple.
If you're up to a generalisation, you may want to use Jonathan Müller's tuple_iterator. That enables application std algorithms on std::tuples just as if std::tuple were a std container. In particular, you can use std::for_each for your desirable iteration.
I want to write benchmark code for several combinations of several possible classes. If I write each combination myself it becomes an unmaintainable mess. Thus I'm looking for a way to automatically combine each type via templates, something akin to the following pseudo code:
for (typename HashFuction : Sha256, Sha512, Sa512_256, Sha3_256, Sha3_512) {
for (typename KeyingWrapper : TwoPassKeyedHash, OnePassKeyedHash, PlainHash) {
for (typename InstantiatedGetLeaf: GetLeaf<8>, GetLeaf<1024>) {
for (typename algorithm : algA, algB, algC) {
runAndTime<HashFunction,KeyingWrapper,
InstantiatedGetLeaf,algorithm>(someArgs);
}
}
}
}
Where Sha256,… ,TwoPassKeyedHash,… are types.
The code I'm looking for is supposed to be functionally equivalent to the following:
runAndTime<Sha256,TwoPassKeyedHash,GetLeaf<8>,algA>(someArgs);
runAndTime<Sha256,TwoPassKeyedHash,GetLeaf<8>,algB>(someArgs);
runAndTime<Sha256,TwoPassKeyedHash,GetLeaf<8>,algC>(someArgs);
runAndTime<Sha256,TwoPassKeyedHash,GetLeaf<1024>,algA>(someArgs);
runAndTime<Sha256,TwoPassKeyedHash,GetLeaf<1024>,algB>(someArgs);
runAndTime<Sha256,TwoPassKeyedHash,GetLeaf<1024>,algC>(someArgs);
runAndTime<Sha256,OnePassKeyedHash,GetLeaf<8>,algA>(someArgs);
runAndTime<Sha256,OnePassKeyedHash,GetLeaf<8>,algB>(someArgs);
runAndTime<Sha256,OnePassKeyedHash,GetLeaf<8>,algC>(someArgs);
// And 99 further lines…
With Peregring-lk's help I have come as far as
#include <iostream>
template<typename Aux_type>
void test_helper()
{}
template<typename Aux_type, typename Head, typename... Tail>
void test_helper() {
std::cout << Head::i;
test_helper<Aux_type, Tail...>();
}
template<typename... Args>
void test()
{
test_helper<void, Args...>();
}
struct A{
static const int i=1;
};
struct B{
static const int i=2;
};
int main() {
test<A, B>();
return 0;
}
but I don't yet see how I could iterate that recursion to get nested loops. Any help would be appreciated.
(Edit: Code restructuring and inclusion of Peregring-lk's answer.)
Sometimes it helps to have an idea of what you are aiming for:
you need several parameter types
and for each parameter types, several possible "values"
And want to apply something on every single combination of values (one per parameter type at a time).
This looks like it could be expressed:
combine<
Set<Sha256, Sha512, Sa512_256, Sha3_256, Sha3_512>,
Set<TwoPassKeyedHash, OnePassKeyedHash, PlainHash>,
Set<GetLeaf<8>, GetLeaf<1024>>,
Set<algA, algB, algC>
>(runAndTime);
if runAndTime is an instance of:
struct SomeFunctor {
template <typename H, typename W, typename L, typename A>
void operator()(cons<H>{}, cons<W>{}, cons<L>{}, cons<A>{});
};
and cons is just a way to pass a type as a regular parameter (much easier).
Let's go ?
First, some way to pass around types (cheaply):
template <typename T>
struct cons { using type = T; };
template <typename... T>
struct Set {};
An explicit bind (with no magic inside):
template <typename F, typename E>
struct Forwarder {
Forwarder(F f): inner(f) {}
template <typename... Args>
void operator()(Args... args) { inner(cons<E>{}, args...); }
F inner;
}; // struct Forwarder
And now we delve into the real task at hand:
we need to iterate on sets of types
within a set, we need to iterate on its elements (types too)
That calls for two levels of dispatch:
template <typename FirstSet, typename... Sets, typename F>
void combine(F func);
template <typename Head, typename... Tail, typename... Sets, typename F>
void apply_set(F func, Set<Head, Tail...>, Sets... others);
template <typename... Sets, typename F>
void apply_set(F func, Set<>, Sets... others);
template <typename E, typename NextSet, typename... Sets, typename F>
void apply_item(F func, cons<E>, NextSet, Sets...);
template <typename E, typename F>
void apply_item(F func, cons<E> e);
Where combine is the outer (exposed) function, apply_set is used to iterate on the sets and apply_item is used to iterate on the types within a set.
The implementations are simple:
template <typename Head, typename... Tail, typename... Sets, typename F>
void apply_set(F func, Set<Head, Tail...>, Sets... others) {
apply_item(func, cons<Head>{}, others...);
apply_set(func, Set<Tail...>{}, others...);
} // apply_set
template <typename... Sets, typename F>
void apply_set(F, Set<>, Sets...) {}
template <typename E, typename NextSet, typename... Sets, typename F>
void apply_item(F func, cons<E>, NextSet ns, Sets... tail) {
Forwarder<F, E> forwarder(func);
apply_set(forwarder, ns, tail...);
}
template <typename E, typename F>
void apply_item(F func, cons<E> e) {
func(e);
} // apply_item
template <typename FirstSet, typename... Sets, typename F>
void combine(F func) {
apply_set(func, FirstSet{}, Sets{}...);
} // combine
For each of apply_set and apply_item we have a recursive case and a base case, though it's some kind of co-recursion here as apply_item calls back to apply_set.
And a simple example:
struct Dummy0 {}; struct Dummy1 {}; struct Dummy2 {};
struct Hello0 {}; struct Hello1 {};
struct Tested {
Tested(int i): value(i) {}
void operator()(cons<Dummy0>, cons<Hello0>) { std::cout << "Hello0 Dummy0!\n"; }
void operator()(cons<Dummy0>, cons<Hello1>) { std::cout << "Hello1 Dummy0!\n"; }
void operator()(cons<Dummy1>, cons<Hello0>) { std::cout << "Hello0 Dummy1!\n"; }
void operator()(cons<Dummy1>, cons<Hello1>) { std::cout << "Hello1 Dummy1!\n"; }
void operator()(cons<Dummy2>, cons<Hello0>) { std::cout << "Hello0 Dummy2!\n"; }
void operator()(cons<Dummy2>, cons<Hello1>) { std::cout << "Hello1 Dummy2!\n"; }
int value;
};
int main() {
Tested tested(42);
combine<Set<Dummy0, Dummy1, Dummy2>, Set<Hello0, Hello1>>(tested);
}
Which you can witness live on Coliru prints:
Hello0 Dummy0!
Hello1 Dummy0!
Hello0 Dummy1!
Hello1 Dummy1!
Hello0 Dummy2!
Hello1 Dummy2!
Enjoy :)
Note: it was presumed that the functor was cheap to copy, otherwise a reference can be used, both when passing and when storing it in Forwarder.
Edit: removed the cons around Set (everywhere it appeared), it's unnecessary.
Functions doesn't allow partial specializations, unless the specialization is complete. Every new different function signature declares a new overload, unless their signatures are exactly the same.
Try instead the following code:
#include <iostream>
template<typename Aux_type>
void test_helper()
{}
template<typename Aux_type, typename Head, typename... Tail>
void test_helper() {
std::cout << Head::i;
test_helper<Aux_type, Tail...>();
}
template<typename... Args>
void test()
{
test_helper<void, Args...>();
}
struct A{
static const int i=1;
};
struct B{
static const int i=2;
};
int main() {
test<A, B>();
return 0;
}
and it does compile (and prints 12).
Anyway, I've not understood your pseudocode sample.
I think that C++ is not the right tool, when it comes to convenient and flexible code generation ... Just write a simple utility in a scripting language of your choice, like in python:
generate_test_code.py:
#!/usr/bin/python
for HashFuction in {"Sha256", "Sha512", "Sa512_256", "Sha3_256", "Sha3_512"}:
for KeyingWrapper in {"TwoPassKeyedHash", "OnePassKeyedHash", "PlainHash"}:
for InstantiatedGetLeaf in {"GetLeaf<8>", "GetLeaf<1024>"}:
for Algorithm in {"algA", "algB", "algC"}:
print("runAndTime<{},{},{},{}>(someArgs);".format(HashFuction,KeyingWrapper,InstantiatedGetLeaf,Algorithm))
... then in your Makefile:
generated_test_code.cpp: generate_test_code.py
python generate_test_code.py > generated_test_code.cpp
... and in your c++ code, simply #include "generated_test_code.cpp" where you want it.