diff --git a/simgear/structure/SGBinding.hxx b/simgear/structure/SGBinding.hxx index 026da270..eb12f3d2 100644 --- a/simgear/structure/SGBinding.hxx +++ b/simgear/structure/SGBinding.hxx @@ -73,7 +73,7 @@ public: * @return The command associated with this binding, or 0 if none * is present. */ - SGCommandMgr::command_t getCommand () const { return _command; } + SGCommandMgr::Command* getCommand () const { return _command; } /** @@ -120,7 +120,7 @@ private: SGBinding (const SGBinding &binding); std::string _command_name; - mutable SGCommandMgr::command_t _command; + mutable SGCommandMgr::Command* _command; mutable SGPropertyNode_ptr _arg; mutable SGPropertyNode_ptr _setting; }; diff --git a/simgear/structure/commands.cxx b/simgear/structure/commands.cxx index 266cdb4c..3a221321 100644 --- a/simgear/structure/commands.cxx +++ b/simgear/structure/commands.cxx @@ -52,12 +52,15 @@ SGCommandMgr::instance() } void -SGCommandMgr::addCommand (const std::string &name, command_t command) +SGCommandMgr::addCommand (const std::string &name, Command* command) { + if (_commands.find(name) != _commands.end()) + throw sg_exception("duplicate command name:" + name); + _commands[name] = command; } -SGCommandMgr::command_t +SGCommandMgr::Command* SGCommandMgr::getCommand (const std::string &name) const { const command_map::const_iterator it = _commands.find(name); @@ -80,7 +83,7 @@ SGCommandMgr::getCommandNames () const bool SGCommandMgr::execute (const std::string &name, const SGPropertyNode * arg) const { - command_t command = getCommand(name); + Command* command = getCommand(name); if (command == 0) return false; diff --git a/simgear/structure/commands.hxx b/simgear/structure/commands.hxx index 09f1c1bc..c03ef5dc 100644 --- a/simgear/structure/commands.hxx +++ b/simgear/structure/commands.hxx @@ -15,12 +15,13 @@ #include #include -#include #include #include -#include +// forward decls +class SGPropertyNode; + /** * Manage commands. * @@ -34,13 +35,65 @@ class SGCommandMgr { public: + /** + * Command functor object + */ + class Command + { + public: + virtual ~Command() { } + virtual bool operator()(const SGPropertyNode * arg) = 0; + }; - /** - * Type for a command function. - */ +private: + template< typename Fun > + class FunctionCommand : public Command + { + public: + FunctionCommand( const Fun* fun ) + : f_(fun) {} + + virtual bool operator()(const SGPropertyNode * arg) { return (*f_)(arg); } + private: + Fun* f_; + }; + + template< class ObjPtr, typename MemFn > + class MethodCommand : public Command + { + public: + MethodCommand( const ObjPtr& pObj, MemFn pMemFn ) : + pObj_(pObj), pMemFn_(pMemFn) {} + + virtual bool operator()(const SGPropertyNode * arg) + { + return ((*pObj_).*pMemFn_)(arg); + } + private: + ObjPtr pObj_; + MemFn pMemFn_; + }; + + /** + * Helper template functions. + */ + + template< typename Fun > + Command* make_functor( const Fun* fun ) + { + return new FunctionCommand(fun); + } + + template< class ObjPtr, typename MemFn > + Command* make_functor( const ObjPtr& pObj, MemFn pMemFn ) + { + return new MethodCommand(pObj, pMemFn ); + } + +public: + typedef bool (*command_t) (const SGPropertyNode * arg); - /** * Destructor. */ @@ -60,9 +113,18 @@ public: * a bool result. The argument is always a const pointer to * an SGPropertyNode (which may contain multiple values). */ - virtual void addCommand (const std::string &name, command_t command); - + template + void addCommand(const std::string& name, const FUNC* f) + { addCommand(name, make_functor(f)); } + + void addCommand (const std::string &name, Command* command); + template + void addCommand(const std::string& name, const OBJ& o, METHOD m) + { + addCommand(name, make_functor(o,m)); + } + /** * Look up an existing command. * @@ -70,7 +132,7 @@ public: * @return A pointer to the command, or 0 if there is no registered * command with the name specified. */ - virtual command_t getCommand (const std::string &name) const; + virtual Command* getCommand (const std::string &name) const; /** @@ -103,7 +165,7 @@ protected: private: - typedef std::map command_map; + typedef std::map command_map; command_map _commands; static SGMutex _instanceMutex; diff --git a/simgear/structure/state_machine_test.cxx b/simgear/structure/state_machine_test.cxx index d1a4d26d..2d31d63d 100644 --- a/simgear/structure/state_machine_test.cxx +++ b/simgear/structure/state_machine_test.cxx @@ -43,13 +43,19 @@ public: bool _state; }; -static int dummy_cmd_state = 0; - -bool dummyCommand(const SGPropertyNode* arg) +class DummyThing { - ++dummy_cmd_state; - return true; -} +public: + DummyThing() : dummy_cmd_state(0) { } + + bool someCommand(const SGPropertyNode* arg) + { + dummy_cmd_state++; + return true; + } + + int dummy_cmd_state; +}; #define COMPARE(a, b) \ if ((a) != (b)) { \ @@ -171,7 +177,8 @@ void testNoSourcesTransition() void testBindings() { SGCommandMgr* cmdMgr = SGCommandMgr::instance(); - cmdMgr->addCommand("dummy", dummyCommand); + DummyThing thing; + cmdMgr->addCommand("dummy", &thing, &DummyThing::someCommand); BUILD_MACHINE_1(); t2->addBinding(new SGBinding("dummy")); @@ -186,20 +193,20 @@ void testBindings() sm->update(1.0); trigger1->_state = false; COMPARE(sm->state()->name(), "b"); - COMPARE(dummy_cmd_state, 1); // exit state A + COMPARE(thing.dummy_cmd_state, 1); // exit state A trigger2->_state = true; sm->update(1.0); trigger2->_state = false; - COMPARE(dummy_cmd_state, 3); // fire transition 2, enter state C + COMPARE(thing.dummy_cmd_state, 3); // fire transition 2, enter state C - dummy_cmd_state = 0; + thing.dummy_cmd_state = 0; sm->changeToState(stateA); - COMPARE(dummy_cmd_state, 1); // enter state A + COMPARE(thing.dummy_cmd_state, 1); // enter state A trigger1->_state = true; sm->update(1.0); trigger1->_state = false; - COMPARE(dummy_cmd_state, 2); // exit state A + COMPARE(thing.dummy_cmd_state, 2); // exit state A //////////////////////// t3->addBinding(new SGBinding("dummy")); @@ -207,11 +214,11 @@ void testBindings() t3->addBinding(new SGBinding("dummy")); sm->changeToStateName("b"); - dummy_cmd_state = 0; + thing.dummy_cmd_state = 0; trigger3->_state = true; sm->update(1.0); trigger3->_state = false; - COMPARE(dummy_cmd_state, 4); // three transition bindings, enter A + COMPARE(thing.dummy_cmd_state, 4); // three transition bindings, enter A } void testParse()