Вероятно, немного кода осталось от отладки (Вы установили точку останова на броске, таким образом, Вы могли исследовать исключение в отладчике). Я мог бы сделать что-то вроде этого, если бы я хотел зарегистрировать исключение и затем передать его цепочка, хотя я, вероятно, обернул бы исключение в другой с более значимым (к моему приложению) сообщение об ошибке.
Создайте синглтон, в котором вы можете зарегистрировать свои классы с указателем на функцию-создатель.
В файлах cpp конкретных классов вы регистрируете каждый класс.
Примерно так:
class Interface;
typedef boost::function<Interface* ()> Creator;
class InterfaceRegistration
{
typedef map<string, Creator> CreatorMap;
public:
InterfaceRegistration& instance() {
static InterfaceRegistration interfaceRegistration;
return interfaceRegistration;
}
bool registerInterface( const string& name, Creator creator )
{
return (m_interfaces[name] = creator);
}
list<string> names() const
{
list<string> nameList;
transform(
m_interfaces.begin(), m_interfaces.end(),
back_inserter(nameList)
select1st<CreatorMap>::value_type>() );
}
Interface* create(cosnt string& name ) const
{
const CreatorMap::const_iterator it
= m_interfaces.find(name);
if( it!=m_interfaces.end() && (*it) )
{
return (*it)();
}
// throw exception ...
return 0;
}
private:
CreatorMap m_interfaces;
};
// in your concrete classes cpp files
namespace {
bool registerClassX = InterfaceRegistration::instance("ClassX", boost::lambda::new_ptr<ClassX>() );
}
ClassX::ClassX() : Interface()
{
//....
}
// in your concrete class Y cpp files
namespace {
bool registerClassY = InterfaceRegistration::instance("ClassY", boost::lambda::new_ptr<ClassY>() );
}
ClassY::ClassY() : Interface()
{
//....
}
Я смутно помню, как много лет назад делал нечто подобное. Ваш вариант (2) в значительной степени совпадает с тем, что я сделал. В этом случае это была std :: map
из std :: string
до std :: typeinfo
. В каждом файле .cpp я зарегистрировал класс следующим образом:
static dummy = registerClass (typeid (MyNewClass));
registerClass
принимает объект type_info
и просто возвращает true
. Вы должны инициализировать переменную, чтобы гарантировать, что registerClass
вызывается во время запуска. Простой вызов registerClass
в глобальном пространстве имен является ошибкой. А создание фиктивного
static позволяет повторно использовать имя в единицах компиляции без конфликта имен.
Я сослался на эту статью, чтобы реализовать саморегистрирующуюся фабрику классов, аналогичную той, которая описана в ответе TimW, но у нее есть хороший трюк с использованием шаблонного прокси-класса фабрики для обработки регистрации объекта. . Стоит взглянуть :)
Саморегистрирующиеся объекты в C ++ -> http://www.ddj.com/184410633
Edit
Вот тестовое приложение, которое я сделал (немного привел в порядок; ):
object_factory.h
#include <string>
#include <vector>
// Forward declare the base object class
class Object;
// Interface that the factory uses to communicate with the object proxies
class IObjectProxy {
public:
virtual Object* CreateObject() = 0;
virtual std::string GetObjectInfo() = 0;
};
// Object factory, retrieves object info from the global proxy objects
class ObjectFactory {
public:
static ObjectFactory& Instance() {
static ObjectFactory instance;
return instance;
}
// proxies add themselves to the factory here
void AddObject(IObjectProxy* object) {
objects_.push_back(object);
}
size_t NumberOfObjects() {
return objects_.size();
}
Object* CreateObject(size_t index) {
return objects_[index]->CreateObject();
}
std::string GetObjectInfo(size_t index) {
return objects_[index]->GetObjectInfo();
}
private:
std::vector<IObjectProxy*> objects_;
};
// This is the factory proxy template class
template<typename T>
class ObjectProxy : public IObjectProxy {
public:
ObjectProxy() {
ObjectFactory::Instance().AddObject(this);
}
Object* CreateObject() {
return new T;
}
virtual std::string GetObjectInfo() {
return T::TalkToMe();
};
};
objects.h
#include <iostream>
#include "object_factory.h"
// Base object class
class Object {
public:
virtual ~Object() {}
};
class ClassA : public Object {
public:
ClassA() { std::cout << "ClassA Constructor" << std::endl; }
~ClassA() { std::cout << "ClassA Destructor" << std::endl; }
static std::string TalkToMe() { return "This is ClassA"; }
};
class ClassB : public Object {
public:
ClassB() { std::cout << "ClassB Constructor" << std::endl; }
~ClassB() { std::cout << "ClassB Destructor" << std::endl; }
static std::string TalkToMe() { return "This is ClassB"; }
};
objects.cpp
#include "objects.h"
// Objects get registered here
ObjectProxy<ClassA> gClassAProxy;
ObjectProxy<ClassB> gClassBProxy;
main.cpp
#include "objects.h"
int main (int argc, char * const argv[]) {
ObjectFactory& factory = ObjectFactory::Instance();
for (int i = 0; i < factory.NumberOfObjects(); ++i) {
std::cout << factory.GetObjectInfo(i) << std::endl;
Object* object = factory.CreateObject(i);
delete object;
}
return 0;
}
вывод:
This is ClassA
ClassA Constructor
ClassA Destructor
This is ClassB
ClassB Constructor
ClassB Destructor
Если вы работаете в Windows и используете C ++ / CLI, это становится довольно просто. Платформа .NET предоставляет эту возможность через отражение, и она очень четко работает в управляемом коде.
В собственном C ++ это становится немного сложнее, поскольку нет простого способа запросить информацию о времени выполнения в библиотеке или приложении. Есть много фреймворков, которые предоставляют это (просто ищите фреймворки IoC, DI или плагинов), но простейший способ сделать это самостоятельно - иметь некоторую форму конфигурации, которую фабричный метод может использовать для регистрации. и верните реализацию вашего конкретного базового класса. Вам просто нужно будет реализовать загрузку библиотеки DLL и регистрацию фабричного метода - как только она у вас есть, это довольно просто.
Вы можете подумать о счетчике объектов. Таким образом, вам не нужно менять каждое место, которое вы выделяете, а только определение реализации. Альтернатива заводскому решению. Рассмотрим плюсы / минусы.
Элегантный способ сделать это - использовать CRTP: Любопытно повторяющийся шаблон шаблона . Основным примером является такой счетчик :)
Таким образом, вам просто нужно добавить в вашу конкретную реализацию класса:
class X; // your interface
class MyConcreteX : public counter<X>
{
// whatever
};
Конечно, это не применимо, если вы используете внешние реализации, которые вы не освоили.
EDIT:
Для решения конкретной задачи вам нужен счетчик, который считает только первый экземпляр.
мои 2 цента
Невозможно запросить подклассы класса в (родном) C ++. Как вы создаете экземпляры? Рассмотрите возможность использования фабричного метода, позволяющего перебирать все подклассы, с которыми вы работаете. Когда вы создадите такой экземпляр, вы не забудете добавить новый подкласс позже.