I have a std::vector of arguments and I would like to call a function with them. Is there any way to do this?
In particular the function is the mysqlx select function and the arguments are the columns I'm trying to query; they will all be of type std::string. The purpose of the function is to reduce duplication in the codebase.
(This seems like a generally useful topic, but I could not find an answer through search. If I missed it and this has already been answered, please point me to the question and close this as a duplicate, thanks.)
You can do it, up to a compile time maximum number of arguments. It isn't pretty.
using result_type = // whatever
using arg_type = // whatever
using args_type = const std::vector<arg_type> &;
using function_type = std::function<result_type(args_type)>;
template <size_t... Is>
result_type apply_vector_static(args_type args, std::index_sequence<Is...>)
{
return select(args[Is]...);
}
template<size_t N>
result_type call_apply_vector(args_type args)
{
return apply_vector_static(args, std::make_index_sequence<N>());
}
template <size_t... Is>
std::map<size_t, function_type> make_funcs(std::index_sequence<Is...>)
{
return { { Is, call_apply_vector<Is> }... };
}
result_type apply_vector(args_type args)
{
// Some maximum limit
static const auto limit = std::make_index_sequence<50>();
static const auto funcs = make_funcs(limit);
return funcs.at(args.size())(args);
}
See it live
Related
I am trying to solve this problem in C++ TMP where in i need to convert one parameter pack types into another, and then convert back the types and also values. The conversion back part is based on a boolean criteria that whether an arg in Args... was transformed or not in the first place.
Basically, i have a pack(Args...). First, i transform this (for each args[i], call a transform function). It works like this:
For each arg in Args..., just create same type in transformed_args... unless it is one of following, in that case do following conversions:
Type In Args...
Type In transformed_Args...
SomeClass
shared_ptr to SomeClass
std::vector of SomeClass
std::vector of shared_ptr to SomeClass
everything else remains the same for ex:
int remains int
std::string remains std::string
I achieve this by template specialization, of course
For the next part, i take transformed_args..., publish a class and a functor. I receive call back on this functor from(C++generated Python using Pybind, not important though). Relevant bits of that class look like this...
template<typename C, typename...transformed_args..., typename... Args>
class SomeTemplateClass
{
MethodWrapper<C,void, Args...> func;
//.....
void operator()(transformed_args... targs)
{
//....
(*func.wrapped_method_inside)(transform_back_magic(targs)...) // this is want i want to achieve.
//transform_back_magic(targs)... is a plaeholder for code that checks if type of args[i]... != type of targs[i]... and then calls a tranform_back specialization on it else just return args[i].val
}
}
targs are in transformed_args... format, but underlying C++ function they are aimed for expects Args...
template<typename... Args, typename... transformed_args, ........whatever else is needed>
transform_back_magic(....)
{
if(Args[i].type != transformed_args[i].types)
tranform_back(targs[i]...);
}
the tranform_back function template logic is specialized for different cases and all logic is in place. But how to invoke that based on this boolean criteria is hitting my TMP knowledge limits. I just got started not many weeks ago.
Here i am listing down what i have created so far.
First of all this is what i need in pseudo code
template<typename C, typename... transformed_args, typename... Args>
class SomeTemplateClass
{
MethodWrapper<C,void, Args...> func;
void operator(transformed_args... targs)
{
**//In pseudo code, this is what i need**
Args... params = CreateArgsInstanceFromTransformedArgs(targs);
(*func.wrapped_method_inside)(params...);
}
}
In my attempt to implement this, so far I have decided on creating a tuple<Args...> object by copying data from targs(with conversions where ever required)
void operator(transformed_args... targs)
{
//....
auto mytup = call1(std::tuple<args...>(), std::make_index_sequence<sizeof...(Args)>,
std::make_tuple(targs...), targs...);
// mytup can be std::tuple<Args...>(transform_back(1st_targs), transform_back(2nd_targs)....). Once available i can write some more logic to extract Args... from this tuple and pass to(*func.wrapped_method_inside)(....)
(*func.wrapped_method_inside)(ArgsExtractorFromTuple(mytup)); // this part is not implemented yet, but i think it should be possible. This is not my primary concern at the moment
}
//call1
template<typename... Args, typename... Targs, std::size_t... N>
auto call1(std::tuple<Args...> tupA, std::index_sequence<N>..., std::tuple<Targs...> tupT, Targs ..)
{
auto booltup = tuple_creator<0>(tupA, tupT, nullptr); // to create a tuple of bools
auto ret1 = std::make_tuple<Args...>(call2(booltup, targs, N)...); // targs and N are expanded together so that i get indirect access to see the corresponding type in Args...
return ret1;
}
// tuple_creator is a recursive function template with sole purpose to create a boolean tuple.
// such that std::get<0>(booltup) = true,
//if tuple_element_t<0,std::tuple<Args...>> and tuple_element_t<0,std::tuple<targs...>> are same types else false
template<size_t I, typename... Targs, typename... Args>
auto tuple_creator(std::tuple<Args...>tupA, std::tuple<Targs...>tupT, std::enable_if_t<I == sizeof...(targs)>*)
{
return std::make_tuple(std::is_same<std::tuple_element_t<I-1, std::tuple<Targs...>>, std::tuple_element_t<I-1, std::tuple<Args...>>>::value);
}
template<size_t I = 0, typename... Targs, typename... Args>
auto tuple_creator(std::tuple<Args...>tupA, std::tuple<Targs...>tupT, std::enable_if_t<I < sizeof...(targs)>*)
{
auto ret1 = tuple_creator<I+1>(tupA, tupT, nullptr);
if(!I)
return ret1;
auto ret2 = std::is_same<std::tuple_element_t<I-1, std::tuple<Targs...>>, std::tuple_element_t<I-1, std::tuple<Args...>>>::value;
return std::tuple_cat(ret1, std::make_tuple(ret2));
}
template<typename TT, typename Tuple>
auto call2(Tuple boolyup, TT t, std::size_t I)
{
auto ret = transform_back<std::get<I>(booltup)>(t); // error: I is not a compile time constant
return ret;
}
transform_back is a template that uses a bool template param and enable_if based specialization to decide whether transform an argument back or not
below are the transform_back specialization for std::vector. Similarly i have others for when T = Class etc and so on
template<bool sameTypes, typename T>
std::enable_if_t<(is_vector<T>::value, is_shared_ptr<typename T::value_type>::value &&
is_class<remove_cvref_t<typename T::value_type_element_type>>::value
&& sameTypes), T>
transform_back(T val) // it was never transfoemd in first place, return as is
{
return val;
}
template<bool sameTypes, typename T>
std::enable_if_t<(is_vector<T>::value, is_shared_ptr<typename T::value_type>::value
&& is_class<remove_cvref_t<typename T::value_type_element_type>>::value
&& !sameTypes),
typename std::vector<typename T::value_type::element_type>>
transform(T val)
{
std::vector<T::value_type::element_type> t;
for(int i = 0 ; i < val.size(); ++i)
{
typename T::value_type::element_type obj = *val[i];
t.push_back(obj);
}
return t;
}
Both these specialization are same and only differ on sameTypes boolean variable
This code currently errors out in call2 method while trying to using
std::get
auto ret = transform_back<std::get<I>(booltup)>(t); // error: I is not a compile time constant
How can you help?
1)What could be the work around to std::get issue here? Just cant figure out a way to fit in std::size_t as template arg here instead of function arg to make it work at compile time.
Other than this:
2)If you can suggest an alternative approach to implement from top level.
Args... params = CreateArgsInstanceFromTransformedArgs(targs);
That would be great. The path i took is not very convincing personally to me.
If I understand correctly, you might do something like:
template <typename> struct Tag{};
std::shared_ptr<SomeClass> transform_to(Tag<std::shared_ptr<SomeClass>>, const SomeClass& s)
{
return std::make_shared<SomeClass>(s);
}
std::vector<std::shared_ptr<SomeClass>> transform_to(Tag<std::vector<std::shared_ptr<SomeClass>>>, const std::vector<SomeClass>& v)
{
std::vector<std::shared_ptr<SomeClass>> res;
res.reserve(v.size());
for (const auto& s : v) {
res.emplace_back(std::make_shared<SomeClass>(s));
}
return res;
}
const SomeClass& transform_to(Tag<SomeClass>, const std::shared_ptr<SomeClass>& s)
{
return *s;
}
std::vector<SomeClass> transform_to(Tag<std::vector<SomeClass>>, const std::vector<std::shared_ptr<SomeClass>>& v)
{
std::vector<SomeClass> res;
res.reserve(v.size());
for (const auto& s : v) {
res.emplace_back(*s);
}
return res;
}
template <typename T>
const T& transform_to(Tag<T>, const T& t) { return t; } // No transformations
And then
std::function<void (Args...)> func;
template <typename ... transformed_args>
void operator () (transformed_args... targs) const
{
func(transform_to(Tag<Args>(), targs)...);
}
Just explaining the use case here to add some context. Consider these three methods in C++ each represented with the function pointer SomeTemplateClass::func:
void foo(vector<shared_ptr<SomeClass>>) // 1
// Args... = vector<shared_ptr<SomeClass>>, Targs... = vector<shared_ptr<SomeClass>>
void foo(vector<SomeClass>) // 2
// Args... = vector<SomeClass>, Targs... = vector<shared_ptr<SomeClass>>
void foo(vector<SomeClass>, vector<shared_ptr<SomeClass>>) // 3
// Args... = vector<SomeClass>, vector<shared_ptr<SomeClass>>, Targs... = vector<shared_ptr<SomeClass>>, vector<shared_ptr<SomeClass>>
One instance each of SomeTemplateClass is exposed to Python via Pybind. I do these transformations so that when foo is called from Python, any arg vector<T>(in C++) is received as vector<shared_ptr<T>> in SomeTemplateClass functor. This helps in to get handle to previously created objects T that i need.
But as you can see from 3 cases for foo, foo(vector<shared_ptr<T>>) does not need to be transformed to and subsequently not need to be transformed back. The case of 'tranform_to'is easily handled with template specialization, but while transforming back, vector<shared_ptr<T>> cant be blindly converted back to vector<T>. So (transform(targs...)) needs an additional logic to transform a particular arg (or targ) only when targ[i]::type != arg[i]::type
Building on Jarod's answer, i rather need something like this where in transform_to method for vector<shared_ptr> is further divided in two possible templates
template<bool wasOriginallyTransformed>
enable_if<!wasOriginallyTransformed, std::vector<std::shared_ptr<SomeClass>> transform_to(Tag<std::vector<SomeClass>>, const std::vector<std::shared_ptr<SomeClass>>& v)
{
return v;
}
template<bool wasOriginallyTransformed>
enable_if<!wasOriginallyTransformed, std::vector<<SomeClass>
transform_to(Tag<std::vector<SomeClass>>, const std::vector<std::shared_ptr<SomeClass>>& v)
{
std::vector<SomeClass> res;
res.reserve(v.size());
for (const auto& s : v) {
res.emplace_back(*s);
}
return res;
}
I am writing a custom interpreted language, which uses a LIFO-stack for data-manipulation. At two places, I need to be able to construct a tuple from values that are stored on the stack. In principle, this code would look like this:
template<typename... Args>
[[nodiscard]] inline std::tuple<Args...> popTupleFromStack(Stack& stack)
{
return { stack.Pop<Args>()... };
}
However, there is a fundamental problem with the LIFO-ordering of the stack: The initializer-list order dictates that calls happen left-to-right, which means that it would actually try to pop the elements in the completely inverse order. Is there any easy way to inverse this order?
I know that fold-expressions allow you to specify left- or right-folds, but it doesn't seem that you can use fold-expressions when you need to initialize an object with the result.
The closes I've come is manually specifying overloadings for the potential number of arguments in the tuple:
template<typename Arg0>
[[nodiscard]] inline std::tuple<Arg0> popStackTuple(Stack& stack)
{
return { stack.Pop<Arg0>() };
}
template<typename Arg0, typename Arg1>
[[nodiscard]] inline std::tuple<Arg0, Arg1> popStackTuple(Stack& stack)
{
Arg1 arg1 = stack.Pop<Arg1>();
Arg0 arg0 = stack.Pop<Arg0>();
return { arg0, arg1 };
}
But this obviously limits the number of arguments I can support, and/or results in a lot of "unnecessary" code. And it seems like such a minor thing not being able to be done with modern C++ (I have everything up to, including C++20 at my disposal if it makes any difference).
You might reverse tuple afterward
template <std::size_t ... Is, typename Tuple>
auto reverse_tuple_impl(std::index_sequence<Is...>, Tuple& tuple)
{
using res_type = std::tuple<std::tuple_element_t<sizeof...(Is) - 1 - Is, std::decay_t<Tuple>>...>;
Is, std::decay_t<Tuple>>>;
return res_type(std::get<sizeof...(Is) - 1 - Is>(tuple)...);
}
template <typename ... Ts>
auto reverse_tuple(std::tuple<Ts...>& tuple)
{
return reverse_tuple_impl(std::index_sequence_for<Ts...>(), tuple);
}
Demo
Non a great improvement but... if you can use C++20, so template lambdas, you can embed the helper function inside reverse_tuple()
template <typename ... Ts>
auto reverse_tuple (std::tuple<Ts...> & tuple)
{
return [&]<std::size_t ... Is> (std::index_sequence<Is...>)
{ return std::make_tuple(std::get<sizeof...(Is)-1u-Is>(tuple)...); }
(std::index_sequence_for<Ts...>{});
}
I am trying to create a function where callers can pass in lambdas with a certain set of known parameters (the return type can be anything the caller wants)
Let's say the possible lambda signatures are
T (Widget*)
T (Widget2*)
I've tried a couple things but I can't get any of them to work
struct Widget2{
int count = 0;
};
class Widget {
public:
template<typename Fn, typename T = std::invoke_result_t<Fn, Widget*>>
T doIt(Fn&& fn)
{
return fn(_sibling);
}
template<typename Fn, typename T = std::invoke_result_t<Fn, Widget2*>>
T doIt(Fn&& fn)
{
return fn(_other);
}
private:
Widget* _sibling = nullptr;
Widget2* _other = nullptr;
};
I've also tried doing something like this
template<typename Fn>
auto doIt(Fn&& fn)
{
if (std::is_invocable_v<Fn, Widget*>)
return fn(_sibling);
else if (std::is_invocable_v<Fn, Widget2*>)
return fn(_other);
}
this code in a compiler: https://wandbox.org/permlink/wMAuw5XXnc0zTjlk
I'd rather avoid having to add multiple functions like doItWidget1, doItWidget2 and so on.
Is there a way I can basically overload functions based on the arguments of the incoming lambda?
You can use enable_if to sfinae away the wrong overload
template<typename Fn, std::enable_if_t<std::is_invocable_v<Fn, Widget*>, int> = 0>
auto doIt(Fn&& fn)
{
return fn(_sibling);
}
template<typename Fn, std::enable_if_t<std::is_invocable_v<Fn, Widget2*>, int> = 0>
auto doIt(Fn&& fn)
{
return fn(_other);
}
Note that you don't even need to figure out the return type T, you can just use auto to do that.
Here's a demo.
While trying to reply to this question, I found my self in the need of creating a bunch of parameters for a variadic function on the fly where:
the number of the parameters is not given
the types are all the same, but unknown (even if they must be default constructible)
At runtime, the standard containers and a for loop can be used to do that.
Anyway, I'd like to generate a set of parameters at compile time, so as to be able to forward them to a variadic function.
Because of that, a std::tuple seemed the obvious solution.
Here arose the question: given a size N and a default constructible type T at compile time, how can I write a function to generate a tuple of the given size?
I'm looking for something like this:
auto tup = gen<MyType, N>();
On SO is a notable example of a recursive generator based structs but I was struggling with a function based solution and I've not been able to find it anywhere.
A correctly written forwarding function (a la std::apply) should work with std::array<T, N> and anything else that implements the std::tuple_size/std::get interface. That said,
template<size_t, class T>
using T_ = T;
template<class T, size_t... Is>
auto gen(std::index_sequence<Is...>) { return std::tuple<T_<Is, T>...>{}; }
template<class T, size_t N>
auto gen() { return gen<T>(std::make_index_sequence<N>{}); }
Here is a possible implementation of such a function:
#include<utility>
#include<tuple>
template<typename T>
constexpr auto
params(std::index_sequence<0>) {
return std::tuple<T>{};
}
template<typename T, std::size_t I, std::size_t... O>
constexpr auto
params(std::index_sequence<I, O...>) {
auto tup = std::tuple<T>{ T{} };
auto seq = std::make_index_sequence<sizeof...(O)>{};
return std::tuple_cat(tup, params<T>(seq));
}
template<typename T, std::size_t N>
constexpr auto
gen(std::integral_constant<std::size_t, N>) {
return params<T>(std::make_index_sequence<N>{});
}
int main() {
auto tup = gen<int>(std::integral_constant<std::size_t, 3>{});
static_assert(std::tuple_size<decltype(tup)>::value == 3, "!");
}
For the sake of simplicity, I've used int as a type.
With a small effort, user defined types can be used and the constraint of having them default constructible can be relaxed.
Note: this question is for the sake of curiosity.
Consider the documentation for the lambda and the one for the parameter pack.
The following code is legal:
template<typename... T>
void f(T... t) {
auto lambda = [t...](){ /* do something */ };
// do something else
}
The same goes without saying for this one:
void f(int i) {
auto lambda = [i = i](){ /* do something */ };
// do something else
}
I was wondering if it's possible to define an initializer in the capture clause for a parameter pack.
Something like this:
template<typename... Args>
void f(Args&&... args) {
auto lambda = [params = std::forward<Args>(args)...](){ /* do something */ };
// do something else
}
Ok, I suspect it doesn't make sense, but it gives an idea at least.
Is there any viable solution to do that?
Please, do not ask me why I would do that. I don't want to do that. As I said, it's for the sake of curiosity.
Nope.
Best you can do is:
template<typename... Args>
void f(Args&&... args) {
auto lambda = [params = std::make_tuple(std::forward<Args>(args)...)]()
{ /* do something */ };
// do something else
}
then interact with params as a tuple, including using get.
I find you end up having to leave the world of lambdas to unpack the elements again. Probably you could do it with a helper like this:
template<std::size_t...Is, class F>
decltype(auto) unpack_impl( std::index_sequence<Is...>, F&& f ) {
return std::forward<F>(f)(std::integral_constant<std::size_t, Is>{}...);
}
template<std::size_t N, class F>
decltype(auto) unpack( F&& f ) {
return unpack_impl( std::make_index_sequence<N>{}, std::forward<F>(f) );
}
which takes a template non-type argument N, then generates a pack of integral_constants with constexpr operator size_t and values 0 through N-1, and passes those to a lambda you pass unpack.
Example use:
template<typename... Args>
auto print_later(Args&&... args) {
auto lambda = [params = std::make_tuple(std::forward<Args>(args)...)](
auto&& stream
)
{
unpack<sizeof...(Args)>( [&](auto...Is){
using discard=int[];
(void)discard{0,(void(
stream << std::get<Is>( params )
),0)...
};
});
};
return lambda;
}
Notice the Is being passed to the inner lambda. Basically this technique let us unpack a parameter pack and get its values in an expandible context without having to create a new explicit template function; instead, we create a variadic lambda, and use the types of the arguments (or constexpr operations on them) to get the unpack results.
The above is a function that takes a package of arguments, and returns a function that takes a stream and prints them all.
live example
The unpack function can be made much more generic; at least, have it take a integer_sequence, and in the end even a variation could take a pack of types directly.