How to unpack tuple with unknown size? - c++

Given,
template<typename T>
void foo(T t)
{
std::tie(/*xxx*/)=t;
auto &[/*yyy*/]=t;
}
int main()
{
foo(forward_as_tuple(1,2,3));
foo(forward_as_tuple(1,2,3,4,5));
}
I want foo() to unpack the tuple that's passed to itself.
Can decomposition declarations with auto or std::tie(), handle the unknown tuple sizes like xxx - yyy up there?
If yes, how?
I'm trying to think of another ways, maybe all elements could be pushed back to a vector of that type, once they're got.
std::vector<T> bar;
size_t baz=std::tuple_size<T>::value; //useless because it can't be used as:
for(int i=0; i<baz; i++)
bar.push_back(std::get<i>(t)); //since i isn't constant
Using vectors was just a bad idea that failed.
How else can it be done?
What I'm trying to, shortly is; I want to get tuple elements in a for loop. That's why I think I need them to be extracted somehow.

How to unpack tuple with unknown size?
auto &[/*yyy*/]=t;
You can't.
maybe all elements could be pushed back to a vector of that type
Use std::apply:
std::vector<int> baz;
std::apply([&baz](auto... args) {
(baz.push_back(args), ...);
}, t);

What I'm trying to, shortly is; I want to get tuple elements in a for loop.
You don't need to unpack a tuple to iterate over it. Here's how I'd do it:
template <typename Integer, Integer ...I, typename F>
constexpr void constexpr_for_each(std::integer_sequence<Integer, I...>, F &&func)
{
(func(std::integral_constant<Integer, I>{}) , ...);
}
template <auto N, typename F>
constexpr void constexpr_for(F &&func)
{
if constexpr (N > 0)
{
constexpr_for_each(std::make_integer_sequence<decltype(N), N>{}, std::forward<F>(func));
}
}
template <typename T>
void foo(T t)
{
constexpr_for<std::tuple_size_v<T>>([&](auto index)
{
constexpr auto i = index.value;
std::cout << std::get<i>(t) << '\n';
});
}

Related

Call variadic templated function with arguments from a std::vector

I need to convert elements of a std::vector to types based on a template parameter and call a function with these parameters. In pseudocode:
template <typename T...>
void foo(std::vector<std::string> v) {
if (v.size() != sizeof...(T))
throw std::runtime_error("Bad");
bar(convert<T0>(v[0]), convert<T1>(v[1]), ..., convert<Tn>(v[n]));
}
My problem is how to obtain the element indices from the parameter pack, I think there will be some kind of a trick using fold expressions, but I can't figure it out.
If you know that the number of elements in a vector is equal to the parameter pack size, you can solve this problem by adding one level of indirection:
template<typename... T, std::size_t... is>
void foo_impl(const std::vector<std::string>& v, std::index_sequence<is...>) {
bar(convert<T>(v[is])...);
}
template<typename... T>
void foo(const std::vector<std::string>& v) {
assert(v.size() == sizeof...(T));
foo_impl<T...>(v, std::index_sequence_for<T...>{});
}
The idea here is to expand two packs, Ts... and is..., which have equal sizes, simultaneously.
C++20 solution:
template<typename... T>
void foo(const std::vector<std::string>& v) {
assert(v.size() == sizeof...(T));
[&v]<std::size_t... is>(std::index_sequence<is...>) {
bar(convert<T>(v[is])...);
}(std::index_sequence_for<T...>{});
}
You could solve this by using an std::integer_sequence to access the elements of the vector.
namespace detail
{
template <typename...T, size_t...I>
void foo(std::vector<std::string>& v, std::index_sequence<I...>) {
bar(convert<T>(v[I])...);
}
}
template <typename...T>
void foo(std::vector<std::string>& v) {
if (v.size() != sizeof...(T))
throw std::runtime_error("Bad");
detail::foo<T...>(v, std::index_sequence_for<T...>{});
}
On Godbolt: Link

Making an index sequence tuple

