4

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.

Jesse Beder
  • 33,081
  • 21
  • 109
  • 146
Idan
  • 5,717
  • 10
  • 47
  • 84

4 Answers4

7

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;
}
Community
  • 1
  • 1
3

the quickest yet very usable way in a lot of areas, would be something like

Base* MyFactoryMethod( const std::string& sClass ) const
{
  if( sClass == "A" )
    return CreateNewA();
  else if( sClass == "B" )
    return new CreateClassB();
  //....
  return 0;
}

A* CreateClassA() const
{
  return new A();
}
stijn
  • 34,664
  • 13
  • 111
  • 163
  • Yeah, I also usually start with something similar - class with similar "create" method. Then if there a lot of classes(or library user is supposed to add some) I add the "true" factory behind it (map,hash,whatever) with registrations. – MaR Dec 02 '09 at 12:09
  • maybe i should read about the factory pattern... i can't remember how it is implemented. – Idan Dec 02 '09 at 12:43
2

You could also look into the Boost class factory implementation.

  • If there's only a few derived classes you can use an "if, else" list.
  • If you plan to have many derived classes it's better to sort out the class registration process (as Georg mentioned) than to use an "if, else" list.

Here's a simple example using the Boost factory method and class registration:

typedef boost::function<Parent*()> factory;

// ...

std::map<std::string, factory> factories;

// Register derived classes
factories["Child1"] = boost::factory<Child1*>();
factories["Child2"] = boost::factory<Child2*>();

// ...

// Instantiate chosen derived class
auto_ptr<Parent> pChild = auto_ptr<Parent>(factories["Child1"]());
Community
  • 1
  • 1
keplerian
  • 512
  • 1
  • 8
  • 19
  • 1
    Hi, I tried this solution but can't make it compile. The problem I have is at line `factories["Child1"] = boost::factory();` There is a typecheck problem, the equality is not defined. If I write `boost::factory();` it works but it's not useful. Have I missed something ? – Alexandre Jun 10 '12 at 00:36
1

First off, yes, that is just what the factory pattern is for.
(By the way, your other idea is a possible implementation of the factory pattern)

If you intend to do this for a large project (if not, just go with stijns answer), you might want to consider using an associative container somewhere instead of explicit branching and maybe even moving the registration responsibility into the classes to

  • avoid code changes in one additional place (your factory)
  • and in turn avoid possibly very long recompilation times (for in-header-implementations) when adding a class

To achieve convenient registration in the classes you could use something like this suggestion and add a function pointer or a functor to the entries that instantiates the derived class and returns a pointer to the base.
If you're not afraid of macros you can then add classes to the factory by just adding one tiny macro to its declaration.

Community
  • 1
  • 1
Georg Fritzsche
  • 97,545
  • 26
  • 194
  • 236