Here are a set of Variadic
Template
Classes
that will enable you to do this with ease if you have C++17
available to you. The first class simply stores a generic version of a std::function
of any type! The second class stores the first class through a member function
and will register
either a function pointer
, a function object
or a lambda
. It also has another member function
that will invoke
it. I am currently using lambdas
in this example. I have two lambdas
, the 1st takes two int
types adds them and returns an int
as in your problem above. The 2nd takes two std::strings
concatenates them then prints them to the screen but does not return any value. You can even expand this by having a std::vector<Functor<>>
stored in the driver class. The register_callback
would change slightly as to pushing them into the vector, and the call_back
you have options. You could either call them all in one go, or search via an index value to call a specific one, but I'll leave that as an exercise to you.
#include <string>
#include <iostream>
#include <functional>
template<typename RES_TYPE, typename... ARG_TYPES>
struct Functor {
std::function<RES_TYPE( ARG_TYPES... )> func_;
};
template<typename RES_TYPE, typename... ARG_TYPES>
class Driver {
private:
Functor<RES_TYPE, ARG_TYPES...> functor;
public:
Driver() = default;
~Driver() = default;
void register_callback( const Functor<RES_TYPE, ARG_TYPES...> &func ) {
functor = func;
}
RES_TYPE call_back( ARG_TYPES... args ) {
return functor.func_( std::forward<ARG_TYPES>(args)... );
}
};
int main() {
// Function Type: int ( int, int );
Functor<int, int, int> func;
auto lambda = []( int a, int b ) { return a + b; };
func.func_ = lambda;
Driver<int, int, int> driver;
driver.register_callback( func );
int a = 3;
int b = 5;
std::cout << driver.call_back( a, b ) << '\n';
std::cout << driver.call_back( 7, 5 ) << '\n';
// Function Type: void ( string, string );
Functor<void, std::string, std::string> func2;
auto lambda2 = []( std::string str1, std::string str2 ) {
str1 = str1 + " " + str2;
std::cout << str1 << '\n';
};
Driver <void, std::string, std::string> driver2;
func2.func_ = lambda2;
driver2.register_callback( func2 );
std::string str1 = "Hello";
std::string str2 = "World";
driver2.call_back( str1, str2 );
driver2.call_back( "Generic", "Programming" );
return 0;
}
The output is:
8
12
Hello World
Generic Programming
If you notice with this code here; there is no need to mess with pointers or dynamic memory. This is all taken care of by the use of std::function
for us and the default dtors
of the classes.
I tried my best to design this with simplicity, readability while making it portable and generic as possible. I am sure there can be some improvements made, but I think this is in line of what you are asking for.