Is there a way to create index tuple with compile-time known size like
std::tuple<int, int, ...> tup = make_index_tuple(100); // -> tup == (0, 1, 2, ..., 99)
Maybe somehow using std::make_index_sequence?
There is a kind of similar question about uninitialized tuple type but with structures involved
EDIT
I am trying to test my hand-written string formatting function with a signature like this
template<class... Args>
std::string format(std::string_view fmt, const Args&... args)
so I implemented a test, that requires to pass a sequence of ints 0, 1, 2, 3, ..., 99 to args. If there is a way to create tuple like so, then I could use std::apply to pass required arguments. If there are other ways i'd be glad to hear them too :)
pass a sequence of ints 0, 1, 2, 3, ..., 99 to [function arguments]
You don't need tuples. Do this:
template <std::size_t ...I>
void foo(std::index_sequence<I...>)
{
format("foo", I...);
}
int main()
{
foo(std::make_index_sequence<42>());
}
If you insist on std::apply, it understands std::array out of the box. You just need to handle the first parameter separately, since it's not an int.
const int n = 32;
std::array<int, n> array;
for (int i = 0; i < n; i++)
array[i] = i;
std::apply([](auto ...params){format("foo", params...);}, array);
For educational purposes, here's the answer to the original question. This is how you make a tuple:
template <typename T, typename I>
struct n_tuple_helper {};
template <typename T, std::size_t ...I>
struct n_tuple_helper<T, std::index_sequence<I...>>
{
using type = std::tuple<std::enable_if_t<I || true, T>...>;
};
template <typename T, std::size_t N>
using n_tuple = typename n_tuple_helper<T, std::make_index_sequence<N>>::type;
Now, n_tuple<int, 3> expands to std::tuple<int, int, int>
Above have mentioned how you could accomplish your idea by creating std::array. In case you still want to see how you should create a tuple:
template<typename T, T ...I>
auto make_tuple_sequence_helper(std::integer_sequence<T, I...>)
{
return std::make_tuple(I...);
}
template<std::size_t I, typename T>
auto make_tuple_sequence()
{
return make_tuple_sequence_helper(std::make_integer_sequence<T, I>());
}
Then you call it in main:
auto int_tuple = make_tuple_sequence<5, int>();
// Equivalent to `std::make_tuple(0,1,2,3,4)`
auto long_tuple = make_tuple_sequence<5, long>();
// Equivalent to `std::make_tuple(0l,1l,2l,3l,4l)`
auto size_t_tuple = make_tuple_sequence<5, std::size_t>();
// Equivalent to `std::make_tuple(0z,1z,2z,3z,4z)`
Note that you could also remove the typename T, if you know you only want tuple<int...> :
template<int ...I>
auto make_tuple_sequence_helper(std::integer_sequence<int, I...>)
{
return std::make_tuple(I...);
}
template<std::size_t I,>
auto make_tuple_sequence()
{
return make_tuple_sequence_helper(std::make_integer_sequence<int, I>());
}
And in main would be:
auto int_tuple = make_tuple_sequence<5>();
While the accepted answer should work for you too, I think it can still be improved. Since your goal appears to be calling the format function with an array of arguments, we will try solving this issue directly.
// declaration of format
template <typename ...Args>
std::string format(std::string_view fmt, const Args &...args);
// variadic template which uses type deduction to obtain the indices as template parameters
template <typename T, std::size_t ...I>
std::string format_arr_impl(std::string_view fmt,
T arr[sizeof...(I)],
std::index_sequence<I...>)
{
return format(fmt, arr[I]...);
}
// wrapper function, so that we don't have to call `std::make_index_sequence` each time
// this accepts a builtin array, but we can also add an overload for std::array
template <typename T, std::size_t N>
std::string format_arr(std::string_view fmt, T (&arr)[N])
{
return format_arr_impl(fmt, arr, std::make_index_sequence<N>());
}
// we can now call format for an array very easily
int main()
{
int data[] {11, 22, 33};
format_arr("{} {} {}", data);
}
Or alternatively, if you really insist on creating a tuple, we can also do the following:
// make use of the same index_sequence trick to deduce the indices
template <typename T, std::size_t ...I>
auto tuple_from_arr_impl(T arr[sizeof...(I)], std::index_sequence<I...>)
{
return std::make_tuple(arr[I]...);
}
// once again, we could also use a std::array instead of a reference to a builtin array
template <typename T, std::size_t N>
auto tuple_from_arr(std::string_view fmt, T (&arr)[N])
{
return tuple_from_arr_impl(fmt, arr, std::make_index_sequence<N>());
}
With the latter approach, we obtain a tuple where each member is an element of the array. We could then use std::apply to print the members.

