Introduction
This simple class provides an interface for calling registered functions by a key. The key can be everything you want assumed that your key is comparable by std::map
. A function is called with only one parameter but you can use a container class for multiple parameters.
The class uses three template parameters. The first TReturn
specifies the trigger function return parameters. The second TParameter
specifies the trigger function parameter. The third template argument is the default key and has a default type of a string
because normally you want to call trigger functions by a string
specifier.
The internal storage of a function is implemented with boost functions. Also, you can register a class member function as a function so you can simply inherit this class.
At the moment, there is one problem. If you want to raise a trigger function and this function was not registered, the return value is undefined. To solve this problem, you could easily implement a check function.
template<typename TReturn, typename TParameter, typename TDefaultKey = std::string>
class Trigger {
public:
typedef boost::function<TReturn (TParameter)> triggerFunction_T;
typedef std::map<TDefaultKey, triggerFunction_T> triggerMap_T;
typedef std::pair<TDefaultKey, triggerFunction_T> triggerPair_T;
Trigger(){
triggers.reset(new triggerMap_T);
};
virtual ~Trigger(){
};
template<typename TFunction, typename TObject>
int addTriggerAcceptor(TDefaultKey name, TFunction f, TObject obj){
typename triggerMap_T::iterator it;
triggerFunction_T trigger = boost::bind(f,obj,_1);
it = triggers->find(name);
triggers->insert(triggerPair_T(name,trigger));
return 0;
};
int addTriggerAcceptor(TDefaultKey name, triggerFunction_T f){
typename triggerMap_T::iterator it;
it = triggers->find(name);
triggers->insert(triggerPair_T(name,f));
return 0;
};
TReturn raiseTrigger(TDefaultKey name, TParameter param){
typename triggerMap_T::iterator it;
it = triggers->find(name);
if(it != triggers->end()){
return it->second(param);
}
};
private:
boost::shared_ptr<triggerMap_T> triggers;
};
Second Version using C++11 Features
The second version of the trigger
class makes use of C++11 features. The interface didn't change but the template arguments are not compatible. The first template argument TReturn
is the return type of the trigger
function. The second template parameter TKey
is the type of the key to call a trigger function. There is no more default key so you have to specify the key all the time.
A disadvantage of the first version was the lack of more than one function argument. In the second version, I make use of a variadic template. TArgs
specifies the function parameters. This makes it possible to use as many function arguments as you wish.
When you want to determine if there is a trigger
with a given key, you can use the hasTrigger
function. This function returns true
if there is a registered trigger
. Using this function will lookup the map. First using hasTrigger
to check if you can call a trigger function and then calling it will result in a double lookup of the map. To prevent this, you can call the trigger
and catch
the exception thrown by raiseTrigger
.
template<typename TReturn, typename TKey, typename... TArgs>
class Trigger2 {
public:
typedef std::function<TReturn (TArgs...)> triggerFunction_T;
typedef std::unordered_map<TKey, triggerFunction_T> triggerMap_T;
typedef std::pair<TKey, triggerFunction_T> triggerPair_T;
Trigger2(){
triggers.reset(new triggerMap_T);
}
virtual ~Trigger2(){
};
bool addTriggerAcceptor(TKey key, triggerFunction_T f){
typename triggerMap_T::iterator it;
it = triggers->find(key);
if(it != triggers->end()){
return false;
}else{
triggers->insert(triggerPair_T(key,f));
}
return true;
}
template<typename TFunction, typename TObject>
bool addTriggerAcceptor(TKey key, TFunction f, TObject obj){
triggerFunction_T trigger = [f,obj](TArgs... args){
return (obj->*f)(args...);
};
typename triggerMap_T::iterator it;
it = triggers->find(key);
if(it != triggers->end()){
return false;
}else{
triggers->insert(triggerPair_T(key,trigger));
}
return true;
};
TReturn raiseTrigger(TKey key, TArgs... args){
typename triggerMap_T::iterator it = triggers->find(key);
if(it != triggers->end()){
return it->second(args...);
}else{
throw std::bad_function_call();
}
}
bool hasTrigger(TKey key){
typename triggerMap_T::iterator it;
it = triggers->find(key);
if(it != triggers->end()){
return true;
}else{
return false;
}
}
private:
std::shared_ptr<triggerMap_T> triggers;
};
Using the Code
The new version interface didn't change a lot. You should only take care of the template parameters, which changed. The example below contains a function and a testing class with a function. The function takes two parameters (int
, double
).
#include <iostream>
#include <string>
#include "Trigger.hpp" /* Include the trigger template here */
double testfunction(int a, double b){
return a + b;
}
class Testclass {
public:
int i;
Testclass(){
i = 5;
}
double testfunction(int a, double b){
return a + b + i;
}
};
int main(int argc, const char **argv){
Testclass test;
Trigger2<double,std::string,int,double> trigger;
trigger.addTriggerAcceptor("test",&testfunction);
trigger.addTriggerAcceptor("testclass",&Testclass::testfunction,&test);
std::cout << trigger.raiseTrigger("testclass",5,10.15) << std::endl;
std::cout << trigger.raiseTrigger("test",5,10.15) << std::endl;
}
History
- 05.01.2015: Removed the use of reserved names although it compiles perfect on gcc, these names could break builds on other compilers (Thanks to .:floyd:.)
- 08.08.2015: Second version has the
trigger
class using C++11 only.