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 Iterate over a parameter pack
To use a std::tuple
to iterate over a parameter pack, you would usually use a std::index_sequence
to introduce a new pack of indices, and use a fold expression to do the actual iteration. Something like this:
template<typename F, typename F2, typename... Args, std::size_t... I>
void func_impl(F& f, F2& f2, std::tuple<Args...> params, std::index_sequence<I...>) {
// This would be a fold expression over a comma in C++17:
/*
(([&]{}(
f(std::get<I*2>(params));
f2(std::get<I*2+1>(params));
)), ...);
*/
// C++14 version
using consume = int[];
(void) consume{ 0, [&]{
f(std::get<I*2>(params));
f2(std::get<I*2+1>(params));
return 0;
}()... };
}
template<typename F, typename F2, typename... Args>
void func(F f, F2 f2, Args&&... args) {
static_assert(sizeof...(args) % 2 == 0, "Must pass a multiple of 2 args to func");
func_impl(
f, f2,
std::forward_as_tuple(std::forward<Args>(args)...),
std::make_index_sequence<sizeof...(args) / 2>{}
);
}
But you can also iterate with recursion, which might be easier in your case:
template<typename F, typename F2>
void func(F&& f, F2&& f2) {
// base case: do nothing
}
template<typename F, typename F2, typename Arg1, typename Arg2, typename... Args>
void func(F&& f, F2&& f2, Arg1&& arg1, Arg2&& arg2, Args&&... args) {
// recursive case
f(arg1);
f2(arg2);
func(std::forward<F>(f), std::forward<F2>(f2), std::forward<Args>(args)...);
}
Iterating over a parameter pack
In C++17, you need additional level of indirection to get a pack of indices to get a pack of elements at those indices:
template<typename... Args, std::size_t... Is>
auto get_tuple_impl(const std::vector<std::size_t>& indices,
std::index_sequence<Is...>,
const Args&... args) {
return std::make_tuple(args[indices[Is]]...);
}
template<typename... Args>
auto get_tuple(const std::vector<std::size_t>& indices, const Args&... args) {
return get_tuple_impl(indices, std::index_sequence_for<Args...>(), args...);
}
In C++20, we could you a lambda function with template parameters invoked in-place:
template<typename... Args>
auto get_tuple(const std::vector<std::size_t>& indices, const Args&... args) {
return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
return std::make_tuple(args[indices[Is]]...);
}(std::index_sequence_for<Args...>());
}
You might also want to add an assertion assert(indices.size() == sizeof...(Args));
or use std::array<std::size_t, N>
type instead.
How to iterate through variadic template parameters to create variable number of local variables?
First, don't take your names as a vector
. Take them as a parameter pack. And, moreover, take them as a pack of what you actually want:
bool is_valid() { return true; }
template <class T, class... Ts>
bool is_valid(T& arg, Ts&... args) {
return arg.valid() && is_valid(args...);
}
template <class T, class... Args>
T* create(Args&&... args) {
if (is_valid(args...)) {
return new T{args...};
}
else {
return nullptr;
}
}
And now just pass the right thing in:
create<T>(A{"nameA"}, B{"nameB"}, C{"nameC"});
create<T>(D{"nameD"}, E{"nameE"});
If, for some reason, you really want to separate the types and the names, you can do that too:
template <class T, class... Cs, class... As>
T* create_classes(As&&... args) {
return create<T>(Cs{std::forward<As>(args)}...);
}
How to iterate over Variadic template types (not arguments)?
In your example, in C++17, you might do:
template<typename... Ts>
class Subscriber
{
Subscriber()
{
auto f = [](auto data){ /* do something with data*/ };
(PubSub.Subscribe<Ts>(f), ...);
}
}
In C++11/14, you might to use more verbose way, such as:
(C++14 currently with your generic lambda)
template<typename... Ts>
class Subscriber
{
Subscriber()
{
auto f = [](auto data){ /* do something with data*/ };
int dummy[] = {0, (PubSub.Subscribe<Ts>(f), void(), 0)...};
static_cast<void>(dummy); // Avoid warning for unused variable.
}
}
How to iterate over the size of a parameter pack with a compile-time index parameter
You can define a helper function to use index_sequence
to expand the elements of Tuple
and assign values through fold expression.
template<class Tuple, class Message, std::size_t... Is>
void assign_tuple(Tuple& tuple, const Message& message, std::index_sequence<Is...>) {
((std::get<Is>(tuple) = message.getArg<std::tuple_element_t<Is, Tuple>(Is)), ...);
};
template <typename ...Args>
auto addCallback(void* handle, std::string address, std::function<void(Args...)> callback) {
return addMessageCallback(std::move(address), [callback](const ci::osc::Message& message) {
std::tuple<Args...> args;
assign_tuple(args, message, std::make_index_sequence<sizeof...(Args)>{});
std::apply(callback, std::move(args));
});
}
In C++20 you can just define template lambda inside the function to do this.
iterating over variadic template's type parameters
What Xeo said. To create a context for pack expansion I used the argument list of a function that does nothing (dummy
):
#include <iostream>
#include <initializer_list>
template<class...A>
void dummy(A&&...)
{
}
template <class ...A>
void do_something()
{
dummy( (A::var = 1)... ); // set each var to 1
// alternatively, we can use a lambda:
[](...){ }((A::var = 1)...);
// or std::initializer list, with guaranteed left-to-right
// order of evaluation and associated side effects
auto list = {(A::var = 1)...};
}
struct S1 { static int var; }; int S1::var = 0;
struct S2 { static int var; }; int S2::var = 0;
struct S3 { static int var; }; int S3::var = 0;
int main()
{
do_something<S1,S2,S3>();
std::cout << S1::var << S2::var << S3::var;
}
This program prints 111
.
How to iterate over variadic template pack during (pseudo?) runtime?
template<class=void, std::size_t...Is >
auto indexer( std::index_sequence<Is...> ){
return [](auto&&f)->decltype(auto){
return decltype(f)(f)( std::integral_constant<std::size_t, Is>{}... );
};
}
template<std::size_t N>
auto indexer(){
return indexer(std::make_index_sequence<N>{} );
}
template<std::size_t N>
void for_each( F&& f ) {
indexer<N>()( [&](auto...Is){
using discard=int[];
(void)discard{0,(void(
f(Is)
),0)...};
});
}
indexer
gives you unpacked indexes.
for_each
calls f
with a compile time i
value for each i
up to N
.
That will let you iterate over integers at compile time. To map integers at runtime to compile time:
template<std::size_t N, class F>
void pick( std::size_t I, F&& f ){
for_each<N>( [&](auto i){
if (I==i) f(i);
} );
}
This invokes f
with a compile time version of I
so long as it is less than N
.
template<class...Args>
void read( std::string pattern, Args&...args ){
auto tied=std::tie(args...);
for (i = 0; i < length; i = format.find(i, opening_bracket))
pick<sizeof...(args)>( i, [&](auto i){
std::cin>>std::get<i>(tied);
} );
}
}
Now there is an implicit chain of if
s written by the above; you can replace with a jump table using a different technique.
Code not compiled; design is sound, but there are probably tyops. Indexer can be found with google (I have written it on SO before). I have written it as for_each
directly, but I find the single pack version too useful. Here I needed the separate pack version. Pick just uses it.
Here is a jump table version of pick:
template<std::size_t N, class F>
void pick( std::size_t I, F&& f ){
indexer<N>()([&](auto...Is){
using table_f=void(*)(&f);
const table_f table[]={
+[](F&f){ f(decltype(Is){}); }...
};
table[I](f);
});
}
Bounds checking not included. This version does not require for_each
, but some compilers break when asked o have a lambda with a parameter pack unexpanded inside a statement.
Related Topics
Are Child Processes Created with Fork() Automatically Killed When the Parent Is Killed
Vector of Const Objects Giving Compile Error
Are Inner Classes in C++ Automatically Friends
C++ Function Pointer (Class Member) to Non-Static Member Function
Std::String::C_Str() and Temporaries
Are C++17 Parallel Algorithms Implemented Already
How to Run a Child Process That Requires Elevation and Wait
C++ Inline Member Function in .Cpp File
How to Embed a File into an Executable
What's This C++ Syntax That Puts a Brace-Surrounded Block Where an Expression Is Expected
About Binding a Const Reference to a Sub-Object of a Temporary
Does Std::List::Remove Method Call Destructor of Each Removed Element
Difference Between Pointer to a Reference and Reference to a Pointer