Container of various functors

I am trying to find a way to have a container of functors so that I can pass the value to functors and change it, however, I have problems so that functors are not limited in what types they can be and the number of arguments that they can take.

My actual use for this is that I have a number of functors that in any case modify a 3D vector based on input. Having the ability to store these functors in a container, I can manipulate the order in which they are called, and ultimately with another resulting vector, iterating over the container, passing a vector to each functor. I use these vectors to determine position, color, acceleration, etc. Particles. Therefore, in the end, I can create completely different particle effects by taking this modular approach, and in the end I can determine the functor order of the file at runtime. This is my ultimate goal :)

The only way I did this work was through inheritance and a set of void pointers that make the code extremely difficult to track, debug, and use.

Here is my code so far, which hopefully will demonstrate what I'm trying to do better than what I typed above. Keep in mind that I'm very out of my comfort zone, so this code can be horrible and make some of you gurus want to beat me with a stick.

#include <iostream> #include <vector> //the base functor class struct Functor { //an integer so we can tell how many arguments the functor takes unsigned num_arguments; Functor() : num_arguments(0) {} //I'm making the arguments default to 0 so the compiler doesn't complain about not giving enough arguments virtual void operator()(void* R, void* A1 = 0, void* A2 = 0, void* A3 = 0) = 0; }; template<typename R, typename A1> struct Double : public Functor { Double() { num_arguments = 1; } void operator()(void* i, void* a, void*, void*) { //having to cast everything out of void pointers so it can be used A1& first = *static_cast<A1*>(a); *static_cast<R*>(i) = first * 2; } }; template<typename R, typename A1, typename A2> struct Sub : public Functor { Sub() { num_arguments = 2; } void operator()(void* i, void* a, void* b, void*) { //having to cast everything out of void pointers so it can be used A1& first = *static_cast<A1*>(a); A2& second = *static_cast<A2*>(b); *static_cast<R*>(i) = first - second; } }; int main() { //our container for the functors std::vector<Functor*> functors; functors.push_back(new Double<int, int>); functors.push_back(new Sub<int, int, int>); for(int i = 0; i < functors.size(); ++i) { int result; int first = 1, second = 2; Functor& f = *functors[i]; if(f.num_arguments == 1) { f(&result, &first); } else if(f.num_arguments == 2){ f(&result, &first, &second); } std::cout << result << std::endl; } Functor* float_sub = new Sub<float, float, float>; float result; float first = 0.5f, second = 2.0f; (*float_sub)(&result, &first, &second); std::cout << result << std::endl; functors.push_back(float_sub); //The functors vector now contains 3 different types of functors: //One that doubles an integer //One that subtracts two integers //One that subtracts two floats std::cin.get(); return 0; } 

Side note: I expect some people to tell me to use the same thing from boost libraries. Despite the fact that I appreciate that I know that there is this option, I still would like to know the best way to implement this without any external libraries, because for me it is something like learning.

Edit

Well, learning about stdarg and boost::any , I think I see a way to make this work beautiful and try it :)


Decision 2

Ok, I reworked the code with boost::any and cstdarg for what I consider to be the best solution. This does not use void pointers and does not limit the number of arguments that functors can have. The newer code also allows you to pass a value using void pointers, everything should have been a reference, which could cause problems: Sub(&result, 1, 1)

 #include <iostream> #include <vector> #include <cstdarg> #include <boost\any.hpp> struct Functor { unsigned num_arguments; Functor() : num_arguments(0) {} virtual void operator()(boost::any, ...) = 0; }; template<typename R, typename A1> struct Double : public Functor { Double() { num_arguments = 1; } void operator()(boost::any r, ...) { R* out = boost::any_cast<R*>(r); va_list args; va_start(args, r); A1 first = va_arg(args, A1); va_end(args); *out = first * 2; } }; template<typename R, typename A1, typename A2> struct Sub : public Functor { Sub() { num_arguments = 2; } void operator()(boost::any r, ...) { R* out = boost::any_cast<R*>(r); va_list args; va_start(args, r); A1 first = va_arg(args, A1); A2 second = va_arg(args, A2); va_end(args); *out = first - second; } }; int main() { std::vector<Functor*> functors; functors.push_back(new Double<int, int>); functors.push_back(new Sub<int, int, int>); functors.push_back(new Sub<int, float, float>); int result = 0; for(int i = 0; i < functors.size(); ++i) { (*functors[i])(&result, 2, 2); std::cout << result << std::endl; } std::cin.get(); return 0; } 

and now I finally have the right to raise: D

+4
source share
2 answers

Have you considered using variational argument lists from the stdarg library ? I am not sure if this is the best solution, but it is different. For instance:

 #include <vector> #include <cstdarg> #include <iostream> using namespace std; template< typename type > void sub( void* out, ... ){ // Initialize the variadic argument list. va_list args; va_start( args, out ); // Extract our arguments. type lhs = va_arg( args, type ); type rhs = va_arg( args, type ); // Close the variadic argument list. va_end( args ); // Function logic goes here. *(static_cast<type*>(out)) = lhs - rhs; } int main( void ){ typedef void (*functor)( void* out, ... ); // Function type. typedef vector< functor > FunctorList; // Function list type. FunctorList fList; fList.push_back( &sub<int> ); int diff; fList[0]( &diff, 3, 5 ); cout << diff << endl; return 0; } 
+4
source

I did not understand that you specifically wanted function objects (I had to click on your use of "functor" and not "function pointer"). Anyway, here is my previous answer using function objects instead. It works the same way:

 #include <vector> #include <cstdarg> #include <iostream> using namespace std; struct FunctorBase { virtual void operator()( void* out, ... ) = 0; }; // end FunctorBase template< typename T > struct Subtract : public FunctorBase { void operator()( void* out, ... ){ // Initialize the variadic argument list. va_list args; va_start( args, out ); // Extract our arguments T lhs = va_arg( args, T ); T rhs = va_arg( args, T ); // Close the variadic argument list. va_end( args ); // Function logic goes here. *(static_cast<T*>(out)) = lhs - rhs; } }; int main( void ){ typedef vector< FunctorBase* > FunctorList; // Function list type. FunctorList fList; fList.push_back( new Subtract<int>() ); int diff; (*fList[0])( &diff, 3, 5 ); cout << diff << endl; return 0; } 
+2
source

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


All Articles