C ++ std :: function variable with variable arguments

On my callback system, I want to save the std::function (or something else) with various arguments.

Example:

  • I want to call void()
  • I want to call void(int, int)

I want 1) and 2) to be stored in the same variable and choose what needs to be called when calling the call

 FunctionPointer f0; FunctionPointer f2; f0(); f2(4, 5); 

Is it possible to do something like this? Or I need to create some "FuntionPointer" templates based on the calculation of input arguments.

EDIT

Is there any way to use std :: bind for this task? With std :: bind, I can have std::function<void()> f = std::bind(test, 2, 5);

EDIT 2

Practical use case: I have a trigger system and I want to assign funtion pointers to actions, so when the action happens, the function is called. Pseudo-code example:

 structure Trigger { Function f; } Init: Trigger0.f = pointer to some function () Trigger1.f = pointer to some function (a, b) Input: Find Trigger by input if (in == A) Trigger.f(); else Trigger.f(10, 20) 

or if possible

 Input: Find Trigger by input if (in == A) f = bind(Trigger.f); else f = bind(Trigger.f, 10, 20); f() 
+5
source share
6 answers

Well, if you can use RTTI, you can define a MultiFuncObject like this, and you can easily link other functions. In addition, they can be easily called. But unfortunately, this approach only works for a limited number of arguments. But in fact, boost::bind also supports a limited number of arguments (9 by default). In this way, you can expand this class to suit your needs.

Before giving you the source of MultiFuncObject , I want to show you how you can use it. It takes a template argument, which will be used as the return type. You can associate new functions with the += operator. Using some template magic, a class distinguishes differences between related functions with the same number of arguments with at least one other type of argument.

You need C ++ 11 because MultiFuncObject uses std::unordered_map and std::type_index .

Here is the usage:

 #include <iostream> using namespace std; void _1() { cout << "_1" << endl; } void _2(char x) { cout << "_2" << " " << x << endl; } void _3(int x) { cout << "_3" << " " << x << endl; } void _4(double x) { cout << "_4" << " " << x << endl; } void _5(int a, int b) { cout << "_5" << " " << a << " " << b << endl; } void _6(char a, int b) { cout << "_6" << " " << a << " " << b << endl; } void _7(int a, int b, int c) { cout << "_7" << " " << a << " " << b << " " << c << endl; } int main() { MultiFuncObject<void> funcs; funcs += &_1; funcs += &_2; funcs += &_3; funcs += &_4; funcs += &_5; funcs += &_6; funcs += &_7; funcs(); funcs('a'); funcs(56); funcs(5.5); funcs(2, 5); funcs('q', 6); funcs(1, 2, 3); return 0; } 

I hope this is close to what you want. Here is the source of MultiFuncObject :

 #include <typeinfo> #include <typeindex> #include <unordered_map> using namespace std; template <typename R> class MultiFuncObject { unordered_map<type_index, void (*)()> m_funcs; public: MultiFuncObject<R> operator +=( R (* f)() ) { m_funcs[typeid( R() )] = (void (*)()) f; return *this; } template <typename A1> MultiFuncObject<R> operator +=( R (* f)(A1) ) { m_funcs[typeid( R(A1) )] = (void (*)()) f; return *this; } template <typename A1, typename A2> MultiFuncObject<R> operator +=( R (* f)(A1, A2) ) { m_funcs[typeid( R(A1, A2) )] = (void (*)()) f; return *this; } template <typename A1, typename A2, typename A3> MultiFuncObject<R> operator +=( R (* f)(A1, A2, A3) ) { m_funcs[typeid( R(A1, A2, A3) )] = (void (*)()) f; return *this; } R operator()() const { unordered_map<type_index, void (*)()>::const_iterator it = m_funcs.find(typeid( R() )); if (it != m_funcs.end()) { R (*f)() = ( R (*)() )(it->second); (*f)(); } } template <typename A1> R operator()(A1 a1) const { unordered_map<type_index, void (*)()>::const_iterator it = m_funcs.find(typeid( R(A1) )); if (it != m_funcs.end()) { R (*f)(A1) = ( R (*)(A1) )(it->second); (*f)(a1); } } template <typename A1, typename A2> R operator()(A1 a1, A2 a2) const { unordered_map<type_index, void (*)()>::const_iterator it = m_funcs.find(typeid( R(A1, A2) )); if (it != m_funcs.end()) { R (*f)(A1, A2) = ( R (*)(A1, A2) )(it->second); (*f)(a1, a2); } } template <typename A1, typename A2, typename A3> R operator()(A1 a1, A2 a2, A3 a3) const { unordered_map<type_index, void (*)()>::const_iterator it = m_funcs.find(typeid( R(A1, A2, A3) )); if (it != m_funcs.end()) { R (*f)(A1, A2, A3) = ( R (*)(A1, A2, A3) )(it->second); (*f)(a1, a2, a3); } } }; 

