Design question about create factory method.

Started by
10 comments, last by HexDump 16 years, 4 months ago
If I understand what you're asking, it's possible, but not a particularly good idea.
#include <boost/smart_ptr.hpp>#include <boost/mpl/list.hpp>#include <boost/function.hpp>#include <typeinfo>#include <map>#include <iostream>using namespace boost;class TypeInfoProxy {  public:    TypeInfoProxy(const std::type_info * t) : ti(t) {}    const std::type_info * ti;};inline bool operator<(const TypeInfoProxy & lhs, const TypeInfoProxy & rhs) {  return lhs.ti->before(*rhs.ti) != 0;}inline bool operator>(const TypeInfoProxy & lhs, const TypeInfoProxy & rhs) {  return rhs.ti->before(*lhs.ti) != 0;}inline bool operator<=(const TypeInfoProxy & lhs, const TypeInfoProxy & rhs) {  return !(rhs > lhs);}inline bool operator>=(const TypeInfoProxy & lhs, const TypeInfoProxy & rhs) {  return !(rhs < lhs);}inline bool operator==(const TypeInfoProxy & lhs, const TypeInfoProxy & rhs) {  return ((*lhs.ti) == (*rhs.ti)) != 0;}inline bool operator!=(const TypeInfoProxy & lhs, const TypeInfoProxy & rhs) {  return ((*lhs.ti) != (*rhs.ti)) != 0;}template <typename T, typename TokenType>class ICreator {  public:    virtual shared_ptr<T> create(void * args) = 0;    virtual ~ICreator() {}};template <typename T>struct Package {};template <typename U1>struct Package<mpl::list<U1> > {  U1 arg1;};template <typename U1, typename U2>struct Package<mpl::list<U1, U2> > {  U1 arg1;  U2 arg2;};template <typename T, typename TypeList, typename TokenType>class ConcreteCreator : public ICreator<T, TokenType> {};template <typename T, typename TokenType>class ConcreteCreator<T, mpl::list<>, TokenType> : public ICreator<T, TokenType> {  public:    typedef function<shared_ptr<T> (void)> FunctorType;    ConcreteCreator(FunctorType functor) : functor_(functor) {}      virtual shared_ptr<T> create(void *) {      return functor_();    }    virtual ~ConcreteCreator() {}  private:    FunctorType functor_;};template <typename T, typename TokenType, typename U1>class ConcreteCreator<T, mpl::list<U1>, TokenType> : public ICreator<T, TokenType> {  public:    typedef function<shared_ptr<T> (U1)> FunctorType;    typedef Package<mpl::list<U1> > PackageType;        ConcreteCreator(FunctorType functor) : functor_(functor) {}      virtual shared_ptr<T> create(void * ptr) {      PackageType * package = reinterpret_cast<PackageType *>(ptr);      return functor_(package->arg1);    }    virtual ~ConcreteCreator() {}  private:    FunctorType functor_;};template <typename T, typename TokenType, typename U1, typename U2>class ConcreteCreator<T, mpl::list<U1, U2>, TokenType> : public ICreator<T, TokenType> {  public:    typedef function<shared_ptr<T> (U1, U2)> FunctorType;    typedef Package<mpl::list<U1, U2> > PackageType;        ConcreteCreator(FunctorType functor) : functor_(functor) {}      virtual shared_ptr<T> create(void * ptr) {      PackageType * package = reinterpret_cast<PackageType *>(ptr);      return functor_(package->arg1, package->arg2);    }    virtual ~ConcreteCreator() {}  private:    FunctorType functor_;};template <typename T, typename TokenType>class Factory {  public:    shared_ptr<T> create(TokenType type_token) {      KeyType key = std::make_pair(type_token, &typeid(void));      CreatorMap::iterator itr = creator_map_.find(key);      if (itr == creator_map_.end())        throw std::runtime_error("No registered creator");      return (itr->second)->create(0);    }    template <typename U1>    shared_ptr<T> create(TokenType type_token, U1 a1) {      KeyType key = std::make_pair(type_token, &typeid(mpl::list<U1>));      CreatorMap::iterator itr = creator_map_.find(key);      if (itr == creator_map_.end())        throw std::runtime_error("No registered creator");      Package<mpl::list<U1> > package = { a1 };      return (itr->second)->create(&package);    }    template <typename U1, typename U2>    shared_ptr<T> create(TokenType type_token, U1 a1, U2 a2) {      KeyType key = std::make_pair(type_token, &typeid(mpl::list<U1, U2>));      CreatorMap::iterator itr = creator_map_.find(key);      if (itr == creator_map_.end())        throw std::runtime_error("No registered creator");      Package<mpl::list<U1, U2> > package = { a1, a2 };      return (itr->second)->create(&package);    }        void register_creator(TokenType type, function<shared_ptr<T> (void)> func) {      CreatorPtr ptr(new ConcreteCreator<T, mpl::list<>, TokenType>(func));      KeyType key = std::make_pair(type, &typeid(void));      creator_map_[key] = ptr;    }    template <typename U1>    void register_creator(TokenType type, function<shared_ptr<T> (U1)> func) {      CreatorPtr ptr(new ConcreteCreator<T, mpl::list<U1>, TokenType>(func));      KeyType key = std::make_pair(type, &typeid(mpl::list<U1>));      creator_map_[key] = ptr;    }    template <typename U1, typename U2>    void register_creator(TokenType type, function<shared_ptr<T> (U1, U2)> func) {      CreatorPtr ptr(new ConcreteCreator<T, mpl::list<U1, U2>, TokenType>(func));      KeyType key = std::make_pair(type, &typeid(mpl::list<U1, U2>));      creator_map_[key] = ptr;    }  private:    typedef std::pair<TokenType, TypeInfoProxy> KeyType;    typedef shared_ptr<ICreator<T, TokenType> > CreatorPtr;    typedef std::map<KeyType, CreatorPtr> CreatorMap;    CreatorMap creator_map_;};struct Shape {  virtual void print(void) = 0;  virtual ~Shape() {}};typedef shared_ptr<Shape> ShapePtr;struct Square : Shape {  Square(int side) : side(side) {}  void print(void) { std::cout << "I'm a square of size " << side << "!\n"; }    static ShapePtr creator(int s) { return ShapePtr(new Square(s)); }    int side;};struct Rectangle : Shape {  Rectangle(int h, int w) : h(h), w(w) {}  void print(void) { std::cout << "I'm a rectangle of sides " << h << " and " << w << "!\n"; }  static ShapePtr creator(int h, int w) { return ShapePtr(new Rectangle(h, w)); }  int h, w;};int main(int, char **) {  Factory<Shape, int> shape_factory;  shape_factory.register_creator(0, function<ShapePtr (int)>(&Square::creator));  shape_factory.register_creator(1, function<ShapePtr (int, int)>(&Rectangle::creator));    ShapePtr square = shape_factory.create(0, 5);  square->print();    ShapePtr rect = shape_factory.create(1, 3, 4);  rect->print();    return 0;}

This should compile and run on a reasonably recent compiler with boost 1.34.1 installed. Take a look at the creation calls at the end. Now change the second call from shape_factory.create(1, 3, 4) to shape_factory.create(1, 3, 4.0) It will now magically throw an exception because it doesn't know how to handle the double, since the creation handler was installed as (int, int).
Advertisement
Hehe sicrane, nice stuff, but somehow dificult for me :). I think I will go for waht I said before, I will move my create function to concrete children clases, this way, I know the signature, and can create just the type of objects I need.

Perhaps it is just dirty and ugly, but for now I think is a good option.

Thanks for all the answers,
HexDump.

This topic is closed to new replies.

Advertisement