Few small problems with the factory.
You are using a pointer to a method.
typedef Task* (*create_callback)(map<string, string> vMap);
As the way of creating your object. This is very C like. It also introduces the possibilities of passing a NULL pointer (which is not good).
The more idiomatic way to to declare a functor:
struct TaskCreator
{
// Leaving Task* for similarities to current code (I will get back to it).
Task* operator()(std::map<std::string, std::string> vMap) const
{
return build(vMap);
}
private:
virtual Task* build(std::map<std::string, std::string> vMap) const = 0;
};
Register objects
Now you should register objects derived from this class with the factory. This will prevent you accidentally passing NULL as a callback.
// This looks incant because there are no `*` here
// But there is a hidden '*' in create_callback
static void register_task(const string& type, create_callback cb);
// I would change this too:
// Notice I am not using `static` here. I will get to that in a second.
void register_task(const string& type, TaskCreator& cb);
// ^^^ That is important.
// Alternatively you can register via smart pointers.
// But I prefer to pass references. Then make the `TaskCreator` automatically
// Register themselves in their constructor.
Order of construction
You also have another invisible problem in the order of construction across compilation units. Remember that the order of construction of static storage duration
objects is undefined across compilation units.
static map<string, create_callback> mTasks;
This object is constructed before main() is called. The problem is you do not know exactly when. As a result if you call register_task()
before main() you don't know if it will work because the storage mTasks
may not have been created yet.
There are a couple of ways of solving this. The easiest to use a static member of a function as the storage.
static map<string, create_callback>& getTaskStorage()
{
static map<string, create_callback> mTasks;
return mTasks;
}
But I would go the other way. And use a factory object pattern.
Factory object
You are using a set of static methods as your factory. I would swing this around and actually create a factory object. The problem with static functions is that it is hard to use test code with them. It is much easier to mock an object than static API.
Ownership
Last. You are not expressing ownership semantics for the created object.
static Task* create_task(const string& type,map<string, string> vMap);
A pointer Task*
is totally useless in this situation as you have no idea if the ownership is being retained by the factory (and it will destroy it) or if ownership is being returned to the caller so he can destroy it.
Here you have two options:
- Return a reference.
- This indicates that the factory is retaining ownership of the created object. This means the object will be destroyed when the factory is destroyed (This implies a factory object).
- Return a smart pointer
- By returning a smart pointer you are indicating somthing else has ownership. The type of ownersip depends on the type of smart pointer (unique_ptr: ownership is returned to the caller, shared_ptr: ownership is shared and the loss of the last reference will delelte it).
Further Advice.
I would suggest some more specific recommendations. But unfortunately you do not provide the expected usage semantics of your factory. Thus I may write something that is tially usless. If you can write some more details on how you think the factory is going to be used I can give you some more concrete information.
Errors in the code:
class Task
{
static Task* create(map<string, string>);
};
If you take this address it does not match:
typedef Task* (*create_callback)(map<string, string> vMap);
There is nothing in the standard that guarantees that the calling convention of a normal function matches the calling convention of static methods. It just happens to work on your compiler but it is non portable.
As stated above: What happens of somebody registers a NULL function.
return mTasks[type](vMap);
This line is likely to fail. Using your method you need to validate that the pointer is not NULL wither at the point of setting or at the point were it is used.