简体   繁体   English

调用模板化成员函数与模板化全局函数以在通用工厂中创建对象

[英]Call templated member function vs. templated global function to create object in generic factory

Having searched online for different approaches to the Factory pattern, I've implemented my own version which I'm quite satisfied with. 在网上搜索了Factory模式的不同方法后,我实现了自己的版本,对此感到非常满意。 The Register member function creates a function pointer for the template type, storing it in a std::map using the index as the key. Register成员函数为模板类型创建一个函数指针,并使用索引作为键将其存储在std :: map中 This below code compiles and runs without hassle (Windows 7 64-bit, Code::Blocks 10.05 with GCC). 下面的代码可以轻松编译和运行(Windows 7 64位,带有GCC的Code :: Blocks 10.05)。

#ifndef OBJECT_FACTORY_HPP
#define OBJECT_FACTORY_HPP

#include <map>

namespace internal {
    template<typename BaseType, typename ObjectType>
    BaseType* CreateFunction() {
        return new ObjectType;
    }
}

template<typename BaseType, typename IndexType>
class ObjectFactory {
    public:
        ObjectFactory();

        template<typename ObjectType>
        bool Register(const IndexType& index);

        bool Unregister(const IndexType& index);

        BaseType* Create(const IndexType& index);

    private:
        typedef BaseType* (*creationCallback)();
        typedef std::map<IndexType, creationCallback> Registry;
        Registry registry;

//    private:
//        template<typename ObjectType>
//        BaseType* CreateFunction();
};

template<typename BaseType, typename IndexType>
ObjectFactory<BaseType, IndexType>::ObjectFactory() {
    registry.clear();
}

template<typename BaseType, typename IndexType>
template<typename ObjectType>
bool ObjectFactory<BaseType, IndexType>::Register(const IndexType& index) {
    if (registry.find(index) != registry.end())
        return false;

    registry[index] = &internal::CreateFunction<BaseType, ObjectType>;
    // registry[index] = &CreateFunction<ObjectType>; <-- FAILS!
    return true;
}

template<typename BaseType, typename IndexType>
bool ObjectFactory<BaseType, IndexType>::Unregister(const IndexType& type) {
    if (registry.find(type) == registry.end())
        return false;

    return (registry.erase(type) == 1);
}

template<typename BaseType, typename IndexType>
BaseType* ObjectFactory<BaseType, IndexType>::Create(const IndexType& index) {
    if (registry.find(index) == registry.end())
        return NULL;

    return registry[index]();
}

//template<typename BaseType, typename IndexType>
//template<typename ObjectType>
//BaseType* ObjectFactory<BaseType, IndexType>::CreateFunction() {
//    return new ObjectType();
//}

#endif

My original approach was to have CreateFunction as a private member to hide it from users (look at the commented sections, note the non-member function takes an additional template argument). 我最初的方法是让CreateFunction作为私有成员向用户隐藏(请查看注释部分,请注意非成员函数需要附加的模板参数)。 However, this fails with the following error messages, all pointing to the line where I store the function pointer in the Register member function: 但是,这失败并显示以下错误消息,所有错误消息都指向我在Register成员函数中存储函数指针的行:

In member function 'bool ObjectFactory<BaseType, IndexType>::Register(const IndexType&) [with ObjectType = Triangle, BaseType = Shape, IndexType = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]':|
instantiated from here
error: no matches converting function 'CreateFunction' to type 'class Shape* (*)()'|
error: candidates are: template<class ObjectType> BaseType* ObjectFactory::CreateFunction() [with ObjectType = ObjectType, BaseType = Shape, IndexType = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]|

I wrote a small test client for testing: 我写了一个小的测试客户端进行测试:

#include "ObjectFactory.hpp"
#include <iostream>

class Shape {
    public:
        Shape() {}
        virtual void print() { std::cout << "At heart, I'm a shape"; }
};

class Triangle : public Shape {
    public:
        Triangle() {}
        void print() { Shape::print(); std::cout << ", but I'm truly a triangle" << std::endl; }
};

int main(int argc, char* argv[]) {
    ObjectFactory<Shape, std::string> objectFactory;

    objectFactory.Register<Triangle>("triangle");

    Shape* triangle = objectFactory.Create("triangle");
    triangle->print();

    delete triangle;

    return 0;
}

Is this even possible? 这有可能吗? I feel it should be, and I understand that I'm somehow calling the incorrect member function, but I'm can't see why. 我认为应该是这样,并且我知道我以某种方式调用了错误的成员函数,但是我不知道为什么。 On a related note, since someone might mention it, I plan on using *boost::shared_ptr* for the allocation instead of the plain new operator ;) Any other advice or suggestions about the implementation are also welcome. 在相关说明中,由于有人可能会提到它,因此我计划使用* boost :: shared_ptr *进行分配,而不是使用普通的new运算符;)也欢迎其他有关实现的建议。

From within our file, we anonymize our namespace: 在文件中,我们将名称空间匿名化

// namespace internal {  
   namespace          {
    template<typename BaseType, typename ObjectType> 
    BaseType* CreateFunction() { ... }  
}

The non-static function can now be called as originally written, without the namespace qualification: 现在可以按原始编写的方式调用非静态函数,而无需使用名称空间:

// registry[index] = &internal::CreateFunction<ObjectType>;  
   registry[index] =           &CreateFunction<ObjectType>; 

The file-scoped CreateFunction function is invisible to code outside of the translation unit and is called only by the class ObjectFactory . 文件范围的CreateFunction函数对于翻译单元外部的代码不可见,并且仅由ObjectFactoryObjectFactory

This approximates well the private access specifier for the CreateFunction (from within the ObjectFactory ) proposed in the question. 这很好地近似了问题中提出的CreateFunctionprivate访问说明CreateFunction (来自ObjectFactory )。

The creating function should be a static member: 创建函数应为静态成员:

template<typename ObjectType> static BaseType* CreateFunction();

(the above code, without the static , is commented-out in your example). (上面的代码(不含static )在您的示例中已被注释掉)。

Now your other commented-out code is valid: 现在您的其他注释掉的代码是有效的:

registry[index] = &CreateFunction<ObjectType>;

Edit: to clear up some confusion: 编辑:清除一些混乱:

The problem here is mostly in syntax. 这里的问题主要是语法。 Templates and private are not important here, so let's simplify the situation: 模板和private在这里并不重要,因此让我们简化一下情况:

struct ObjectFactory {
    void CreateFunction() {std::cout << "message";}
    void AnotherFunction()
    {
        ... = &CreateFunction; // what's that?
    }
};

If CreateFunction is a nonstatic member function, the syntax &CreateFunction is incorrect; 如果CreateFunction是非静态成员函数,则语法&CreateFunction不正确; i guess that gcc complains just about that. 我想海湾合作委员会只是抱怨。 To make things worse, however, MS Visual Studio tries to "help" you by internally "correcting" the syntax to &ObjectFactory::CreateFunction and trying to use that; 然而,更糟糕的是,MS Visual Studio尝试通过内部“更正” &ObjectFactory::CreateFunction的语法并尝试使用它来“帮助”您; but it fails with unintelligible error messages (the fact that you have templates aggravates them even more). 但是它失败并显示错误消息(您拥有模板的事实进一步加剧了它们)。

I am not sure about the Visual Studio part (i remember having such trouble with MSVC 6 a few years ago; a newer version of Visual Studio doesn't have this problem). 我不确定Visual Studio的部分(我记得几年前在MSVC 6上遇到过此类麻烦; Visual Studio的较新版本没有此问题)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM