C ++通用对象工厂按字符串名称


C++ generic object factory by string name

我需要一种方法来根据作为std::string传递的类名实例化对象。这目前有效,但需要概括:

1
2
3
4
5
void* create(std::string name) {
    if(name =="classOne") return new ClassOne();
    else if(name =="classTwo") return new ClassTwo();
    /* ... */
}

我没有的:

  • 对要实例化的类的控制:可以是30个方类。不能对此类进行任何更改(例如,基祖先、多态创建者方法等…)
  • 完整的类名列表:以后可以添加更多的类,并且不应该在这个工厂中发生更改。
  • 围绕要实例化的类的包装器:由于前面两个点。

别的什么都可以。

最佳用例场景将是:

1
2
3
4
5
6
7
int main() {
    void *obj = create("classTree"); // create object based on the string name
    /* ... */
    // once we know by context which specific class we are dealing with
    ClassTree *ct = (ClassTree*)obj; // cast to appropiate class
    std::cout << ct->getSomeText() << std::endl; // use object
}

考虑到要实例化的对象可能来自一个类或一个结构,作为一个侧面,也可能是不相关的注释。

添加的信息

我看到需要更多的上下文。下面是我的特定用例,简化了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// registration mechanism
int main() {
    std::map< std::string, void(*func)(std::string, void*) > processors; // map of processors by class name
    processors["ClassFour"] = (void(*)(std::string, void*)) &classFourMessageProcessor; // register processor (cast needed from specific to generic)
}
// function receiving string messages
void externalMessageHandler(std::string msg) {
    std::string objType = extractTypeFromMessageHeader(msg); // extract type from message
    // now that we know what we are dealing with, create the specific object
    void *obj = create(objType); // << creator needed
    processors[objType](msg, obj); // dispatch message to process
}
// previously registered message processor
void classFourMessageProcessor(std::String msg, ClassFour *obj) {
    std::string streetAddress = msg.substr(10, 15); // knowing the kind of message we can extract information
    obj->moveTheEtherTo(streetAddress); // use the created object
}

添加的信息

我用C++ 11用最新的GNU编译器。


您可以为每种类类型存储一个工厂函数。一个简单的方法是使用模板

1
2
3
4
template <typename T>
void* creator() {
  return new T();
}

并将它们存储在地图中(即"class4"链接到creatorClassFourMessageProcessor)。

编辑:为了澄清,processors变为

1
2
3
4
5
typedef void* (*CreatorFunc)();
typedef void (*ProcessorFunc)(std::string, void*);

typedef std::pair<CreatorFunc, ProcessorFunc> Entry;
std::map< std::string, Entry > processors;

添加一个新类非常简单

1
processors["SomeClass"] = Entry(creator<SomeClass>, ClassFourMessageProcessor);


也许下面的关于lokup表的讨论将是一个很好的解决方案。

(注:我不知道你使用的是编译器,所以这个解决方案是C++ 03,如果你使用的是带有C++ 11支持的编译器,你可以使用无序的地图代替地图)

(注意2:您也可以使用智能指针,并注意返回值,在这个示例中,我只想显示一个函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <iostream>
#include <string>
#include <map>
#include <vector>


struct ClassMaker
{
    virtual void* getInstance() const = 0;
    virtual ~ClassMaker() {}
};

class Factory
{
private:
    std::map<std::string, ClassMaker*> lokupTable;
    typedef typename std::map<std::string, ClassMaker*>::iterator Iterator;
public:
    void addClass(const std::string& key, ClassMaker* const newClassMaker)
    {
        lokupTable[key] = newClassMaker;
    }

    void* create(const std::string& key)
    {
        void* result = NULL;
        Iterator it = lokupTable.find(key);

        if(it != lokupTable.end())
            result = (it->second)->getInstance();
        return result;
    }

    void releaseTable()
    {
        for (Iterator it = lokupTable.begin(); it != lokupTable.end(); ++it)
            delete it->second;
    }
};

struct IntCreator : public ClassMaker
{
    void* getInstance() const
    {
        return new int;
    }

};

struct StringCreator : public ClassMaker
{
    void* getInstance() const
    {
        return new std::string;
    }

};



int main()
{
    Factory myFactory;
    myFactory.addClass("int", new IntCreator);
    myFactory.addClass("string", new StringCreator);

    int* myInt = reinterpret_cast<int*>(myFactory.create("int"));
    *myInt = 10;
    std::cout<< *myInt << std::endl;
    delete myInt;
    myFactory.releaseTable();
    return 0;
}


以下是一个例子:

  • 对于每个类,创建一个createinsance()函数(不是方法),该函数实例化一个实例并返回一个指针强制转换为void*。注意,这个函数不是类的一部分,只是一个普通函数。
  • 维护指向CreateInstance类型函数的字符串到函数指针的映射。
  • "注册"映射中的每个相关类-将字符串函数指针对添加到映射中。
  • 现在,generic create将在映射中搜索字符串并调用特定的createInstane,返回新实例的ptr。
  • 现在,您没有对类进行任何更改,并且可以在不重新编程工厂的情况下添加更多的类。您可能会将至少1作为模板-一定要让编译器实例化特定的实现。


    你会考虑Boost.mpl吗?与STL不同,它允许创建包含类型而不是实例的容器。有一个从字符串到类型的映射会给你想要的工厂,不是吗?