Get ordinal number of C++ variadic template parameter - c++

I want to retrieve index of each parameter from variadic template parameters, currently I am using this approach:
From third party library:
struct statement {
template <typename T>
void bind(int, T t) { // ....
}
};
My code:
template <unsigned Index, typename T>
void bind(statement& stmt, T t) {
stmt.bind(Index, t);
}
template <unsigned Index, typename T, typename... Args>
void bind(statement& stmt, T t, Args... args) {
bind<Index>(stmt, t);
bind<Index + 1>(stmt,args...);
}
template <typename... Args>
void bind_all(statement& stmt, Args... args) {
constexpr int Index = 0;
bind<Index>(stmt, args...);
}
Usage:
statement stmt;
prepare(stmt, "insert into tab (a,b,c,d,e,f) values(?,?,?,?,?,?)");
bind_all(stmt, 1,1.24f, 3.14, "Hello", std::string{"World"}, true);
My question: Is there a better way to achieve this, to get ordinal numbers of variadic template parameters?
Edit:
I want to use this implementation to wrap sql prepared statement and to bind specific parameter to specific index.
Here is an example of code that I want to wrap, instead of listing each bind individually, I want to call bind_all
prepare(stmt, "insert into tab (a, b) values (?, ?);");
const int eight_int = 8;
stmt.bind(0, &eight_int);
const string eight_str = "eight";
stmt.bind(1, eight_str.c_str());
execute(stmt);

With simple expansion (could use a fold expression in C++17)
struct statement
{
template<class T>
void bind(int index, T&& arg)
{
// magic
}
};
template<class... Args>
void BindAll(statement& stmt, Args&&... args)
{
using swallow = int[];
int idx = 0;
(void)swallow{0, (void (stmt.bind(idx++, std::forward<Args>(args))), 0)...};
}
I took some liberties with the API, but I think it maps close enough to your code.
Usage:
statement stmt;
BindAll(stmt, 1, 1.2, 1.3f, true, "abc");
Demo

Why not use a std::tuple for this.
#include <utility>
#include <tuple>
template<typename T, std::size_t... Index>
void doBind(Statement& st, T const& tuple, std::index_sequence<Index...> const&)
{
// Using C++17 fold expression
((st.bind(Index, std::get<Index>(tuple))),...);
// Using C++11 dummy variable
int dummy[] = {0, (st.bind(Index, std::get<Index>(tuple)),0)...};
(void)dummy; // to prevent unused variable warning.
}
template<typename... Args>
void prepare(std::string const& sql, Args&&... args)
{
Statement statement;
prepare(statement, sql);
doBind(statement, std::make_tuple(args...), std::make_index_sequence<sizeof...(args)>());
execute(statement);
}

You could use std::index_sequence to create a second template parameter pack containing the matching indices:
template <typename... Args, size_t... Is>
void bind_all_helper(std::index_sequence<Is...>, Args... args) {
int dummy[]{(bind<Is>(args), 0)...};
(void)dummy; // just to avoid unused variable warnings
}
template <typename... Args>
void bind_all(Args... args) {
bind_all_helper(std::make_index_sequence<sizeof...(args)>{}, args...);
}
Live Demo
This uses a dummy array definition along with the comma operator to create a sequence of calls to bind. For example, given bind_all(1, 3.14), the array definition would expand to something like this:
int dummy[] {
(bind<0>(1), 0),
(bind<1>(3.14), 0)
};
Each element of the array ends up being 0, but its evaluation has the side-effect of calling bind<N>(arg).
With C++17 the dummy array definition could be replaced with a fold expression, but that obviously isn't an option if you're limited to C++14.

Related

Variadic function that accepts arguments of same type at compile time and iterate on them

