Iterate over 2 template parameter packs in parallel - c++

I started to implement a very flexible Odometer. It may have several disks with even different amount of values on each different disk. And, as an extension, even the data type of values on each of the single disks could be different.
All this shall be implemented with one class. The number of template parameters defines the behavior of the class.
1 template parameter: Like Odomoter<int> shall result in an Odometer having int values on each disk. The resulting internal data type will be a std::vector<std::vector<int>>
2 or more template parameters: The number of template parameter will define the number of single disks of the Odometer. Each disk has the data type of the template parameter. In the case of Odometer<char, int, double>, this will result in a data type std::tuple<std::vector<char>, std::vector<int>, std::vector<double>>
Now, I want to add a variadic constructor, where I can add whatever data. Of course types and number of arguments must match. I omit the check for the moment and will add it later.
So, now I have a templatized variadic class and a variadic constructor. So, I have the parameter pack off the class and the parameter pack of the constructor.
Now I would need iterate over the elements of both parameter packs at the same time in parallel.
Please see the below code example for an illustration of the problem (I deleted most of the code in the class, to just show you the problem):
#include <vector>
#include <tuple>
#include <list>
#include <initializer_list>
template<typename...Ts>
struct Odometer {
static constexpr bool IsTuple = ((std::tuple_size<std::tuple<Ts...>>::value) > 1);
template<typename...Ts>
using Tuples = std::tuple<std::vector<Ts>...>;
template<typename...Ts>
using MyType = std::tuple_element_t<0, std::tuple<Ts...>>;
template<typename...Ts>
using Vectors = std::vector<std::vector<MyType<Ts...>>>;
template<typename...Ts>
using Disks = std::conditional<IsTuple, Tuples<Ts...>, Vectors<Ts...>>::type;
Disks<Ts...> disks{};
template <typename...Args>
Odometer(Args...args) {
if constexpr (IsTuple) {
// Here disk is a std::tuple<std::vector<char>, std::vector<int>, std::vector<double>>
([&] {
//std::vector<MyType<Ts...>> disk{}; // Does not work. Or would always be a std::vector<char>
if constexpr (std::ranges::range<Args>) {
//for (const auto& r : args) // Does not work
//disk.push_back(r); // Does not work
}
else {
//disk.push_back(args); // Does not work
} } (), ...);
}
else {
([&] {
disks.push_back({});
if constexpr (std::ranges::range<Args>) {
for (const auto& r : args)
disks.back().push_back(r);
}
else {
disks.back().push_back(args);
} } (), ...);
}
}
};
int main() {
Odometer<char, int, double> odo2('a', std::vector{1,2,3}, std::list{4.4, 5.5});
}
I can iterate over the parameter pack of the constructor using a fold expression. I could also use std::apply. But, I need to iterate also over the tuple elements of the "disks", defined by the class template parameters.
I do not want to use recursive templates.
So, I need to iterate of 2 parameter packs in parallel at the same time. How could this be done?
The only idea I have now is to use a helper class with a std::index_sequence, but I do not know.
Please be reminded. Check of number of elements in parameter packs and type will be done later.

The only idea I have now is to use a helper class with a
std::index_sequence, but I do not know.
You can use template lambda to expand index_sequence and get the corresponding tuple elements through std::get<Is>:
static_assert(sizeof...(Args) == sizeof...(Ts));
[&]<std::size_t... Is>(std::index_sequence<Is...>) {
([&] {
auto& disk = std::get<Is>(disks);
if constexpr (std::ranges::range<Args>)
for (const auto& elem : args)
disk.push_back(elem);
else
disk.push_back(args);
} (), ...);
}(std::index_sequence_for<Args...>{});
Demo with reduced examples

You don't need to iterate manually, you just need a function to convert each argument into vector:
template <typename T, typename Arg>
std::vector<T> as_vector(Arg&& arg)
{
if constexpr (std::ranges::range<std::decay_t<Arg>>) {
return {std::begin(arg), std::end(arg)};
} else {
return {std::forward<Arg>(arg)};
}
}
and then your constructor is simply (Should be more complicated: SFINAE for nearly copy constructor with forwarding reference to avoid current copies):
template <typename...Args>
Odometer(Args... args) : disks{as_vector<Ts>(args)...} {}
Demo
Notice that as_vector<Ts>(args)... uses both packs with a single ..., so they should have same size.