std::tuple unpack over multiple arguments

I have a function taking pairs of (argument type, data ptr) as a variadic list (C function). I would like to unpack the tuple into that function as follows:
foo(TypeIndex<std::tuple_element_t<I, Tuple>>(), &std::get<I>(tuple));
thus I wrote a following function:
template<typename Tuple, size_t ...I>
void doUnpack(Tuple const& tp, std::index_sequence<I...>)
{
foo((type<std::tuple_element_t<I, Tuple>>(), std::get<I>(tp))...);
}
the only problem is comma operator ignores everything on the left uses the right hand side. Imagine type<> function returns 0 for now, so the above evaluates (using input tuple{1,2,3,4,5}) to foo(1,2,3,4,5) instead of foo(0,1, 0,2, 0,3, 0,4, 0,5)
Is there any way to accomplish this?
Code to reproduce:
template<typename Tp>
int type() { return 0; }
template<typename ...Args>
void fun(Args&& ...args)
{
(std::cout << ... << args) << std::endl;
}
template<typename Tuple, size_t ...I>
void doUnpack(Tuple const& tp, std::index_sequence<I...>)
{
fun((type<std::tuple_element_t<I, Tuple>>(), std::get<I>(tp))...);
}
int main()
{
doUnpack(std::tuple{1,2,3,4,5}, std::make_index_sequence<5>{});
return 0;
}
The following does what you want:
#include <tuple>
#include <iostream>
template<typename Tp>
int type() {
return 0;
}
template<typename Tuple, size_t... I>
auto doUnpack(Tuple const &tp, std::index_sequence<I...>) {
auto fun = [](auto &&...args) { (std::cout << ... << args) << std::endl; };
std::apply(fun, std::tuple_cat(std::pair{type<std::tuple_element_t<I, Tuple>>(), std::get<I>(tp)}...));
}
int main() {
doUnpack(std::tuple{1, 2, 3, 4, 5}, std::make_index_sequence<5>{});
return 0;
}
We use two nice STL-functions. std::tuple_cat takes a bunch of tuples (here pairs of the type and the value of the tuple) and concatenates them to one big tuple. So you go from (type0, val0), (type1, val1)... to (type0, val0, type1, val1, ...). This goes around the problem with the comma operator. After that, we apply the function to that with std::apply. Note that I use a lambda since std::apply needs (basically) a callable with a type and a function template does not qualify for that (whereas a lambda is (basically) a struct with templated operator() which works for that.

Avoiding code duplication for runtime-to-compile-time numeric parameter translation

Suppose we have function such as
template <typename T, unsigned N> void foo();
and for simplicity assume that we know that only (constant) values N_1, N_2 ... N_k are valid for N.
Now, suppose I want to make that compile-time parameter a run-time one, using foo() as a black-box, i.e. implement:
template <typename T> void foo(unsigned n);
by making foo<,>() calls. How should I go about doing that? Obviously, I can write:
template <typename T> void foo(unsigned n) {
switch(n) {
case N_1 : foo<T, N_1>(); break;
case N_2 : foo<T, N_2>(); break;
// etc. etc.
case N_k : foo<T, N_k>(); break;
}
}
... but this makes me feel all dirty. I could use a MAP() meta-macro to generate these k lines, I suppose; but can I do anything better and less-macroish to achieve the same? Is it possible to write something like the above that's general, and works for every variadic template and a fixed sequence of constant values?
Notes:
C++11/14/17-specific suggestions are obviously welcome.
The N's are not necessarily contiguous, nor small, nor sorted. e.g. suppose N_2 = 123456789 and N_5 = 1.
You could make a function pointer table:
using F = void(*)();
template <class T, class >
struct Table;
template <class T, size_t... Is>
struct Table<T, std::index_sequence<Is...> > {
static constexpr F fns[] = {
foo<T, Is>...
};
};
template <class T, size_t... Is>
constexpr F Table<T, std::index_sequence<Is...> >::fns[sizeof...(Is)];
And then just invoke the one you want:
template <class T, size_t N>
struct MakeTable : Table<T, std::make_index_sequence<N>> { };
template <typename T>
void foo(unsigned n) {
MakeTable<T, MaxN>::fns[n]();
}
If the N_ks aren't contiguous, then we can use a lambda for inline parameter unpacking:
template <class T>
void foo(unsigned n) {
using seq = std::index_sequence<N_1, N_2, ..., N_k>;
indexer(seq)([n](auto i){
if (n == i) {
f<T, i>();
}
});
}
If the above is too slow, then I guess just manually build a std::unordered_map<unsigned, void(*)()> or something.
In these kind of situations I like to build a static table of function pointers, with a dynamic parameter deciding which one to dispatch to. Below is an implementation that achieves this, in the function foo_dynamic. To this function, you specify the maximum value of N you'd like to support, and it builds a static table of function pointers using some recursive templates. You then dereference into this table with your dynamic parameter.
using ftype = void (*)();
template <typename T, unsigned N> void foo()
{
std::cout << N << std::endl;
}
template <typename T, unsigned max>
struct TablePopulator
{
static void populateFTable(ftype* table)
{
table[max] = foo<T,max>;
TablePopulator<T,max-1>::populateFTable(table);
}
};
template <typename T>
struct TablePopulator<T, 0>
{
static void populateFTable(ftype* table)
{
table[0] = foo<T,0>;
}
};
template<typename T, unsigned max_N>
std::array<ftype, max_N>& initTable()
{
static std::array<ftype, max_N> table;
TablePopulator<T, max_N-1>::populateFTable(table.data());
return table;
}
template<typename T, unsigned max_N>
void foo_dynamic(unsigned actualN)
{
static auto ftable = initTable<T, max_N>();
if(actualN >= max_N)
throw std::runtime_error("Max param exceeded");
ftable[actualN]();
}
int main()
{
foo_dynamic<int, 10>(1);
foo_dynamic<int, 10>(5);
return 0;
}
EDIT: Given the constraints in the question edit, here's an approach where valid indices are specified manually, which uses an unordered_map instead of an array:
using ftype = void (*)();
template <typename T, unsigned N> void foo()
{
std::cout << N << std::endl;
}
template<typename T, size_t ... Indices>
void foo_dynamic_indices(size_t actual_index)
{
static std::unordered_map<size_t, ftype> fmap = {{Indices, foo<T,Indices>}...};
auto fIt = fmap.find(actual_index);
if(fIt == fmap.end())
throw std::runtime_error("Index not found");
fIt->second();
}
int main()
{
foo_dynamic_indices<int, 0, 3, 400, 1021, 10000000>(10000000);
foo_dynamic_indices<int, 0, 3, 400, 1021, 10000000>(4); //Exception
return 0;
}

Is it possible to iterate over all elements in a struct or class?

Is it possible to iterate over all elements in a struct or class?
For example if I have a struct of three elements of different type:
struct A {
classA a;
classB b;
classC c;
};
then I need some iterator such that a method next() would give me the value
of the next element. The problem is that as you see, the values have different types.
Nope, not with the language as it is.
You could do it by deriving your classes from a common base, and then implementing your own iterator to return pointers to each item as the iterator is traversed.
Alternatively put the items in a std::vector and use that to provide the iteration.
No, there is no reflection in C++, (yet, there are murmurs about static reflection coming one day).
Anyway, there is a way to work around this, to an extent - first of all, you'll need a (temporary) tuple with references to your data members.
Then you will need a construct "iterating" over the tuple, such as:
void applyToAll() { }
template <typename Lambda, typename... Lambdas>
void applyToAll(Lambda&& closure, Lambdas&&... closures) {
std::forward<Lambda>(closure)();
applyToAll(std::forward<Lambdas>(closures)...);
}
// use your favourite sequence-making trick
template <unsigned... Is>
struct _Sequence {
typedef _Sequence<Is...> type;
};
template <unsigned Max, unsigned... Is>
struct _MakeSequence : _MakeSequence<Max - 1, Max - 1, Is...> { };
template <unsigned... Is>
struct _MakeSequence<0, Is...> : _Sequence<Is...> { };
template <typename Tuple, typename Functor, unsigned... Is>
void _foreachElemInTuple(_Sequence<Is...>, Tuple&& t, Functor&& f) {
applyToAll(
[&]{ std::forward<Functor>(f)(std::get<Is>(std::forward<Tuple>(t))); }...
);
}
template <typename Tuple, typename Functor>
void foreachElemInTuple(Tuple&& t, Functor&& f) {
_foreachElemInTuple(
_MakeSequence<std::tuple_size<
typename std::decay<Tuple>::type>::value>(),
std::forward<Tuple>(t), std::forward<Functor>(f)
);
}
Then you can call foreachElemInTuple(yourTuple, some_adapter()).
Your adapter will look like:
struct some_adapter {
template <typename... Args>
// A little bit of C++14, you can also just -> decltype the thing
decltype(auto) operator()(Args&& ... args) const {
return doStuff(std::forward<Args>(args)...);
}
};
As everyone else says, you cannot directly iterate over data members of a
class. However, it is not difficult to do it indirectly, provided of course that
you can access each of the data members you want to iterate over. The idea
in essense, as per ScarletAmaranth's solution, is to iterate over an std::tuple
of references to those data members.
The following program shows how to obtain such a tuple, using std::forward_as_tuple,
and another way to do the iterating by compiletime recursion, without
auxiliary apparatus.
#include <tuple>
/* You want to be able do something with the values of the members of an `A`
in turn.
*/
struct A
{
char ch;
int i;
double d;
// May also have members of class type. It doesn't matter
};
/* 1) Provide yourself with the means of creating a sequence that contains
references to the data members of a given `A`
*/
std::tuple<char const &, int const &, double const &> get_A_vals(A const & a)
{
return std::forward_as_tuple(a.ch,a.i,a.d);
}
/* 2) Provide yourself with a means of applying some operation, `Func`,
to each element of an `std::tuple`
*/
template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I == sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> const &, Func) {}
template<size_t I = 0, typename Func, typename ...Ts>
typename std::enable_if<I < sizeof...(Ts)>::type
for_each_in_tuple(std::tuple<Ts...> const & tpl, Func func)
{
func(std::get<I>(tpl));
for_each_in_tuple<I + 1>(tpl,func);
}
/* 3) Combine 1) and 2) to apply `Func` over the members of an `A`
*/
template<typename Func>
void for_each_in_A(A const & a, Func func)
{
for_each_in_tuple(get_A_vals(a),func);
}
// Testing...
#include <iostream>
// A specimen operation: just prints its argument
struct printer
{
template<typename T>
void operator () (T && t)
{
std::cout << t << std::endl;
}
};
int main()
{
A a{'a',1,2.0};
for_each_in_A(a,printer());
return 0;
}
// EOF
The program outputs:
a
1
2
If you have control of the structs or classes over whose members you need to
iterate, you may consider whether it is practical simply to dispense with them
and use the corresponding std::tuples everywhere.
Code built with gcc 4.8.2 and clang 3.3, -std=c++11.