I am looking for a way to implement Variadic function that accepts arguments of same type at compile-time and should be able to iterate on them. The variadic parameters are at the end with all of them having the same type.
Something like below -
void SampleFunc(Other arguments(String may be)..., int... arg)
{
for (const auto& val : arg)
{
// Each argument available here.
}
}
then I will call this function like below -
SampleFunc("String", "{1,2,3,4})
Most important thing is that variadic parameters are hardcoded every time the function is called so I should be able to generate this Variadic argument at compile time.
Right now I am accepting function parameters as shown below -
void SampleFunc(std::string str, std::vector<int>& nums)
But this adds run time cost of constructing a vector every time function is called which I want to avoid.
UPDATE :-
I forgot to mention that this function has other parameters at the start. It is my bad, sorry about that. I have updated my example now.
If the arguments are known at compile-time, in c++17, using fold expressions you can do something like
#include <utility> // std::forward
template <typename Type>
void doSomething(Type&& arg) /* noexcept */
{
// Each argument available here.
std::cout << arg << "\n";
}
template <typename... Args>
void SampleFunc(Args&&... args) /* noexcept */
{
(doSomething(std::forward<Args>(args)), ...);
}
Now you could call the function like
SampleFunc(1, 2, 3, 4);
and using doSomething you can do something with each arguments.
(See a Demo Online)
In previous compilers, you could imitate the fold expression via expander trick, as follows
template <typename Type>
void doSomething(Type&& arg) /* noexcept */
{
// Each argument available here.
std::cout << arg << "\n";
}
template <typename... Args>
void SampleFunc(Args&&... args) /* noexcept */
{
using dummy = int[];
(void)dummy {
0, (doSomething(std::forward<Args>(args)), 0)...
};
}
(See a Demo Online)
Iterate on variadic arguments is the simplest part: you tagged C++17 so you can use template folding, as suggested by JeJo, or other ways (recursion, initialization of an unused array).
More complicated is impose that all the arguments are exactly of the same type.
Obviously you can use SFINAE to impose that the deduced type are of the same type, but if you pass arguments of the different types, by example
foo(1l, 2l, 3l, 4); // long, long, long, int
when an argument is convertible to the type of the others, the code doesn't compile.
If you accept to pass through an additional function and that your function is a method of a template struct, you can start with an using that select the type from a couple type/index
template <typename T, std::size_t>
using get_type = T;
you can write the template struct as follows
template <typename...>
struct bar;
template <typename T, std::size_t ... Is>
struct bar<T, std::index_sequence<Is...>>
{
void operator() (std::string const & str, get_type<T, Is> const & ... ts)
{ ((std::cout << ts << ' '), ..., (std::cout << '\n')); }
};
Observe that the arguments following str in the operator() are all of type T, where T is the first template argument of the struct.
The additional function is
template <typename T, typename ... Ts>
void foo (std::string const & str, Ts const & ... ts)
{ bar<T, std::index_sequence_for<Ts...>>{}(str, ts...); }
You can call foo() as follows
foo<int>("string", 1, 2, 3, 4l);
Observe that a long value (4l) is accepted because is converted to int.
You can also directly call the bar::operator(), if you prefer
bar<int, std::make_index_sequence<4u>>{}("string", 10, 20, 30, 40);
but you have to explicit the second template argument so there is some redundancies.
The following is a full compiling example
#include <string>
#include <utility>
#include <iostream>
template <typename T, std::size_t>
using get_type = T;
template <typename...>
struct bar;
template <typename T, std::size_t ... Is>
struct bar<T, std::index_sequence<Is...>>
{
void operator() (std::string const & str, get_type<T, Is> const & ... ts)
{ ((std::cout << ts << ' '), ..., (std::cout << '\n')); }
};
template <typename T, typename ... Ts>
void foo (std::string const & str, Ts const & ... ts)
{ bar<T, std::index_sequence_for<Ts...>>{}(str, ts...); }
int main ()
{
foo<int>("string", 1, 2, 3, 4l); // a long value is converted to int
bar<int, std::make_index_sequence<4u>>{}("string", 10, 20, 30, 40);
}
The variadic parameters are at the end with all of them having the same type.
Whereas std::vector might have the overhead of extra allocation, you might simply use std::initializer_list instead (of variadic).
void SampleFunc(std::string str, std::initializer_list<int>& nums)
{
for (int val : nums)
{
// Each argument available here.
}
}
With call similar to
SampleFunc("String", {1, 2, 3, 4});

Unrolling and forwarding arrays

I'm having trouble unrolling and forwarding a parameter pack of std::arrays to another function
Suppose we have a function that takes a single std::array and I want to unroll it and pass it as an argument to some other function I can do so by doing this:
template<typename T, typename...Ts>
void other_function(T, Ts...) { /* Do stuff with Ts */ }
template<typename T, size_t Size, size_t... I>
void forward_array(std::array<T, Size>& array_arg, std::index_sequence<I...>)
{
other_function(array_arg[I]...);
// for Size == 3 let's say we get the same thing as we would write
// other_function(array_arg[0], array_arg[1], array_arg[2]
// I skipped the std::forward
}
Now let's say we have a function that does this same thing, but it takes multiple arrays that can be of different size.
template<typename T, size_t... Sizes /*, size_t... I_Sequence0, size_t... I_Sequence1, ... I_SequenceN */>
void forward_many_arrays(std::array<T, Sizes>&... array_args /*, ???*/)
{
other_func( /* ??? */);
}
I want to unfold each array_arg and pass it to other_func, but how do I do that exactly here?. We would need a way to index into each array arg.
In my actual program, I have a class that has a member std::array of std::reference_wrapper which is not default constructable and I'm trying to provide an alternative constructor for that class that takes any number of arrays&, where the sum of their sizes matches the member array size and delegate it to the explicit constructor that takes T references, but I'm kind of stuck cause I don't know how to handle the unrolling.
You might have a "generic" getter
template <std::size_t I, typename Container, typename ... Containers>
decltype(auto) get(Container&& container, Containers&&...containers)
{
constexpr std::size_t size = std::tuple_size_v<std::decay_t<Container>>;
if constexpr (I < size) {
return container[I];
} else {
return get<I - size>(containers...);
}
}
Used like:
template <typename...Ts>
void other_function(Ts... ts) { ((std::cout << ts << " "), ...); }
template<typename... Ts, size_t... Is>
void forward_many_arrays(std::index_sequence<Is...>, Ts&&...ts)
{
other_function(get<Is>(ts...)...);
}
template<typename... Ts>
void forward_many_arrays(Ts&&...ts)
{
forward_many_arrays(std::make_index_sequence<(std::tuple_size_v<std::decay_t<Ts>> + ...)>(), ts...);
}
Demo
An implementation based on simple recursion:
template<std::size_t n, class Fn, class T, class... Ts>
void apply_all_impl(Fn fn, T& t, Ts&... ts) {
if constexpr (n == 0)
fn(t, ts...);
else
std::apply([&](auto&... args) {
apply_all_impl<n - 1>(fn, ts..., args...);
}, t);
}
template<class Fn, class... Ts>
void apply_all(Fn fn, Ts&... ts) {
apply_all_impl<sizeof...(Ts)>(fn, ts...);
}
Usage example:
std::array<int, 3> arr1{1, 2, 3};
std::array<int, 4> arr2{4, 5, 6, 7};
auto print_all = [](auto... ts) { (std::cout << ... << ts); };
apply_all(print_all, arr1, arr2); // Output: 1234567
Demo

Are fold expressions fully supported in VS2017?

I found an interesting article and tried its code with MSVS 2017:
#include <utility>
#include <tuple>
template <typename... Args, typename Func, std::size_t... Idx>
void for_each(const std::tuple<Args...>& t, Func&& f, std::index_sequence<Idx...>) {
f(std::get<Idx>(t))...;
}
template <typename... Args, typename Func>
void for_each(const std::tuple<Args...>& t, Func&& f) {
for_each(t, f, std::index_sequence_for<Args...>{});
}
template <typename T>
void Write(std::wostream & out, const T & t)
{
out << t;
}
template<typename ...Args>
void WriteV(std::wostream & out, Args&... args)
{
for_each(std::tuple<Args&...>(args...), [&out](auto& a) { Write(out, a); });
}
struct A
{
int n;
std::wstring s;
double d;
};
void main()
{
std::wostringstream out;
A a{ 1, std::wstring(L"2"), 3.0 };
WriteV(a.n, a.s, a.d);
}
, but the code did not compile with errors:
error C2760: syntax error: unexpected token '...', expected ';'
error C3520: 'Idx': parameter pack must be expanded in this context
does it mean that VS2017 does not fully support fold expressions?
This code needs just a couple of syntax fixes:
(f(std::get<Idx>(t)), ...);
and
WriteV(out, a.n, a.s, a.d);
Note that this code is unnecessary long for some reason. It can be replaced with just
template<typename ... Args>
void WriteV(std::wostream & out, Args const & ... args)
{
(out << ... << args);
}
This way is wrong
f(std::get<Idx>(t))...;
You have to choose.
(1) do you want to call f() only one time with all arguments? In this case you have to put the ellipsis ("...") inside the call
f(std::get<Idx>(t)...);
(2) or do you want (I suppose is the case, in your example) call f() with every argument (N argument, N calls)? In this case you can (starting from C++17) use template folding with comma operator adding a couple of parentheses
(f(std::get<Idx>(t) , ...);
// .^...................^....^ <- comma and parentheses
The second way, pre C++17, can be simulated inside the inizialization of an (usually unused) array. Something as follows
using unused = int[];
(void) unused { 0, ((void)f(std::get<Idx>(t)), 0)... };

how to assing multiple std::tuple_element as function arguments using variadic size_t templates

I want to create a function that changes multiple values inside a tuple with one call.
template<class... Args>
class EventN {
public:
std::tuple<Args...> mArgs;
EventN(Args... args) : mArgs(args) {}
EventN() {}
template<size_t N> void set(typename std::tuple_element<N-1,Tuple>::type value) {
std::get<N-1>(mArgs) = value; //I use N-1, to start from '1'
}
};
The set function above works as I expect it to:
auto event = new EventN<String,int>();
event->set<1>("testEvent");
event->set<2>(12);
Now, I want to extend the function to:
event->set<1,2>("testEvent", 12);
How can I achieve that? Is there a way to make std::tuple_element<N-1,Tuple>::type variadic?
Try something like this:
template <int ...N, typename ...Args>
void set_all(Args &&... args)
{
int dummy[] = { (set<N>(std::forward<Args>(args)), 0)... };
static_cast<void>(dummy); // unused
}
This works because the different packs are expanded in lockstep. This requires all packs to have the same size, which is enforced at compile time.
The dummy array and the comedic comma operator are just a sleazy way of evaluating all the expressions for their side effects' sake without caring for their value.
Here's a complete little demo:
#include <iostream>
#include <utility>
template <int N, typename T> void set(T && t)
{
std::cout << "Setting " << N << " => " << std::forward<T>(t) << "\n";
}
template <int ...N, typename ...Args>
void set_all(Args &&... args)
{
int dummy[] = { (set<N>(std::forward<Args>(args)), 0)... };
static_cast<void>(dummy);
}
int main()
{
set_all<2, 51, 1>("Hello", 100, true);
}
Observe that at this point there is no constraint on the values of the integers; this constraint only comes in later when you use the integer as a tuple index.

Calling a function for each variadic template argument and an array

So I have some type X:
typedef ... X;
and a template function f:
class <typename T>
void f(X& x_out, const T& arg_in);
and then a function g:
void g(const X* x_array, size_t x_array_size);
I need to write a variadic template function h that does this:
template<typename... Args>
void h(Args... args)
{
constexpr size_t nargs = sizeof...(args); // get number of args
X x_array[nargs]; // create X array of that size
for (int i = 0; i < nargs; i++) // foreach arg
f(x_array[i], args[i]); // call f (doesn't work)
g(x_array, nargs); // call g with x_array
}
The reason it doesn't work is because you can't subscript args like that at runtime.
What is the best technique to replace the middle part of h?
And the winner is Xeo:
template<class T> X fv(const T& t) { X x; f(x,t); return x; }
template<class... Args>
void h(Args... args)
{
X x_array[] = { fv(args)... };
g(x_array, sizeof...(Args));
}
(Actually in my specific case I can rewrite f to return x by value rather than as an out parameter, so I don't even need fv above)
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.
Nice template as answer for first part of question:
template <class F, class... Args>
void for_each_argument(F f, Args&&... args) {
[](...){}((f(std::forward<Args>(args)), 0)...);
}
It's obvious: you don't use iteration but recursion. When dealing with variadic templates something recursive always comes in. Even when binding the elements to a std::tuple<...> using tie() it is recursive: It just happens that the recursive business is done by the tuple. In your case, it seems you want something like this (there are probably a few typos but overall this should work):
template <int Index, int Size>
void h_aux(X (&)[Size]) {
}
template <int Index, int Size, typename Arg, typename... Args>
void h_aux(X (&xs)[Size], Arg arg, Args... args) {
f(xs[Index], arg);
h_aux<Index + 1, Size>(xs, args...);
}
template <typename... Args>
void h(Args... args)
{
X xs[sizeof...(args)];
h_aux<0, sizeof...(args)>(xs, args...);
g(xs, sizeof...(args));
}
I think you won't be able to use nargs to define the size of the array either: Nothing indicates to the compiler that it should be a constant expression.
It's fairly simple to do with parameter pack expansion, even if you can't rewrite f to return the output parameter by value:
struct pass { template<typename ...T> pass(T...) {} };
template<typename... Args>
void h(Args... args)
{
const size_t nargs = sizeof...(args); // get number of args
X x_array[nargs]; // create X array of that size
X *x = x_array;
int unused[]{(f(*x++, args), 1)...}; // call f
pass{unused};
g(x_array, nargs); // call g with x_array
}
It should be possible just to write
pass{(f(*x++, args), 1)...}; // call f
but it appears g++ (4.7.1 at least) has a bug where it fails to order the evaluation of brace-initializer-list parameters as class initialisers. Array initialisers are OK though; see Sequencing among a variadic expansion for more information and examples.
Live example.
As an alternative, here's the technique mentioned by Xeo using a generated index pack; unfortunately it does require an extra function call and parameter, but it is reasonably elegant (especially if you happen to have an index pack generator lying around):
template<int... I> struct index {
template<int n> using append = index<I..., n>; };
template<int N> struct make_index { typedef typename
make_index<N - 1>::type::template append<N - 1> type; };
template<> struct make_index<0> { typedef index<> type; };
template<int N> using indexer = typename make_index<N>::type;
template<typename... Args, int... i>
void h2(index<i...>, Args... args)
{
const size_t nargs = sizeof...(args); // get number of args
X x_array[nargs]; // create X array of that size
pass{(f(x_array[i], args), 1)...}; // call f
g(x_array, nargs); // call g with x_array
}
template<typename... Args>
void h(Args... args)
{
h2(indexer<sizeof...(args)>(), std::forward<Args>(args)...);
}
See C++11: I can go from multiple args to tuple, but can I go from tuple to multiple args? for more information.
Live example.
Xeo is onto the right idea- you want to build some kind of "variadic iterator" that hides a lot of this nastiness from the rest of the code.
I'd take the index stuff and hide it behind an iterator interface modeled after std::vector's, since a std::tuple is also a linear container for data. Then you can just re-use it all of your variadic functions and classes without having to have explicitly recursive code anywhere else.