Calling a Function for Each Variadic Template Argument and an Array

Calling a function for each variadic template argument and an array

You could refactor or wrap f to return a new X instead of having it passed, since this would play pack expansion into the hand and make the function really concise:

template<class T>
X fw(T const& t){ X x; f(x, t); return x; }

template<class... Args>
void h(Args... args){
X xs[] = { fw(args)... };
g(xs, sizeof...(Args));
}

Live example.

And if you could change g to just accept an std::initializer_list, it would get even more concise:

template<class... Args>
void h(Args... args){
g({f(args)...});
}

Live example. Or (maybe better), you could also provide just a wrapper g that forwards to the real g:

void g(X const*, unsigned){}

void g(std::initializer_list<X> const& xs){ g(xs.begin(), xs.size()); }

template<class... Args>
void h(Args... args){
g({f(args)...});
}

Live example.

Edit: Another option is using a temporary array:

template<class T>
using Alias = T;

template<class T>
T& as_lvalue(T&& v){ return v; }

template<class... Args>
void h(Args... args){
g(as_lvalue(Alias<X[]>{f(args)...}), sizeof...(Args));
}

Live example. Note that the as_lvalue function is dangerous, the array still only lives until the end of the full expression (in this case g), so be cautious when using it. The Alias is needed since just X[]{ ... } is not allowed due to the language grammar.

If all of that's not possible, you'll need recursion to access all elements of the args pack.

#include <tuple>

template<unsigned> struct uint_{}; // compile-time integer for "iteration"

template<unsigned N, class Tuple>
void h_helper(X (&)[N], Tuple const&, uint_<N>){}

template<unsigned N, class Tuple, unsigned I = 0>
void h_helper(X (&xs)[N], Tuple const& args, uint_<I> = {}){
f(xs[I], std::get<I>(args));
h_helper(xs, args, uint_<I+1>());
}

template<typename... Args>
void h(Args... args)
{
static constexpr unsigned nargs = sizeof...(Args);
X xs[nargs];

h_helper(xs, std::tie(args...));

g(xs, nargs);
}

Live example.

Edit: Inspired by ecatmur's comment, I employed the indices trick to make it work with just pack expansion and with f and g as-is, without altering them.

template<unsigned... Indices>
struct indices{
using next = indices<Indices..., sizeof...(Indices)>;
};
template<unsigned N>
struct build_indices{
using type = typename build_indices<N-1>::type::next;
};
template <>
struct build_indices<0>{
using type = indices<>;
};
template<unsigned N>
using IndicesFor = typename build_indices<N>::type;

template<unsigned N, unsigned... Is, class... Args>
void f_them_all(X (&xs)[N], indices<Is...>, Args... args){
int unused[] = {(f(xs[Is], args), 1)...};
(void)unused;
}

template<class... Args>
void h(Args... args){
static constexpr unsigned nargs = sizeof...(Args);
X xs[nargs];
f_them_all(xs, IndicesFor<nargs>(), args...);
g(xs, nargs);
}

Live example.

How to call a function on all variadic template args?

C++17 fold expression

(f(args), ...);

If you call something that might return an object with overloaded comma operator use:

((void)f(args), ...);

Pre-C++17 solution

The typical approach here is to use a dumb list-initializer and do the expansion inside it:

{ print(Args)... }

Order of evaluation is guaranteed left-to-right in curly initialisers.

But print returns void so we need to work around that. Let's make it an int then.

{ (print(Args), 0)... }

This won't work as a statement directly, though. We need to give it a type.

using expand_type = int[];
expand_type{ (print(Args), 0)... };

This works as long as there is always one element in the Args pack. Zero-sized arrays are not valid, but we can work around that by making it always have at least one element.

expand_type{ 0, (print(Args), 0)... };

We can make this pattern reusable with a macro.

namespace so {
using expand_type = int[];
}

#define SO_EXPAND_SIDE_EFFECTS(PATTERN) ::so::expand_type{ 0, ((PATTERN), 0)... }

// usage
SO_EXPAND_SIDE_EFFECTS(print(Args));

However, making this reusable requires a bit more attention to some details. We don't want overloaded comma operators to be used here. Comma cannot be overloaded with one of the arguments void, so let's take advantage of that.

#define SO_EXPAND_SIDE_EFFECTS(PATTERN) \
::so::expand_type{ 0, ((PATTERN), void(), 0)... }

If you are paranoid afraid of the compiler allocating large arrays of zeros for naught, you can use some other type that can be list-initialised like that but stores nothing.

