минимальное отражение в C ++ - PullRequest
       4

минимальное отражение в C ++

2 голосов
/ 08 сентября 2010

Я хочу создать фабрику классов, и я хотел бы использовать отражение для этого. Мне просто нужно создать объект с заданной строкой и вызвать только несколько известных методов.

Как я могу это сделать?

Ответы [ 4 ]

3 голосов
/ 08 сентября 2010

Тебе придётся катиться самостоятельно.Обычно у вас есть карта строк для функций создания объектов.
Вам понадобится что-то вроде следующего:

class thing {...};
/*
class thing_A : public thing {...};
class thing_B : public thing {...};
class thing_C : public thing {...};
*/

std::shared_ptr<thing> create_thing_A(); 
std::shared_ptr<thing> create_thing_C(); 
std::shared_ptr<thing> create_thing_D();

namespace {
  typedef std::shared_ptr<thing> (*create_func)();

  typedef std::map<std::string,create_func> creation_map;
  typedef creation_map::value_type creation_map_entry;
  const creation_map_entry creation_map_entries[] = { {"A", create_thing_A}
                                                    , {"B", create_thing_B}
                                                    , {"C", create_thing_C} };
  const creation_map creation_funcs( 
          creation_map_entries, 
          creation_map_entries + sizeof(creation_map_entries)
                               / sizeof(creation_map_entries[0] );
}

std::shared_ptr<thing> create_thing(const std::string& type)
{
  const creation_ma::const_iterator it = creation_map.find(type);
  if( it == creation_map.end() ) {
     throw "Dooh!"; // or return NULL or whatever suits you
  }
  return it->second();
}

Есть и другие способы сделать это (например, иметь карту строк для объекты , из которых можно клонировать ), но я думаю, что все они сводятся к наличию карты строк с чем-то, относящимся к конкретным типам.

2 голосов
/ 08 сентября 2010

Нет отражения в C ++, напрямую поддерживаемого стандартом.

Однако C ++ является достаточно низкоуровневым, так что вы можете реализовать некоторую минимальную поддержку рефлексии для выполнения поставленной задачи.

Для простой задачи создания Фабрики вы обычно используете подход Prototype:

class Base
{
public:
  virtual Base* clone() const = 0;
  virtual ~Base();
};

class Factory
{
public:
  std::unique_ptr<Base> get(std::string const& name);

  void set(std::string const& name, std::unique_ptr<Base> b);

private:
  boost::ptr_map<std::string,Base> mExemplars;
};

Конечно, те "известные методы", о которых вы говорите, должны быть определены в классе Base, который действует как интерфейс.

1 голос
/ 08 сентября 2010

В C ++ нет отражения, поэтому вам следует переформулировать свой вопрос, пытаясь объяснить, какие требования вы бы выполнили с помощью отражающей его части.

В зависимости от ваших реальных ограничений и требований, существуетЕсть несколько вещей, которые вы можете сделать.Первый подход, который я выбрал бы, - это создание абстрактной фабрики, где конкретные фабрики могут регистрироваться и предоставлять простой интерфейс:

class Base {}; // shared base by all created objects
class ConcreteFactoryBase {
public:
   virtual ~ConcreteFactoryBase() {}
   virtual Base* create() const = 0;   // actual construction
   virtual std::string id() const = 0; // id of the types returned
};
class AbstractFactory
{
   typedef std::map<std::string, ConcreteFactory* > factory_map_t;
public:
   void registerFactory( ConcreteFactoryBase* factory ) {
      factories[ factory->id() ] = factory;
   }
   Base* create( std::string const & id ) const {
      factory_map_t::const_iterator it = factories.find( id );
      if ( it == factories.end() ) {
         return 0; // or throw, or whatever makes sense in your case
      }
      return (*it)->create();
   }
   ~AbstractFactory(); // ensure that the concrete factories are deleted
private:
   std::map<ConcreteFactoryBase*> factories;
};

Реальные бетонные фабрики могут быть реализованы вручную, но, вероятно, они могут быть шаблонизированы, есликонструкторы для разных типов требуют разных аргументов:

template <typename T>
class ConcreteFactory : public ConcreteFactoryBase {
public:
   ConcreteFactory( std::string const & id ) : myid(id) {}
   virtual Base* create() const {
      return new T;
   }
   virtual std::string id() const {
      return myid;
   }
private:
   std::string myid;
};
class Test : public Base {};
int main() {
   AbstracFactory factory;
   factory.register_factory( new ConcreteFactory<Test>("Test") );
}

При желании вы можете адаптировать подписи, чтобы вы могли передавать аргументы конструктору через разные слои.

Опять же, знаяфактические ограничения, некоторые другие подходы могут быть лучше.Подход clone(), предложенный в другом месте, хорош (либо путем клонирования, либо путем создания пустого объекта того же типа).Это в основном смешивает фабрику с самими объектами, так что каждый объект является фабрикой объектов одного типа.Мне не очень нравится смешивать эти две обязанности, но это может быть один из самых простых подходов с меньшим количеством кода для написания.

0 голосов
/ 08 сентября 2010

Вы можете использовать typeid и шаблоны для реализации фабрики, так что вам не понадобятся строки вообще.

#include <string>
#include <map>
#include <typeinfo>

//***** Base *****
class Base
{
public:
    virtual ~Base(){} //needs to be virtual to make typeid work
};

//***** C1 *****
class C1 : public Base
{};

//***** Factory *****
class Factory
{
public:
    template <class T>
    Base& get();
private:
    typedef std::map<std::string, Base> BaseMap;
    BaseMap m_Instances;
};

template <class T>
Base& Factory::get()
{
    BaseMap::const_iterator i = m_Instances.find(typeid(T).name());
    if(i == m_Instances.end()) {
       m_Instances[typeid(T).name()] = T();
    }
    return m_Instances[typeid(T).name()];
}

//***** main *****
int main(int argc, char *argv[])
{
    Factory f;
    Base& c1 = f.get<C1>();
    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...