Because you are handling the case where Ts... is a pack of one differently, it would be sensible to make that a specialisation.
Then you can expand both packs in one ... for the general case, and you only have one pack for the special case.
template <typename T, typename Arg>
std::vector<T> as_vector(Arg&& arg)
{
if constexpr (std::ranges::range<std::decay_t<Arg>>) {
return {std::begin(arg), std::end(arg)};
} else {
return {std::forward<Arg>(arg)};
}
}
template<typename...Ts>
struct Odometer {
std::tuple<std::vector<Ts>...> disks{};
template <typename...Args>
requires (sizeof...(Args) == sizeof...(Ts))
Odometer(Args&&... args) : disks{as_vector<Ts>(std::forward<Args>(args))...} {}
};
template<typename T>
struct Odometer<T> {
std::vector<std::vector<T>> disks{};
template <typename...Args>
Odometer(Args&&... args) : disks{as_vector<T>(std::forward<Args>(args))...} {}
};

Related

How can I send c++ template to a wrapped element in parameter pack?

now I have a special requirement.
the quantity of parameters handled by the function is not constant, so I have to use parameter pack in C++
the parameter is std::pair<std:string, template>, but different argument has different type of template, so the args would be like std::pair<std::string, int>, std::pair<std::string, bool>, .... Actually, I need to evaluate the type of each argument and perform different branch by the type. And in C++ only template can transfer typename as argument.
an iterable class would be one of input arguments, so I hope to use the size of this argument instead of the quantity of variable parameters.
so the function would be similar like this:
template<typename... T>
template<typename pair_t> std::pair<std::string, T>
std::vector<ret_class> my_fun(const iterable_class &in_obj, pair_t &...type)
{
std::vector<ret_class> ret;
int i=0;
for(auto arg:type) // ergodic all variable parameters, but I hope to use the iterable_class.size() as the index limit.
{
ret.push(handle_fun<arg.second>(iterable_class[i])); // arg.second would change the behavior of handle_fun
++i;
}
return ret;
}
but it can't pass compile.
Can anybody help me to clarify this issue?
This looks like a zip operation. Given a parameter pack T0...Ti, you want to create a parameter pack std::pair<std::string, T0> ... std::pair<std::string, Ti>.
The declaration would be straightforward:
template<typename... T>
std::vector<ret_class> my_fun
(const iterable_class &in_obj, std::pair<std::string, T>&...type)
{ /****/ }
std::pair<std::string, T>& is a pattern containing the template pack identifier T, so it's expanded.
The for(auto arg:type) bit is more puzzling. You can't loop over a heterogeneous set of arguments. What type would auto even deduce?
It might just be easier to write this recursively
template<typename HEAD, typename... Tail>
std::vector<ret_class> my_fun
(const iterable_class &in_obj, std::pair<std::string, HEAD> head, Tail&...tail)
{
auto in_head = iterable_class.front();
iterable_class.pop_front()
auto ret = my_fun(iterable_class, tail...);
ret.push_front(handle_fun<HEAD>(in_head));
return ret;
}
}
return ret;

Calling a common method of tuple elements

