Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
200 views
in Technique[技术] by (71.8m points)

c++ - Instantiating classes by name with factory pattern

Suppose I have a list of classes A, B, C, ... which all inherit from Base.

I get the class name as a string from the user, and I want to instantiate the right class and return a pointer to Base. How would you implement this?

I thought of using a hash-table with the class name as the key, and a function pointer to a function that instantiates the right class and returns a Base *.

However, I think I might be able to use the factory pattern here and make it a lot easier, but I just can't quite remember it well, so I though I'd ask for suggestions.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Here is a generic factory example implementation:

template<class Interface, class KeyT=std::string>
struct Factory {
    typedef KeyT Key;
    typedef std::auto_ptr<Interface> Type;
    typedef Type (*Creator)();

    bool define(Key const& key, Creator v) {
        // Define key -> v relationship, return whether this is a new key.
        return _registry.insert(typename Registry::value_type(key, v)).second;
    }
    Type create(Key const& key) {
        typename Registry::const_iterator i = _registry.find(key);
        if (i == _registry.end()) {
            throw std::invalid_argument(std::string(__PRETTY_FUNCTION__) +
                                        ": key not registered");
        }
        else return i->second();
    }

    template<class Base, class Actual>
    static
    std::auto_ptr<Base> create_func() {
        return std::auto_ptr<Base>(new Actual());
    }

private:
    typedef std::map<Key, Creator> Registry;
    Registry _registry;
};

This is not meant to be the best in every circumstance, but it is intended to be a first approximation and a more useful default than manually implementing the type of function stijn mentioned. How each hierarchy should register itself isn't mandated by Factory, but you may like the method gf mentioned (it's simple, clear, and very useful, and yes, this overcomes the inherent problems with macros in this case).

Here's a simple example of the factory:

struct Base {
    typedef ::Factory<Base> Factory;
    virtual ~Base() {}
    virtual int answer() const = 0;

    static Factory::Type create(Factory::Key const& name) {
        return _factory.create(name);
    }
    template<class Derived>
    static void define(Factory::Key const& name) {
        bool new_key = _factory.define(name,
            &Factory::template create_func<Base, Derived>);
        if (not new_key) {
            throw std::logic_error(std::string(__PRETTY_FUNCTION__) +
                                   ": name already registered");
        }
    }

private:
    static Factory _factory;
};
Base::Factory Base::_factory;

struct A : Base {
    virtual int answer() const { return 42; }
};

int main() {
    Base::define<A>("A");
    assert(Base::create("A")->answer() == 42);
    return 0;
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...