Как я могу отслеживать (перечисляют) все классы, которые реализуют интерфейс

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

7
задан Community 23 May 2017 в 10:27
поделиться

6 ответов

Создайте синглтон, в котором вы можете зарегистрировать свои классы с указателем на функцию-создатель. В файлах 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()
{
    //....
}
7
ответ дан 6 December 2019 в 15:25
поделиться

Я смутно помню, как много лет назад делал нечто подобное. Ваш вариант (2) в значительной степени совпадает с тем, что я сделал. В этом случае это была std :: map из std :: string до std :: typeinfo . В каждом файле .cpp я зарегистрировал класс следующим образом:

static dummy = registerClass (typeid (MyNewClass));

registerClass принимает объект type_info и просто возвращает true . Вы должны инициализировать переменную, чтобы гарантировать, что registerClass вызывается во время запуска. Простой вызов registerClass в глобальном пространстве имен является ошибкой. А создание фиктивного static позволяет повторно использовать имя в единицах компиляции без конфликта имен.

3
ответ дан 6 December 2019 в 15:25
поделиться

Я сослался на эту статью, чтобы реализовать саморегистрирующуюся фабрику классов, аналогичную той, которая описана в ответе 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
2
ответ дан 6 December 2019 в 15:25
поделиться

Если вы работаете в Windows и используете C ++ / CLI, это становится довольно просто. Платформа .NET предоставляет эту возможность через отражение, и она очень четко работает в управляемом коде.

В собственном C ++ это становится немного сложнее, поскольку нет простого способа запросить информацию о времени выполнения в библиотеке или приложении. Есть много фреймворков, которые предоставляют это (просто ищите фреймворки IoC, DI или плагинов), но простейший способ сделать это самостоятельно - иметь некоторую форму конфигурации, которую фабричный метод может использовать для регистрации. и верните реализацию вашего конкретного базового класса. Вам просто нужно будет реализовать загрузку библиотеки DLL и регистрацию фабричного метода - как только она у вас есть, это довольно просто.

1
ответ дан 6 December 2019 в 15:25
поделиться

Вы можете подумать о счетчике объектов. Таким образом, вам не нужно менять каждое место, которое вы выделяете, а только определение реализации. Альтернатива заводскому решению. Рассмотрим плюсы / минусы.

Элегантный способ сделать это - использовать CRTP: Любопытно повторяющийся шаблон шаблона . Основным примером является такой счетчик :)

Таким образом, вам просто нужно добавить в вашу конкретную реализацию класса:

class X; // your interface

class MyConcreteX : public counter<X>
{
    // whatever
};

Конечно, это не применимо, если вы используете внешние реализации, которые вы не освоили.

EDIT:

Для решения конкретной задачи вам нужен счетчик, который считает только первый экземпляр.

мои 2 цента

1
ответ дан 6 December 2019 в 15:25
поделиться

Невозможно запросить подклассы класса в (родном) C ++. Как вы создаете экземпляры? Рассмотрите возможность использования фабричного метода, позволяющего перебирать все подклассы, с которыми вы работаете. Когда вы создадите такой экземпляр, вы не забудете добавить новый подкласс позже.

0
ответ дан 6 December 2019 в 15:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: