1

Factory パターンへのさまざまなアプローチをオンラインで検索した結果、非常に満足している独自のバージョンを実装しました。Registerメンバー関数は、テンプレート型の関数ポインターを作成し、インデックスをキーとして使用してstd::mapに格納します。以下のコードは問題なくコンパイルおよび実行されます (Windows 7 64 ビット、Code::Blocks 10.05 with GCC)。

#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

私の最初のアプローチは、CreateFunctionをプライベート メンバーとしてユーザーから非表示にすることでした (コメント セクションを見てください。非メンバー関数は追加のテンプレート引数を取ることに注意してください)。ただし、これは次のエラー メッセージで失敗し、すべて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> >]|

テスト用に小さなテスト クライアントを作成しました。

#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;
}

これは可能ですか?そうあるべきだと思いますし、どういうわけか間違ったメンバー関数を呼び出していることは理解していますが、その理由はわかりません。関連するメモとして、誰かが言及するかもしれないので、私は単純な new 演算子の代わりに割り当てに *boost::shared_ptr* を使用する予定です;) 実装に関するその他のアドバイスや提案も大歓迎です。

4

2 に答える 2

1

ファイル内から、名前空間を匿名化します。

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

名前空間修飾なしで、非静的関数を最初に記述されたとおりに呼び出すことができるようになりました。

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

ファイル スコープのCreateFunction関数は、翻訳単位の外側のコードからは見えず、クラスによってのみ呼び出されObjectFactoryます。

これは、質問で提案されている( 内から) のprivateアクセス指定子によく似ています。CreateFunctionObjectFactory

于 2012-05-09T01:41:54.750 に答える
0

作成関数は静的メンバーである必要があります。

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

(上記のコードはstatic、例ではコメントアウトされています)。

これで、コメントアウトされた他のコードが有効になります。

registry[index] = &CreateFunction<ObjectType>;

編集:混乱を解消するために:

ここでの問題は主に構文にあります。ここではテンプレートとテンプレートprivateは重要ではないため、状況を単純化しましょう。

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

が非静的メンバー関数の場合CreateFunction、構文&CreateFunctionが正しくありません。私はgccがそれについて不平を言っていると思います。しかし、さらに悪いことに、MS Visual Studio は構文を内部的に「修正」し、&ObjectFactory::CreateFunctionそれを使用しようとすることで、ユーザーを「助け」ようとします。しかし、理解できないエラーメッセージで失敗します (テンプレートがあるという事実は、それらをさらに悪化させます)。

Visual Studio の部分についてはよくわかりません (数年前に MSVC 6 でこのような問題が発生したことを覚えています。新しいバージョンの Visual Studio にはこの問題はありません)。

于 2012-05-08T19:02:14.970 に答える