#ifndef __GenericDataNodes_h_ #define __GenericDataNodes_h_ #include #include "DataNodes.h" #include "StandardPlaceholders.h" class GenericDataNode; typedef GenericDataNode *(*GenericDataNodeConstructor) (DataNodeArgument const &args); class GenericDataNode : public DataNode { public: virtual void getInteger(bool &valid, Integer &value) const { valid = false; } virtual void getDouble(bool &valid, double &value) const { valid = false; } virtual void getBoolean(bool &valid, bool &value) const { valid = false; } virtual void getString(bool &valid, std::string &value) const { valid = false; } // This is aimed at GenericDataNodeFactory. static DataNodeLink *find(DataNodeListener *listener, int msgId, GenericDataNode *&node, std::string const &name, DataNodeArgument args, GenericDataNodeConstructor constructor); }; class GenericDataNodeFactory : public DataNodeArgumentBase { private: const GenericDataNodeConstructor _constructor; const std::string _constructorName; const DataNodeArgument _args; GenericDataNodeFactory(GenericDataNodeConstructor constructor, std::string constructorName, DataNodeArgument args) : _constructor(constructor), _constructorName(constructorName), _args(args) { } // You cannot store a pointer to a constructor. But you can store a pointer // to an instantiated version of createHelper. This has exactly the right // signature to be of type GenericDataNodeConstructor. template < class T > static GenericDataNode *createHelper(DataNodeArgument const &args) { return new T(args); } public: std::string asString() const; DataNodeArgument replace(std::string const &name, DataNodeArgument const &value) const; // The idea is that this can be easily and automatically converted to a // DataNodeArgument. But if you plan to use the factory immediately, // you might just use this pointer as is. template< class T > static GenericDataNodeFactory *create(DataNodeArgument args) { return new GenericDataNodeFactory(createHelper< T >, typeid(T).name(), args); } // find() always creates and returns a link. The assumption is that the // caller will have a variable to store the data node for later. But link // will only be used immediately in a call to addAutoLink(). This isn't // the only case, but it is the most common case. DataNodeLink *find(DataNodeListener *listener, int msgId, GenericDataNode *&node) const; DataNodeLink *find(GenericDataNode *&node) const { return find(NULL, 0, node); } // We assume, for simplicity, that all factories will be added before we // create multiple threads, and then the factories can be accessed in any // thread. There are other ways of accessing this data. These routines // do not make any specific assertions about threads and naively add or // look up data exactly as requested. "sf" is short for "store factory". // The initialization part of many units gets just a little bit ugly. // Having a simple procedure with a short name should help with the common // tasks. static DataNodeArgument findFactory(std::string const &name, bool noComplain = false); static void storeFactory(std::string const &name, DataNodeArgument const &factory); template < class T > static void sf(std::string const &name, DataNodeArgument const &args) { // Create and store a factory with the given name and args. storeFactory(name, GenericDataNodeFactory::create< T >(args)); } template < class T > static void sf(std::string const &name, DataNodeArgument const &arg1, DataNodeArgument const &arg2) { // Create and store a factory with the given name and args. sf< T >(name, argList(arg1, arg2)); } template < class T > static void sf(std::string const &name, DataNodeArgument const &arg1, DataNodeArgument const &arg2, DataNodeArgument const &arg3) { // Create and store a factory with the given name and args. sf< T >(name, argList(arg1, arg2, arg3)); } template < class T > static void sf(std::string const &name, DataNodeArgument const &arg1, DataNodeArgument const &arg2, DataNodeArgument const &arg3, DataNodeArgument const &arg4) { // Create and store a factory with the given name and args. sf< T >(name, argList(arg1, arg2, arg3, arg4)); } template < class T > static void sf(std::string const &name, DataNodeArgument const &arg1, DataNodeArgument const &arg2, DataNodeArgument const &arg3, DataNodeArgument const &arg4, DataNodeArgument const &arg5) { // Create and store a factory with the given name and args. sf< T >(name, argList(arg1, arg2, arg3, arg4, arg5)); } template < class T > static void storeStandardFactory(std::string const &name) { sf< T >(name, symbolPlaceholderObject); } // This exists only for convenience. A common way to use a factory is to // start from a DataNodeArgument which is really a factory, replace one // item (the symbol), then convert the new DataNodeArgument to a factory, // and call find on that. This puts all those steps together. static DataNodeLink *replaceAndFind(DataNodeArgument const &factory, DataNodeListener *listener, int msgId, GenericDataNode *&node, std::string const &name, DataNodeArgument const &value); }; // One of the basic requirements for a generic data node is that you can create // it from a factory. However, sometimes you don't have a factory handy, and // you want to create a data node from just a class and a list of arguments. // This simplifies the task by creating and disposing of the factory // automatically. This makes generic data nodes just as easy to use as // other data nodes. // // We pick up the type of the data node from the variable where it is being // stored. The other examples of find all return a GenericDataNode. // This seemed simpler for the user, and it is probably right most of the // time. Other verions of find can't be this specific because you don't always // know in advance what type to expect. template< class T > DataNodeLink *findGeneric(DataNodeListener *listener, int msgId, T *&node, DataNodeArgument const &args) { GenericDataNodeFactory *factory = GenericDataNodeFactory::create< T >(args); GenericDataNode *tempNode; DataNodeLink *result = factory->find(listener, msgId, tempNode); delete factory; node = dynamic_cast< T * >(tempNode); return result; } #endif