namespace so {
struct expand_type {
template <typename... T>
expand_type(T&&...) {}
};
}

C++ Template Variadic - Call a member function once for every template argument

in C++17, you may do:

bool calcResult()
{
return (calcSingleResult<Args>() && ...);
}

In c++11, you have to have other methods:

template <typename T>
bool calcImpl()
{
return calcSingleResult<T>();
}

template <typename T, typename T2, typename...Ts>
bool calcImpl()
{
return calcSingleResult<T>() && calcImpl<T2, Ts...>();
}

bool calcResult()
{
return calcImpl<Args...>();
}

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

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.

How to convert variadic function arguments to array?

You can't convert a parameter pack into an array. You could create an array from one, but that would copy, and that would be wasteful. Instead, we can "convert" the parameter pack into a tuple of references, and then use get to index into that tuple. That would look like

template <typename... Ta>
Colordata(Ta... args)
{
constexpr std::size_t n = sizeof...(Ta);
static_assert(n >= 1, "must pass at least one argument");
std::cout << n << std::endl;
auto& tuple = std::tie(args...);
dataFirst = std::get<0>(tuple)
dataLast = std::get<n - 1>(tuple);
}

how to call member functions when the objects is variable template parameters

In C++17, you can use a fold expression:

template <typename ...A>
void f(A... args)
{
(..., args->memfunc("examples")); // the code not right!
}

Specifying one type for all arguments passed to variadic function or variadic template function w/out using array, vector, structs, etc?

You can just accept the arguments by the variadic template and let typechecking check the validity later on when they are converted.

You can check convertibility on the function interface level though, to make use of overload resolution for rejecting outright wrong arguments for example, by using SFINAE

template<typename R, typename...> struct fst { typedef R type; };

template<typename ...Args>
typename fst<void,
typename enable_if<
is_convertible<Args, ToType>::value
>::type...
>::type
f(Args...);

For your use-case if you know the steps to go from an std::array<> to your dragon_list_t then you have already solved it though according to the first option above ("convert-later"):

template<typename ...Items>
dragon_list_t make_dragon_list(Items... maidens) {
std::array<Maiden, sizeof...(Items)> arr = {{ maidens ... }};
// here be dragons
}

If you combine this with the above is_convertible approach you have a reject-early template that also does overload resolution on arguments and rejects them if not applicable.

Unpacking variadic template arguments into array applying function for each type

Just another attempt to find a work-around (too long for a comment, so I'll just post it as an answer):

struct FooNull {};

template<typename T> Foo* FuncWrapper() { return Func<T>(); }
template<> Foo* FuncWrapper< FooNull >() { return nullptr; }

template<typename... TArgs>
void SomeFuncImpl() {
Foo* data[] = {
FuncWrapper<TArgs>()...
};
}

template<typename... TArgs>
void SomeFunc() {
SomeFuncImpl<TArgs...,FooNull>();
}

How to assign variadic template arguments to std::array

Sth like this:

template<class T, size_t N, class ... Values>
void assign_values(std::array<T,N>& arr, Values... vals) {
static_assert(N == sizeof...(vals));
int j = 0;
for (auto i : std::initializer_list< std::common_type_t<Values...> >{vals...})
arr[j++] = i;
}

Demo

Executing function for each packed parameter in variadic template

  1. How, exactly, does the line work? How come parameter unpacking is allowing that function to execute for each parameter in the variadic template?

A parameter pack expansion is some pattern involving a parameter pack followed by ...

So expr(T)... is a pack expansion with expr(T) as its pattern, and it expands to expr(T0), expr(T1), expr(T2), ..., expr(TN) for each Ti in the parameter pack.

A pack expansion can only be used in certain contexts, such as argument lists, or initializer lists, so in this case the results of each sub-expression is being used to form an initializer list for the array int _[]. The array is unused and only exists so that its initializer can be used as the context in which to do the pack expansion. Each sub-expression is of the form (SubscribeToType<Ti>(blah, blah), 0) which means the result of the function call is discarded and the expression has the value 0. This is a bit of a kluge to allow the pack expansion to produce a braced-init-list containing N integers, because that's what's needed to initialize the array.


  1. I am very certain this line could be replaced by a lambda. How could you replace the line with a lambda expression?

Why would you want to?

It could, but you'd need a very similar pack expansion in the lambda, so it wouldn't simplify anything.



Related Topics



Leave a reply



Submit