Say I have a tuple of types T1,...,TN that implement some method, apply().
How do I define a function that takes this tuple and some initial element, and returns the chained call of apply() on this element?
For example:
template <typename... Args, typename Input>
auto apply(std::tuple<Args...> const &tpl, Input x) {
// return ???
}
// simple example
struct Sqr {
static int apply(int x) { return x * x; }
};
enum class Choice {
One,
Two,
};
struct Choose {
static int apply(Choice choice) {
switch (choice) {
case Choice::One:
return 1;
case Choice::Two:
return 2;
}
}
};
void test() {
auto tpl = std::tuple(Sqr{}, Choose{});
assert(apply(tpl, Choice::One) == 1);
assert(apply(tpl, Choice::Two) == 4);
}
I tried to use fold expressions, and variations of answers from: Template tuple - calling a function on each element but couldn't get anything to compile.
The main difference is that I need each invocation's result as the input for the next one.
Concretely, I tried the following, which failed because it calls each argument with the initial value:
template <typename... Args, typename Input>
auto apply(std::tuple<Args...> const &tpl, Input x) {
return std::apply([&x](auto &&... args) {
return (..., args.apply(x));
}, tpl);
}
Clarifications and assumptions:
I want the methods to be called in a specific order - last to first - similarly to mathematical function composition.
(f * g)(x) := f(g(x))
The input and output types of each tuple argument are not constricted. The only assumption is that consecutive arguments agree on the corresponding types.
There may be snazzier C++17 ways of doing it, but there is always good old-fashioned partially-specialized recursion. We'll make a struct that represents your recursive algorithm, and then we'll build a function wrapper around that struct to aid in type inference. First, we'll need some imports.
#include <tuple>
#include <utility>
#include <iostream> // Just for debugging later :)
Here's our structure definition.
template <typename Input, typename... Ts>
struct ApplyOp;
Not very interesting. It's an incomplete type, but we're going to provide specializations. As with any recursion, we need a base case and a recursive step. We're inducting on the tuple elements (you're right to think of this as a fold-like operation), so our base case is when the tuple is empty.
template <typename Input>
struct ApplyOp<Input> {
Input apply(Input x) {
return x;
}
};
In this case, we just return x. Computation complete.
Now our recursive step takes a variable number of arguments (at least one) and invokes .apply.
template <typename Input, typename T, typename... Ts>
struct ApplyOp<Input, T, Ts...> {
auto apply(Input x, const T& first, const Ts&... rest) {
auto tail_op = ApplyOp<Input, Ts...>();
return first.apply(tail_op.apply(x, rest...));
}
};
The tail_op is our recursive call. It instantiates the next version of ApplyOp. There are two apply calls in this code. first.apply is the apply method in the type T; this is the method you control which determines what happens at each step. The tail_op.apply is our recursive call to either another version of this apply function or to the base case, depending on what Ts... is.
Note that we haven't said anything about tuples yet. We've just taken a variadic parameter pack. We're going to convert the tuple into a parameter pack using an std::integer_sequence (More specifically, an std::index_sequence). Basically, we want to take a tuple containing N elements and convert it to a sequence of parameters of the form
std::get<0>(tup), std::get<1>(tup), ..., std::get<N-1>(tup)
So we need to get an index sequence from 0 up to N-1 inclusive (where N-1 is our std::tuple_size).
template <typename Input, typename... Ts>
auto apply(const std::tuple<Ts...>& tpl, Input x) {
using seq = std::make_index_sequence<std::tuple_size<std::tuple<Ts...>>::value>;
// ???
}
That complicated-looking type alias is building our index sequence. We take the tuple's size (std::tuple_size<std::tuple<Ts...>>::value) and pass it to std::make_index_sequence, which gives us an std::index_sequence<0, 1, 2, ..., N-1>. Now we need to get that index sequence as a parameter pack. We can do that with one extra layer of indirection to get type inference.
template <typename Input, typename... Ts, std::size_t... Is>
auto apply(const std::tuple<Ts...>& tpl, Input x, std::index_sequence<Is...>) {
auto op = ApplyOp<Input, Ts...>();
return op.apply(x, std::get<Is>(tpl)...);
}
template <typename Input, typename... Ts>
auto apply(const std::tuple<Ts...>& tpl, Input x) {
using seq = std::make_index_sequence<std::tuple_size<std::tuple<Ts...>>::value>;
return apply(tpl, x, seq());
}
The second apply is the one outside users call. They pass a tuple and an input value. Then we construct an std::index_sequence of the appropriate type and pass that to the first apply, which uses that index sequence to access each element of the tuple in turn.
Complete, runnable example
The main difference is that I need each invocation's result as the
input for the next one.
Apply fold-expression to assignment operator
template <typename... Args, typename Input>
auto my_apply(std::tuple<Args...> const &tpl, Input x) {
return std::apply([&x](auto... op) {
return ((x = op.apply(x)), ...);
}, tpl);
}
Demo
You can introduce an dummy variable for reverse order
template <typename... Args, typename Input>
auto my_apply(std::tuple<Args...> const &tpl, Input x) {
return std::apply([&x](auto... op) {
int dummy;
(dummy = ... = ((x = op.apply(x)), 0));
return x;
}, tpl);
}
Demo
One way without recursion is to use fold expression.
Unfortunately, there is no call composition operator folding.
But you might create custom type and divert regular operator:
template <typename T>
struct Wrapper
{
T t;
};
// Deduction guide, not needed in C++20
template <typename T> Wrapper(T) -> Wrapper<T>;
// Then the operator with changed semantic
template <typename T1, typename T2>
auto operator+(const Wrapper<T1>& lhs, const Wrapper<T2>& rhs)
{
return Wrapper{lhs.t.apply(rhs.t)};
}
template <typename T1, typename T2>
auto operator-(const Wrapper<T1>& lhs, const Wrapper<T2>& rhs)
{
return Wrapper{rhs.t.apply(lhs.t)};
}
// And now, the function with fol expression
template <typename... Args, typename Input>
auto my_apply(std::tuple<Args...> const &tup, Input x) {
return std::apply([&](auto&...args){
return (Wrapper<const Args&>{args} + ... + Wrapper<Input&>{x});
}, tup).t;
}
template <typename... Args, typename Input>
auto my_apply_rev(std::tuple<Args...> const &tup, Input x) {
return std::apply([&](auto&...args){
return (Wrapper<Input&>{x} - ... - Wrapper<const Args&>{args});
}, tup).t;
}
Usage similar to
// std::size(std::to_string(10 * 10));
my_apply(std::tuple{ LengthOp{}, ToStringOp{}, SquareOp{}}, 10);
my_apply_rev(std::tuple{ SquareOp{}, ToStringOp{}, LengthOp{}}, 10);
Demo

Capture template parameter of variadic templated arguments

