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)... };
Related
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.
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});
The function 'Process' is taking a variable number of arguments of variable type. To handle different cases, I have successfully overloaded it like this:
// general case
template <typename ...Types>
void Process( const Types&... items )
// single T
template <typename T>
void Process( const T& t )
// one or more of type NVP<>
template <typename T1, typename ...Types>
void Process( const NVP<T1>& nvp1, const NVP<Types>&... nvps )
What I want to do - but can't - is the following: I need an overload for cases with any number of leading arguments of a types ATT<> followed by any number of NVP<> like this:
// any number of leading Types ATT<> followed by any number of NVP<>
template <typename ...ATypes, typename ...BTypes>
void Process( const ATT<ATypes>&... atts, const NVP<BTypes>&... nvps )
At first you would think it should be 'easy' for a compiler to match this, if it can already do the other cases. There should be absolutely no ambiguity here!? However, the matching fails, no error messages, but the desired overload it is just ignored by the compiler.
Currently using VS2017 with /std:c++17
Notes:
1. It can, obviously, be done for one leading type ATT<T1> like this
// one leading Type ATT<T1>
template <typename T1, typename ...Types>
void Process( const ATT<T1>& a1, const Types&... remaining )
But for more than one, I need to do some ugly manual recursion. I really want to have the whole pack of leading ATT<...>.
2. I am aware that a leading parameter pack - of general types - always is ambiguous for matching, but for a specialization like ATT<ATypes>... no ambiguity should exist.
You could dispatch from the const Types&... overload based on if Types... matches ATT<T>..., NVP<U>....
The basic strategy here is finding the index of the last ATT<T>, forwarding everything as a tuple, then indexing with the appropriate index sequence to forward to another function where the ATT values and NVP values are in two tuples:
namespace detail {
template<class...>
struct get_split_index;
template<class T, class... Others>
struct get_split_index<T, Others...> {
static constexpr std::size_t i = -1;
};
template<class T, class... Others>
struct get_split_index<ATT<T>, Others...> {
static constexpr std::size_t next = get_split_index<Others...>::i;
static constexpr std::size_t i = next == -1 ? -1 : next + 1u;
};
template<class T, class... Others>
struct get_split_index<NVP<T>, Others...> {
// will be 0 if the rest are all NVP<T>, otherwise -1
static constexpr std::size_t i = get_split_index<Others...>::i;
};
template<>
struct get_split_index<> {
static constexpr std::size_t i = 0;
};
template<typename... ATypes, typename... BTypes, std::size_t... ATT_I, std::size_t... NVP_I>
void Process(const std::tuple<const ATT<ATypes>&...>& att, const std::tuple<const NVP<BTypes>&...>& nvp, std::index_sequence<ATT_I...>, std::index_sequence<NVP_I...>) {
// Use (std::get<ATT_I>(att)) and (std::get<NVP_I>(nvp))
// instead of (atts) and (nvps) that you would use in your
// supposed `void Process(const ATT<ATypes>&..., const NVP<BTypes>&...)`
}
template<typename... Types, std::size_t... ATT_I, std::size_t... NVP_I>
void ProcessDispatch(const std::tuple<Types...>& t, std::index_sequence<ATT_I...> att_i, std::index_sequence<NVP_I...> nvp_i) {
detail::Process(std::forward_as_tuple(std::get<ATT_I>(t)...), std::forward_as_tuple(std::get<NVP_I + sizeof...(ATT_I)>(t)...), att_i, nvp_i);
}
}
template <typename ...Types>
void Process( const Types&... items ) {
constexpr std::size_t split_index = detail::get_split_index<Types...>::i;
if constexpr (split_index != -1) {
// Might want to check `&& sizeof...(Types) != 0`
detail::ProcessDispatch(std::forward_as_tuple(items...), std::make_index_sequence<split_index>{}, std::make_index_sequence<sizeof...(Types) - split_index>{});
} else {
// general case
}
}
template <typename T>
void Process( const T& t ) {
// single T
}
template <typename T1, typename ...Types>
void Process( const NVP<T1>& nvp1, const NVP<Types>&... nvps ) {
// one or more of type NVP<>
// This can also be folded into `detail::Process`, checking
// `if constexpr (sizeof...(BTypes) == 0)`.
}
Believe you can use a struct to help you here. The compiler can't determine where one parameter pack stops and the other begins, consider:
foo(1, 2.0, '3', "45", 6.0f). The first parameter pack could be nothing, the first, all of them or none of the above. There is no particular reason to prefer one over another. So you can't make a function that accepts two variadics. What you can do, is to split it into two structs, and specify explicitly the arguments for the outer class.
template<typename... Args>
struct S
{
template<typename... Inner>
static void Process(const ATT<Args>&... atts, const NVP<Inner>&... nvps) {}
};
Example for usage:
ATT<double> a1;
ATT<long> a2;
NVP<int> n1;
NVP<const char*> n2;
S<double, long>::Process(a1, a2, n1, n2);
Another version could be by using the constructor. Here, you also get auto-deduction which is easier. Unfortunately, it only works from C++17 and above.
template<typename... Args>
struct S
{
std::tuple<ATT<Args>...> tup;
S(const ATT<Args>&... atts)
: tup(atts...)
{}
template<typename... Inner>
void Process(const NVP<Inner>&... nvps){}
};
template<typename... Args>
S(const ATT<Args>&... atts)->S<Args...>;
And the usage is:
S(ATT(1), ATT(3.4)).Process(NVP("asdf"), NVP(3.4), NVP('f'));
return 0;
Assuming you're OK with getting them as tuples I made this after drawing from https://stackoverflow.com/a/12782697/1480324 :
#include <iostream>
#include <tuple>
template<typename T>
struct ATT {};
template<typename T>
struct NVP {};
template<typename... ATTs, typename... NVPs>
void Process(const std::tuple<ATT<ATTs>...>& atts, const std::tuple<NVP<NVPs>...>& nvps) {
std::cout << sizeof...(ATTs) << std::endl;
std::cout << sizeof...(NVPs) << std::endl;
}
int main() {
Process(std::make_tuple(ATT<int>(), ATT<double>()), std::make_tuple(NVP<std::string>(), NVP<bool>()));
return 0;
}
It compiles on https://www.onlinegdb.com/online_c++_compiler , but I can't test in visual studio.
Lately I wrote a template function to solve some code repetitions. It looks like this:
template<class T, class R, class... Args>
R call_or_throw(const std::weak_ptr<T>& ptr, const std::string& error, R (T::*fun)(Args...), Args... args) {
if (auto sp = ptr.lock())
{
return std::invoke(fun, *sp, args...);
}
else
{
throw std::runtime_error(error.c_str());
}
}
int main() {
auto a = std::make_shared<A>();
call_or_throw(std::weak_ptr<A>(a), "err", &A::foo, 1);
}
This code works perfectly well for class A which looks like this:
class A {
public:
void foo(int x) {
}
};
But fails to compile for one like this:
class A {
public:
void foo(const int& x) {
}
};
Why is it so (by why I mean why it fails to deduce the type) and how (if it is possible at all) can I make this code work with references?
Live example
Args types cannot be deduced both as const& (from fun parameter declaration) and non-reference from args declaration. A simple fix is to use two separate template type parameter packs:
template<class T, class R, class... Args, class... DeclaredArgs>
R call_or_throw(
const std::weak_ptr<T>& ptr,
const std::string& error,
R (T::*fun)(DeclaredArgs...),
Args... args);
As a downside, I can imagine slightly longer error messages in case of bad usage.
Note that the template parameter Args's type is deduced as const int& on the 3rd function argument &A::foo, and deduced as int on the 4th function parameter 1. They don't match and cause deduction fails.
You can exclude the 4th parameter from deduction, e.g.
template<class T, class R, class... Args>
R call_or_throw(const std::weak_ptr<T>& ptr,
const std::string& error,
R (T::*fun)(Args...),
std::type_identity_t<Args>... args) {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
LIVE
PS: std::type_identity is supported since C++20; but it's quite easy to implement one.
Your issue is that you have conflict deductions for Args between:
R (T::*fun)(Args...)
Args... args
I suggest to have more generic code (no duplications between R (T::*fun)(Args...) and
const version R (T::*fun)(Args...) const and other alternative) with:
template<class T, class F, class... Args>
decltype(auto) call_or_throw(const std::weak_ptr<T>& ptr,
const std::string& error,
F f,
Args&&... args)
{
if (auto sp = ptr.lock())
{
return std::invoke(f, *sp, std::forward<Args>(args)...);
}
else
{
throw std::runtime_error(error.c_str());
}
}
Here's what I want to do; posting the whole code because it's not too long and also to demonstrate the specific task I'm trying to solve. Basically, I need a way to iterate values from parameter pack by index (the index part is important, even though it's not required in this example).
#include <iostream>
#include <tuple>
#include <type_traits>
template <int First, int Last, typename Functor>
constexpr void static_for(Functor&& f)
{
if constexpr (First < Last)
{
f(std::integral_constant<int, First>{});
static_for<First + 1, Last, Functor>(std::forward<Functor>(f));
}
}
template <size_t index, typename... Args>
auto value_by_index(Args&&... args) noexcept {
return std::get<index>(std::forward_as_tuple(std::forward<Args>(args)...));
}
template <typename... ValueTypes>
void traverse(ValueTypes... values)
{
static_for<0, sizeof...(ValueTypes)>([&](int i) {
auto v = value_by_index<static_cast<size_t>(i), ValueTypes...>(values...);
std::cout << v << std::endl;
});
}
int main()
{
traverse(0.0f, 1, 3.33, "str");
return 0;
}
The compiler error, of course, is:
<source>:24:71: error: 'i' is not a constant expression
If lambdas could have explicit template arguments, i would be such an argument and it would be obvious to the compiler that it's known at compile time. But that's not how lambdas work.
If you want to treat it as an X-Y problem, I suppose I don't specifically need to call a lambda inside my static_for, but I do need to call some piece of code that can access parameter pack(s) of traverse by index, and if traverse was a member function, I need to have access to its this.
Try it online: https://godbolt.org/z/eW4rnm
Use a generic lambda and a constexpr conversion operator c++17:
template <typename... ValueTypes>
void traverse(ValueTypes... values)
{
static_for<0, sizeof...(ValueTypes)>([&](auto I)
// ~~~^
{
auto v = value_by_index<I>(values...);
// ~^~
std::cout << v << std::endl;
});
}
DEMO
Use a template parameter list for the lambda expression c++20:
template <typename... ValueTypes>
void traverse(ValueTypes... values)
{
static_for<0, sizeof...(ValueTypes)>([&]<int I>(std::integral_constant<int, I>)
// ~~~~^ ~^~
{
auto v = value_by_index<I>(values...);
// ~^~
std::cout << v << std::endl;
});
}
DEMO 2
It's too late to play?
Basically, I need a way to iterate values from parameter pack by index (the index part is important, even though it's not required in this example).
Sorry but... what about the good old use of std::make_index_sequence and std::index_sequence ?
Maintaining your value_by_index(), I propose the following C++14 solution based on traverse() with traverse_helper()
template <typename F, std::size_t ... Is, typename ... VTs>
void traverse_helper (F f, std::index_sequence<Is...>, VTs ... vs)
{
using unused = int[];
(void)unused { 0, (f(value_by_index<Is>(vs...)), 0)... };
}
template <typename F, typename ... VTs>
void traverse (F f, VTs ... vs)
{ traverse_helper(f, std::make_index_sequence<sizeof...(VTs)>{}, vs...); }
Observe that I've passed also the callable as parameter.
If you can use C++17 (as you tagged), traverse_helper() simply become
template <typename F, std::size_t ... Is, typename ... VTs>
void traverse_helper (F f, std::index_sequence<Is...>, VTs ... vs)
{ (f(value_by_index<Is>(vs...)), ...); }
You can call traverse() as follows
traverse([](auto x){ std::cout << x << std::endl; },
0.0f, 1, 3.33, "str");
The following is a full C++14 compiling example
#include <iostream>
#include <tuple>
#include <type_traits>
template <std::size_t I, typename ... As>
auto value_by_index (As && ... as) noexcept
{ return std::get<I>(std::forward_as_tuple(std::forward<As>(as)...)); }
template <typename F, std::size_t ... Is, typename ... VTs>
void traverse_helper (F f, std::index_sequence<Is...>, VTs ... vs)
{
using unused = int[];
(void)unused { 0, (f(value_by_index<Is>(vs...)), 0)... };
}
template <typename F, typename ... VTs>
void traverse (F f, VTs ... vs)
{ traverse_helper(f, std::make_index_sequence<sizeof...(VTs)>{}, vs...); }
int main ()
{
traverse([](auto x){ std::cout << x << std::endl; },
0.0f, 1, 3.33, "str");
}