Reputation: 137
In Python, it is possible to store a function pointer with different number of arguments and store the arguments in a list and then unpack the list and call that function like:
def Func(x1, x2):
return x1+x2
ArgList = [1.2, 3.22]
MyClass.Store(Func)
MyClass.FuncPtr(*ArgList)
Is it possible in c++ to do a similar thing?
For example, store the function with the variable-number of inputs and the values in a std::vector
and call function pointer by that vector?
I don’t want to define the argument list as a vector.
Upvotes: 1
Views: 1421
Reputation: 114461
Unfortunately it is not possible to call an arbitrary function passing arbitrary values in a complete dynamic way at runtime. At least some part of the C++ code must compile a static call to a function with the same exact signature.
The problem is that in C++ the parameter passing can only be handled by the compiler and even variadic functions (e.g. printf
) where only inspection is performed at runtime normally require a different and simpler calling convention.
One technical problem is that modern ABIs are extremely complex and asymmetrical (e.g. some parameters in registers, some in special FPU registers, some in the stack) and are only handled at compile time.
What you can do is "wrap" the functions you want to be able to call in a function accepting for example an std::vector
of generic values and then calling the function passing those parameters converted to the proper type, e.g. something like
Value myabs(const std::vector<Value>& args) {
if (args.size() != 1) throw std::runtime_error("Bad arity");
return Value(fabs(args[0].to<double>()));
}
then it is easy to dynamically call these wrappers because the signature is the same for all of them.
Those wrappers unfortunately must be written (or generated) for each of the functions you need to be able to call so the compiler can actually generate the code needed to call the regular C++ functions.
I am not a template expert, but C++ metaprogramming can be used to generate the wrappers instead of coding each of them manually or writing an external code generator.
This is a very basic example:
typedef Value (*FWrapper)(const std::vector<Value>&);
template<typename RV, typename T1, typename T2, RV (*f)(T1, T2)>
Value wrapper2(const std::vector<Value>& args) {
if (args.size() != 2) throw std::runtime_error("Bad arity");
return f(args[0].to<T1>(), args[1].to<T2>());
}
For example to wrap double atan2(double, double)
you can simply write:
FWrapper myatan2 = &wrapper2<double, double, double, &atan2>;
I tried a bit without success to avoid passing explicitly the return value type and number and types of parameters and to extract them instead from the passed function pointer, but may be that is also possible or even practical with recent C++.
The idea is basically to invoke the compiler to build a specific function call on demand when doing a template instantiation.
Upvotes: 3
Reputation: 6474
Not exactly the same thing but in C++ you can apply a combining function to a range of values to produce a single result. std::accumulate can help you here. However, it works with a given range, not variadic arguments.
#include <numeric>
#include <vector>
#include <functional>
#include <iostream>
class MyClass
{
public:
template<typename F, typename T>
T Execute(F &&fn, const T initialValue, const std::vector<T> &values)
{
auto result = std::accumulate(values.begin(), values.end(),
initialValue, std::forward<F>(fn));
return result;
}
};
int main()
{
MyClass cls;
std::vector<double> values = { 1.2, 3.22 };
auto sum = cls.Execute(std::plus<double>{}, 0.0, values);
std::cout << "Sum = " << sum << std::endl;
auto product = cls.Execute(std::multiplies<double>{}, 1.0, values);
std::cout << "Product = " << product << std::endl;
std::vector<int> values2 = { 10, 20 };
auto subtractFrom200 = cls.Execute(std::minus<int>{}, 200, values2);
std::cout << "Subtract from 200 = " << subtractFrom200 << std::endl;
std::vector<std::string> mystrings = {"Hello", " ", " world", ". ", "Bye!"};
auto concatenate = cls.Execute([](std::string a, std::string b){ return a + b ;}, std::string(), mystrings);
std::cout << "Concatenation = " << concatenate << std::endl;
std::vector<double> values3 = {100, 98, 3.5, 50};
auto findMin = [](double a, double b){ return std::min(a, b); };
auto lowest = cls.Execute(findMin, values3.front(), values3);
std::cout << "Min = " << lowest << std::endl;
}
Note: the fourth parameter to std::accumulate
is optional and if omitted it will return the sum of the values. However, you can provide your own binary function if you want to do something else, e.g. multiplication or subtraction.
Upvotes: 2
Reputation: 32722
Is it possible in c++ to do a similar thing?
If you know the arguments(types) at compile-time, of-course you can!
For, instance like/similar to what @Evg suggested, make the Func
as a variadic-generic-lambda, and with the support of c++17's std::apply
,(for future readers) you could do.
#include <iostream>
#include <utility> // std::forward
#include <tuple> // std::apply
constexpr auto func_lambda = [](auto&&... args) {
return (std::forward<decltype(args)>(args) + ...); // fold expression
};
int main()
{
std::cout << std::apply(func_lambda, std::make_tuple(1, 2, 3)); // prints 6!
return 0;
}
And now as per your python demo code, you could pack the above idea into to a class-template.
Here, you can avoid the use of std::function
, as the Func
will be the lambda you pass, and can be stored as compiler deduced lambda unspecified type.
#include <iostream>
#include <utility> // std::forward
#include <tuple> //std::apply, std::make_tuple
// `Func` as variadic-generic-lambda
constexpr auto func_lambda = [](auto&&... args) noexcept {
return (std::forward<decltype(args)>(args) + ...);
};
template<typename Func>
class MyClass final
{
Func mFunction;
public:
explicit MyClass(Func func) // Store: pass the lambda here!
: mFunction{ func }
{}
template<typename... Args> // to execute the `Func` with `Args`!
constexpr auto execute(Args&&... args) const noexcept
{
return std::apply(mFunction, std::make_tuple(std::forward<Args>(args)...));
}
};
int main()
{
MyClass myClass{ func_lambda }; // MyClass.Store(Func)
std::cout << myClass.execute(1.2, 2.82, 3); // MyClass.FuncPtr(*ArgList)
return 0;
}
Output:
7.02
Upvotes: 5