I have the following (incomplete, not-working) definition:
template<typename T, std::function<Args(Context&)>... Funcs>
struct constructor
{
T construct(Context& ctx)
{
return T(Funcs(ctx)...);
}
};
What I want is a templated class - the first argument is the constructed type and all following are functions to be called, the user templates with std::functions that are then called to produce the values for the constructor of type T.
I don't see a possibility to make this code work, let beside capturing the return types of the functions. I want the user to use it like this:
std::function<int(Context&)> ind = [](Context&) {return 2;};
Constructor<int, ind> c;
// c.construct(...) returns 2 by calling the constructor int(int) with argument
// ind(ctx) - which returns 2.
This might be roughly what you're looking for. Keep in mind that a std::function can't be a template parameter.
template <typename R> using Generator = std::function<R (Context&)>;
template <typename T, typename Generators, std::size_t... Is>
T constructImpl(Context& ctx, const Generators& generators,
std::index_sequence<Is...>) {
return T(std::get<Is>(generators)(ctx)...);
}
template <typename T, typename... Args>
class Constructor {
std::tuple<Generator<Args>...> generators;
public:
Constructor(Generator<Args>... generators)
: generators(std::move(generators)...)
{}
T construct(Context& ctx) {
return constructImpl<T>(ctx, generators,
std::index_sequence_for<Args...>());
}
};
Usage:
Constructor<int, int> c([](Context&) { return 2; });
int i = c.construct(context);
assert(i == 2);
Types cannot depend on run time data.
Calling std::function<X(Y)> requires run time data. So your type cannot depend on std::function<X(Y)>, so the type cannot be used as a template parameter.
Now, it can depend on a pointer to a global object: that is interestingly enough not run time state as far as C++ is concerned.
As such, your design is fundamentally flawed.
If you want a function such that it returns 2, this works:
template<class...ignored>
struct Constructor {
template<class... also_ignored>
Constructor(also_ignored&&...) {}
template<class... also_ignored>
int construct(also_ignored&&...) { return 2; }
};
this will pass the unit tests described in your OP, with the exception that you cannot pass ind to Constructor as it is not legal. However, dropping it from the type signature doesn't matter.
If you want more power, we could do this:
template<class T, class... Functors>
struct Constructor {
T construct( Context& ctx ) {
return T( Functors{}( ctx )... );
}
};
in this case you need stateless function objects:
struct ind { int operator()(Context&)const{return 2;} };
much like how std::map requires stateless comparison objects.
If your function objects require state, then you need to store a copy of them for Constructor to access (possibly within Constructor), and you might need the tuple and indexes trick to store them. ("indexes trick" is a helpful google)
I think your Construct can just be a function:
template <typename T, typename... Funcs>
T construct(Context& ctx, Funcs... funcs) {
return T(funcs(ctx)...);
}
Whose usage could be in your example could be:
int x = construct<int>(ctx, [](Context& ) { return 2; });

Create functors from functions with unknown argument count