It stores prototypes of various functions using std::unordered_map with std::type_index and void (*)() values. If necessary, the correct function is extracted using this card.

Here is a working example

+2
source

std::function<void()> and std::function<void(int, int)> are two completely different types. To store an object of an unknown type, you need some kind of union function (or polymorphism).

If you can use Boost, you can easily do this with boost::variant :

 // Declaration: boost::variant<std::function<void()>, std::function<void(int, int)> > f; // Calling, explicit: if (fContainsNullary()) { boost::get<std::function<void()>>(f)(); } else { boost::get<std::function<void(int, int)>>(f)(4, 5); } 

You must provide the fContainsNullary() logic. Alternatively, you can use the option of your own stored knowledge of type value using a visitor:

 struct MyVisitor : boost::static_visitor<void> { result_type operator() (const std::function<void()> &a) { a(); } result_type operator() (const std::function<void(int, int)> &a) { a(4, 5); } }; // Calling, via visitor: boost::apply_visitor(MyVisitor(), f); 

If Boost is not an option, you can use a suitable union for the same purpose.

+10
source

The following solution may work for you (I'm not sure if the code is absolutely right here):

Create a wrapper for std::function with a virtual destructor to enable dynamic casting

 class function_wrapper_base { virtual ~function_wrapper_base(); } template <class... Args> class function_wrapper : public function_wrapper_base { public: std::function<void, Args...> f; ... }; 

Then create the variant_function_holder class

 class variant_function_holder { std::unique_ptr<function_wrapper_base> f; ... template <class... Args> void operator()(Args&&... args) { function_wrapper<std::decay<Args>::type...> * g = dynamic_cast<function_wrapper<std::decay<Args>::type...>>(f.get()); if (g == nullptr) { // ToDo } g->f(std::forward<Args>(args)...); } }; 
+4
source

C ++ 11 to the rescue!

If you can generalize your function to a functor object without arguments, you can call it with any lambda.

 #include <iostream> using namespace std; template <class F> void call_it(F&& f) { f(); } int main() { int x = 50, y = 75; call_it([] () { cout << "Hello!\n"; }); call_it([x,y] () { cout << x << " + " << y << " = " << x + y << '\n';}); return 0; } 
+4
source

If you do not need std::function , you can create a proxy class.

 class fn_t { public: typedef void (*fn_1_t)(); typedef void (*fn_2_t)(int, int); fn_1_t fn_1; fn_2_t fn_2; fn_t operator=(fn_1_t func_1) { fn_1 = func_1; return *this; } fn_t operator=(fn_2_t func_2) { fn_2 = func_2; return *this; } void operator()() { (*fn_1)(); } void operator()(int a, int b) { (*fn_2)(a, b); } }; #include <iostream> using namespace std; void first() { cout << "first" << endl; } void second(int a, int b) { cout << "second " << a << " : " << b << endl; } int main() { fn_t f; f = &first; f = &second; f(); f(5, 4); return 0; } 

The fn_t class automatically works with the two prototypes you want, assigns the necessary one automatically, and it can call functions with both prototypes, overloading the operator () with the appropriate parameters.

You can verify the fn_1 and fn_2 function fn_1 fn_2 , but I have not included this check for minimality.

The advantage of this is that you only need C ++ and not even STL and Boost.

+1
source

The other answers are great, but I want to show my solution as well.

This is a small heading with which you can “lengthen” function signatures. This allows you to do this (extract from github example ):

 int foo_1p(int a); int foo_2p(int a, int b); int foo_3p(int a, int b, int c); int foo_4p(int a, int b, int c, int d); int foo_5p(int a, int b, int c, int d, int e); int foo_6p(int a, int b, int c, int d, int e, int f); int foo_7p(int a, int b, int c, int d, int e, int f, std::string g); ... int main() { std::unordered_map<std::string, std::function<int(int, int, int, int, int, int, std::string)>> map; map["foo_1p"] = ex::bind(foo_1p, ph, ph, ph, ph, ph, ph); map["foo_2p"] = ex::bind(foo_2p, ph, ph, ph, ph, ph); map["foo_3p"] = ex::bind(foo_3p, ph, ph, ph, ph); map["foo_4p"] = ex::bind(foo_4p, ph, ph, ph); map["foo_5p"] = ex::bind(foo_5p, ph, ph); map["foo_6p"] = ex::bind(foo_6p, ph); map["foo_7p"] = foo_7p; for (const auto& f : map) { std::cout << f.first << " = " << f.second(1, 1, 1, 1, 1, 1, "101") << std::endl; } } 
0
source

Source: https://habr.com/ru/post/1232450/


All Articles