I wrote a program in C++ & boost. Is it possible to write a template class producing functors from functions with an unknown number of arguments, e.g. my_call<func>(vector<variant>), where fun can be bool fun(string) or bool fun(int, int, string), etc.?
First, it is important to recognize that boost::variant<> is a class template that requires the list of all the possible types it can hold. So, you won't have just a vector<variant>, but rather a vector<variant<string, double>>, or vector<variant<int, double, string, my_class>>, and you won't be able to mix them.
This made me think you might want to use boost::any rather than boost::variant<>. Thus, I present here a solution that works with boost::variant and can be slightly modified to use boost::any, so you can pick the version you prefer.
To begin with, I must admit that the solution is simple to use but not so simple to understand, so I will have to introduce some machinery first. This machinery is common to both the variant-based and the any-based solution.
//=============================================================================
// META-FUNCTIONS FOR CREATING INDEX LISTS
// The structure that encapsulates index lists
template <size_t... Is>
struct index_list
{
};
// Collects internal details for generating index ranges [MIN, MAX)
namespace detail
{
// Declare primary template for index range builder
template <size_t MIN, size_t N, size_t... Is>
struct range_builder;
// Base step
template <size_t MIN, size_t... Is>
struct range_builder<MIN, MIN, Is...>
{
typedef index_list<Is...> type;
};
// Induction step
template <size_t MIN, size_t N, size_t... Is>
struct range_builder : public range_builder<MIN, N - 1, N - 1, Is...>
{
};
}
// Meta-function that returns a [MIN, MAX) index range
template<size_t MIN, size_t MAX>
using index_range = typename detail::range_builder<MIN, MAX>::type;
The meta-class index_range allows defining compile-time sequences of integers. An interesting proposal have been made by Jonathan Wakely to standardize this kind of construct, so that this whole machinery would not be needed. For the moment, however, we have to hand code this as done above.
Now that we can build compile-time integer sequences, we can exploit variadic templates and argument unpacking to create a dispatching mechanism that translates a vector of variant arguments into a regular argument list. Notice how the concrete variant<> type must be provided as a template argument. This will not be needed for the solution based on any.
// Headers needed for the implementation of the dispatcher
#include <vector>
#include <functional>
#include <boost/variant.hpp>
// Just for convenience
using namespace std;
using boost::variant;
//============================================================================
// DISPATCHER IMPLEMENTATION
// Call dispatching mechanism: notice how the underlying variant type
// must be provided as a template argument (the first one)
template<typename VT, typename R, typename... Args>
struct dispatcher
{
template<typename F>
dispatcher(F f) : _f(f) { }
// The call operator which performs the variant dispatch
R operator () (vector<VT> const& v)
{
if (v.size() != sizeof...(Args))
{
// Wrong number of arguments provided!
return false;
}
// Delegates to internal function call: needed for deducing
// a sequence of integers to be used for unpacking.
index_range<0, sizeof...(Args)> indexes;
return do_call(v, indexes);
}
private:
// The heart of the dispatching mechanism
template<size_t... Is>
R do_call(vector<VT> const& v, index_list<Is...> indexes)
{
return _f((get_ith<Args>(v, Is))...);
}
// Helper function that extracts a typed value from the variant.
template<typename T>
T get_ith(vector<VT> const& v, size_t i)
{
return boost::get<T>(v[i]);
}
// Wrapper that holds the function to be invoked.
function<R(Args...)> _f;
};
// Helper function that allows deducing the input function signature
template<typename VT, typename R, typename... Args>
function<R (vector<VT> const&)> get_dispatcher(R (*f)(Args...))
{
dispatcher<VT, R, Args...> d(f);
return d;
}
Finally, a short demonstration of how you could use this. Suppose we have two test functions such as the ones below:
#include <iostream>
bool test1(string s, double d)
{
cout << s << " " << d << endl;
return true;
}
bool test2(int i1, int i2, string s1, string s2)
{
cout << i1 << " " << i2 << " " << s1 << " " << s2 << endl;
return true;
}
What we want is to invoke them by building a vector of variants and have it dispatched to the desired function. Once again, I must stress the fact that we need to specify the list of all the types our variant can hold. Here, I will assume these types are string, double, and int, but your program might work with different ones.
Also, the solution is based on std::function<> for realizing the type erasure that allows you creating functors of different types and yet invoke them uniformly. Thus, a convenience type definition for this std::function<> (which in turn depends on the variant<> type we use) is provided as well:
int main()
{
// A helper type definition for the variant
typedef variant<int, double, string> vt;
// A helper type definition for the function wrapper
typedef function<bool (vector<vt>)> dispatcher_type;
// Get a caller for the first function
dispatcher_type f1 = get_dispatcher<vt>(test1);
// Prepare arguments for the first function
vector<vt> v = {"hello", 3.14};
// Invoke the first function
f1(v);
// Get a caller for the second function
dispatcher_type f2 = get_dispatcher<vt>(test2);
// Prepare arguments for the second function
v.assign({1, 42, "hello", "world"});
// Invoke the second function
f2(v);
}
Since all dispatchers have type dispatcher_type, you can easily put them into a container. However, you must be aware of the fact that attempts to invoke a function with the wrong number of arguments will be detected only at run-time (it is impossible to know at compile-time how many elements an std::vector<> contains). Thus, proper care must be taken.
As promised, I will now slightly modify this solution to use boost::any rather than boost::variant. The advantage is that since boost::any can hold any value, it is not necessary to specify the list of the possible types which can be used as function arguments.
While the helper machinery is unchanged, the core dispatcher class template must be modified as follows:
#include <vector>
#include <functional>
#include <boost/any.hpp>
using namespace std;
using boost::any;
//=============================================================================
// DISPATCHER IMPLEMENTATION
template<typename R, typename... Args>
struct dispatcher
{
template<typename F>
dispatcher(F f) : _f(f) { }
// The call operator which performs the dispatch
R operator () (vector<any> const& v)
{
if (v.size() != sizeof...(Args))
{
// Wrong number of arguments provided!
return false;
}
// Delegates to internal function call: needed for deducing
// a sequence of integers to be used for unpacking.
index_range<0, sizeof...(Args)> indexes;
return do_call(v, indexes);
}
private:
// The heart of the dispatching mechanism
template<size_t... Is>
R do_call(vector<any> const& v, index_list<Is...> indexes)
{
return _f((get_ith<Args>(v, Is))...);
}
// Helper function that extracts a typed value from the variant.
template<typename T>
T get_ith(vector<any> const& v, size_t i)
{
return boost::any_cast<T>(v[i]);
}
// Wrapper that holds the function to be invoked.
function<R(Args...)> _f;
};
// Helper function
template<typename R, typename... Args>
function<R (vector<any> const&)> get_dispatcher(R (*f)(Args...))
{
dispatcher<R, Args...> d(f);
return d;
}
As you see, the VT template argument has vanished. In particular, it is possible to call get_dispatcher without explicitly specifying any template argument. Using the same test functions we have defined for the variant-based solution, here is how you would adapt the main() routine:
int main()
{
// Helper type definition
typedef function<bool (vector<any>)> dispatcher_type;
// Get a caller for the first function
dispatcher_type f1 = get_dispatcher(test1);
// Get a caller for the second function
dispatcher_type f2 = get_dispatcher(test2);
// Prepare arguments for the first function
vector<any> v = {string("hello"), 3.14};
// Invoke the first function
f1(v);
// Prepare arguments for the second function
v.assign({1, 42, string("hello"), string("world")});
// Invoke the second function
f2(v);
}
The only disadvantage is that with boost::any you cannot assign string literals explicitly, because string literals are of type char [], and arrays cannot be used to initialize objects of type any:
any a = "hello"; // ERROR!
Thus, you have to either wrap them into string objects, or explicitly convert them to a pointer to char const*:
any a = string("hello"); // OK
any b = (char const*)"hello"; // OK
If this is not a huge problem for you, it's probably better to go for this second solution.

How can I iterate over a packed variadic template argument list?

I'm trying to find a method to iterate over an a pack variadic template argument list.
Now as with all iterations, you need some sort of method of knowing how many arguments are in the packed list, and more importantly how to individually get data from a packed argument list.
The general idea is to iterate over the list, store all data of type int into a vector, store all data of type char* into a vector, and store all data of type float, into a vector. During this process there also needs to be a seperate vector that stores individual chars of what order the arguments went in. As an example, when you push_back(a_float), you're also doing a push_back('f') which is simply storing an individual char to know the order of the data. I could also use a std::string here and simply use +=. The vector was just used as an example.
Now the way the thing is designed is the function itself is constructed using a macro, despite the evil intentions, it's required, as this is an experiment. So it's literally impossible to use a recursive call, since the actual implementation that will house all this will be expanded at compile time; and you cannot recruse a macro.
Despite all possible attempts, I'm still stuck at figuring out how to actually do this. So instead I'm using a more convoluted method that involves constructing a type, and passing that type into the varadic template, expanding it inside a vector and then simply iterating that. However I do not want to have to call the function like:
foo(arg(1), arg(2.0f), arg("three");
So the real question is how can I do without such? To give you guys a better understanding of what the code is actually doing, I've pasted the optimistic approach that I'm currently using.
struct any {
void do_i(int e) { INT = e; }
void do_f(float e) { FLOAT = e; }
void do_s(char* e) { STRING = e; }
int INT;
float FLOAT;
char *STRING;
};
template<typename T> struct get { T operator()(const any& t) { return T(); } };
template<> struct get<int> { int operator()(const any& t) { return t.INT; } };
template<> struct get<float> { float operator()(const any& t) { return t.FLOAT; } };
template<> struct get<char*> { char* operator()(const any& t) { return t.STRING; } };
#define def(name) \
template<typename... T> \
auto name (T... argv) -> any { \
std::initializer_list<any> argin = { argv... }; \
std::vector<any> args = argin;
#define get(name,T) get<T>()(args[name])
#define end }
any arg(int a) { any arg; arg.INT = a; return arg; }
any arg(float f) { any arg; arg.FLOAT = f; return arg; }
any arg(char* s) { any arg; arg.STRING = s; return arg; }
I know this is nasty, however it's a pure experiment, and will not be used in production code. It's purely an idea. It could probably be done a better way. But an example of how you would use this system:
def(foo)
int data = get(0, int);
std::cout << data << std::endl;
end
looks a lot like python. it works too, but the only problem is how you call this function.
Heres a quick example:
foo(arg(1000));
I'm required to construct a new any type, which is highly aesthetic, but thats not to say those macros are not either. Aside the point, I just want to the option of doing:
foo(1000);
I know it can be done, I just need some sort of iteration method, or more importantly some std::get method for packed variadic template argument lists. Which I'm sure can be done.
Also to note, I'm well aware that this is not exactly type friendly, as I'm only supporting int,float,char* and thats okay with me. I'm not requiring anything else, and I'll add checks to use type_traits to validate that the arguments passed are indeed the correct ones to produce a compile time error if data is incorrect. This is purely not an issue. I also don't need support for anything other then these POD types.
It would be highly apprecaited if I could get some constructive help, opposed to arguments about my purely illogical and stupid use of macros and POD only types. I'm well aware of how fragile and broken the code is. This is merley an experiment, and I can later rectify issues with non-POD data, and make it more type-safe and useable.
Thanks for your undertstanding, and I'm looking forward to help.
If your inputs are all of the same type, see OMGtechy's great answer.
For mixed-types we can use fold expressions (introduced in c++17) with a callable (in this case, a lambda):
#include <iostream>
template <class ... Ts>
void Foo (Ts && ... inputs)
{
int i = 0;
([&]
{
// Do things in your "loop" lambda
++i;
std::cout << "input " << i << " = " << inputs << std::endl;
} (), ...);
}
int main ()
{
Foo(2, 3, 4u, (int64_t) 9, 'a', 2.3);
}
Live demo
(Thanks to glades for pointing out in the comments that I didn't need to explicitly pass inputs to the lambda. This made it a lot neater.)
If you need return/breaks in your loop, here are some workarounds:
Demo using try/throw. Note that throws can cause tremendous slow down of this function; so only use this option if speed isn't important, or the break/returns are genuinely exceptional.
Demo using variable/if switches.
These latter answers are honestly a code smell, but shows it's general-purpose.
If you want to wrap arguments to any, you can use the following setup. I also made the any class a bit more usable, although it isn't technically an any class.
#include <vector>
#include <iostream>
struct any {
enum type {Int, Float, String};
any(int e) { m_data.INT = e; m_type = Int;}
any(float e) { m_data.FLOAT = e; m_type = Float;}
any(char* e) { m_data.STRING = e; m_type = String;}
type get_type() const { return m_type; }
int get_int() const { return m_data.INT; }
float get_float() const { return m_data.FLOAT; }
char* get_string() const { return m_data.STRING; }
private:
type m_type;
union {
int INT;
float FLOAT;
char *STRING;
} m_data;
};
template <class ...Args>
void foo_imp(const Args&... args)
{
std::vector<any> vec = {args...};
for (unsigned i = 0; i < vec.size(); ++i) {
switch (vec[i].get_type()) {
case any::Int: std::cout << vec[i].get_int() << '\n'; break;
case any::Float: std::cout << vec[i].get_float() << '\n'; break;
case any::String: std::cout << vec[i].get_string() << '\n'; break;
}
}
}
template <class ...Args>
void foo(Args... args)
{
foo_imp(any(args)...); //pass each arg to any constructor, and call foo_imp with resulting any objects
}
int main()
{
char s[] = "Hello";
foo(1, 3.4f, s);
}
It is however possible to write functions to access the nth argument in a variadic template function and to apply a function to each argument, which might be a better way of doing whatever you want to achieve.
Range based for loops are wonderful:
#include <iostream>
#include <any>
template <typename... Things>
void printVariadic(Things... things) {
for(const auto p : {things...}) {
std::cout << p.type().name() << std::endl;
}
}
int main() {
printVariadic(std::any(42), std::any('?'), std::any("C++"));
}
For me, this produces the output:
i
c
PKc
Here's an example without std::any, which might be easier to understand for those not familiar with std::type_info:
#include <iostream>
template <typename... Things>
void printVariadic(Things... things) {
for(const auto p : {things...}) {
std::cout << p << std::endl;
}
}
int main() {
printVariadic(1, 2, 3);
}
As you might expect, this produces:
1
2
3
You can create a container of it by initializing it with your parameter pack between {}. As long as the type of params... is homogeneous or at least convertable to the element type of your container, it will work. (tested with g++ 4.6.1)
#include <array>
template <class... Params>
void f(Params... params) {
std::array<int, sizeof...(params)> list = {params...};
}
This is not how one would typically use Variadic templates, not at all.
Iterations over a variadic pack is not possible, as per the language rules, so you need to turn toward recursion.
class Stock
{
public:
bool isInt(size_t i) { return _indexes.at(i).first == Int; }
int getInt(size_t i) { assert(isInt(i)); return _ints.at(_indexes.at(i).second); }
// push (a)
template <typename... Args>
void push(int i, Args... args) {
_indexes.push_back(std::make_pair(Int, _ints.size()));
_ints.push_back(i);
this->push(args...);
}
// push (b)
template <typename... Args>
void push(float f, Args... args) {
_indexes.push_back(std::make_pair(Float, _floats.size()));
_floats.push_back(f);
this->push(args...);
}
private:
// push (c)
void push() {}
enum Type { Int, Float; };
typedef size_t Index;
std::vector<std::pair<Type,Index>> _indexes;
std::vector<int> _ints;
std::vector<float> _floats;
};
Example (in action), suppose we have Stock stock;:
stock.push(1, 3.2f, 4, 5, 4.2f); is resolved to (a) as the first argument is an int
this->push(args...) is expanded to this->push(3.2f, 4, 5, 4.2f);, which is resolved to (b) as the first argument is a float
this->push(args...) is expanded to this->push(4, 5, 4.2f);, which is resolved to (a) as the first argument is an int
this->push(args...) is expanded to this->push(5, 4.2f);, which is resolved to (a) as the first argument is an int
this->push(args...) is expanded to this->push(4.2f);, which is resolved to (b) as the first argument is a float
this->push(args...) is expanded to this->push();, which is resolved to (c) as there is no argument, thus ending the recursion
Thus:
Adding another type to handle is as simple as adding another overload, changing the first type (for example, std::string const&)
If a completely different type is passed (say Foo), then no overload can be selected, resulting in a compile-time error.
One caveat: Automatic conversion means a double would select overload (b) and a short would select overload (a). If this is not desired, then SFINAE need be introduced which makes the method slightly more complicated (well, their signatures at least), example:
template <typename T, typename... Args>
typename std::enable_if<is_int<T>::value>::type push(T i, Args... args);
Where is_int would be something like:
template <typename T> struct is_int { static bool constexpr value = false; };
template <> struct is_int<int> { static bool constexpr value = true; };
Another alternative, though, would be to consider a variant type. For example:
typedef boost::variant<int, float, std::string> Variant;
It exists already, with all utilities, it can be stored in a vector, copied, etc... and seems really much like what you need, even though it does not use Variadic Templates.
There is no specific feature for it right now but there are some workarounds you can use.
Using initialization list
One workaround uses the fact, that subexpressions of initialization lists are evaluated in order. int a[] = {get1(), get2()} will execute get1 before executing get2. Maybe fold expressions will come handy for similar techniques in the future. To call do() on every argument, you can do something like this:
template <class... Args>
void doSomething(Args... args) {
int x[] = {args.do()...};
}
However, this will only work when do() is returning an int. You can use the comma operator to support operations which do not return a proper value.
template <class... Args>
void doSomething(Args... args) {
int x[] = {(args.do(), 0)...};
}
To do more complex things, you can put them in another function:
template <class Arg>
void process(Arg arg, int &someOtherData) {
// You can do something with arg here.
}
template <class... Args>
void doSomething(Args... args) {
int someOtherData;
int x[] = {(process(args, someOtherData), 0)...};
}
Note that with generic lambdas (C++14), you can define a function to do this boilerplate for you.
template <class F, class... Args>
void do_for(F f, Args... args) {
int x[] = {(f(args), 0)...};
}
template <class... Args>
void doSomething(Args... args) {
do_for([&](auto arg) {
// You can do something with arg here.
}, args...);
}
Using recursion
Another possibility is to use recursion. Here is a small example that defines a similar function do_for as above.
template <class F, class First, class... Rest>
void do_for(F f, First first, Rest... rest) {
f(first);
do_for(f, rest...);
}
template <class F>
void do_for(F f) {
// Parameter pack is empty.
}
template <class... Args>
void doSomething(Args... args) {
do_for([&](auto arg) {
// You can do something with arg here.
}, args...);
}
You can't iterate, but you can recurse over the list. Check the printf() example on wikipedia: http://en.wikipedia.org/wiki/C++0x#Variadic_templates
You can use multiple variadic templates, this is a bit messy, but it works and is easy to understand.
You simply have a function with the variadic template like so:
template <typename ...ArgsType >
void function(ArgsType... Args){
helperFunction(Args...);
}
And a helper function like so:
void helperFunction() {}
template <typename T, typename ...ArgsType >
void helperFunction(T t, ArgsType... Args) {
//do what you want with t
function(Args...);
}
Now when you call "function" the "helperFunction" will be called and isolate the first passed parameter from the rest, this variable can b used to call another function (or something). Then "function" will be called again and again until there are no more variables left. Note you might have to declare helperClass before "function".
The final code will look like this:
void helperFunction();
template <typename T, typename ...ArgsType >
void helperFunction(T t, ArgsType... Args);
template <typename ...ArgsType >
void function(ArgsType... Args){
helperFunction(Args...);
}
void helperFunction() {}
template <typename T, typename ...ArgsType >
void helperFunction(T t, ArgsType... Args) {
//do what you want with t
function(Args...);
}
The code is not tested.
#include <iostream>
template <typename Fun>
void iteratePack(const Fun&) {}
template <typename Fun, typename Arg, typename ... Args>
void iteratePack(const Fun &fun, Arg &&arg, Args&& ... args)
{
fun(std::forward<Arg>(arg));
iteratePack(fun, std::forward<Args>(args)...);
}
template <typename ... Args>
void test(const Args& ... args)
{
iteratePack([&](auto &arg)
{
std::cout << arg << std::endl;
},
args...);
}
int main()
{
test(20, "hello", 40);
return 0;
}
Output:
20
hello
40