Wie kann ich alle Klassen zu verfolgen (aufzählen), die eine Schnittstelle implementieren

StackOverflow https://stackoverflow.com/questions/1260954

  •  12-09-2019
  •  | 
  •  

Frage

Ich habe eine Situation, wo ich eine Schnittstelle, die definieren, wie eine bestimmte Klasse, um verhält sich eine gewisse Rolle in meinem Programm, aber zu diesem Zeitpunkt zu füllen Ich bin nicht 100% sicher, wie viele Klassen ich schreibe diese Rolle auszufüllen. Doch zur gleichen Zeit, ich weiß, dass ich der Benutzer möchte in der Lage zu wählen, aus einer GUI-Combo / Listenfeld, die konkrete Klasse die Schnittstelle implementieren, die sie verwenden möchten eine gewisse Rolle zu füllen. Ich möchte, dass die GUI in der Lage sein, alle verfügbaren Klassen aufzählen, aber ich würde es vorziehen, nicht zurück und ändern alten Code gehen, wenn ich eine neue Klasse zu implementieren entscheiden, diese Rolle zu füllen (die Monate ab jetzt sein kann)

Einige Dinge, die ich in Betracht gezogen habe:

  1. unter Verwendung einer Aufzählung
    • Pros:
      1. Ich weiß, wie es zu tun
    • Cons
      1. Ich werde aktualisieren, um die Auszählung zu aktualisieren, wenn ich eine neue Klasse
      2. hinzufügen
      3. hässlich zu durchlaufen
  2. mit einer Art von static Listenobjekt in der Schnittstelle und das Hinzufügen eines neuen Elements aus der Definitionsdatei der implementierenden Klasse
    • Pros:
      1. Wont hat alten Code ändern
    • Nachteile:
      1. Nicht einmal sicher, ob dies möglich ist,
      2. Nicht sicher, welche Art von Informationen zu speichern, so dass eine Factory-Methode der richtige Konstruktor wählen kann (vielleicht eine Karte zwischen einem String und einem Funktionszeiger, der einen Zeiger auf ein Objekt der Schnittstelle zurückzugibt)

Ich vermute, dies ist ein Problem (oder ähnlich ein Problem), dass mehr erfahrenen Programmierer wahrscheinlich über gekommen sind, bevor (und oft), und es ist wahrscheinlich eine gemeinsame Lösung für diese Art von Problem, die mit ziemlicher Sicherheit ist besser als alles, was mit mir fähig bin kommen. Also, wie mache ich das?

(PS ich gesucht, aber alles, was ich war gefunden, und es ist nicht das gleiche: Wie kann ich alle Elemente aufzuzählen, die eine generische Schnittstelle implementieren? . Es scheint, dass er schon weiß, wie das Problem zu lösen ich versuche herauszufinden.)

Edit: Ich umbenannt den Titel „Wie kann ich im Auge behalten ...“ und nicht nur „Wie kann ich aufzählen ...“, weil die ursprüngliche Frage klang wie ich bei der Prüfung der Laufzeitumgebung mehr daran interessiert war, wo wie das, was ich wirklich interessiert bin, ist Compiler-Buchhaltung.

War es hilfreich?

Lösung

einen Singleton erstellen, wo Sie Ihre Klassen mit einem Zeiger auf einen Schöpfer Funktion registrieren. In den CPP-Dateien der konkreten Klassen, die Sie jede Klasse registrieren.
So etwas wie folgt aus:

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()
{
    //....
}

Andere Tipps

Ich erinnere mich vage, etwas ähnlich wie diese vor vielen Jahren. Ihre Wahl (2) ist ziemlich viel, was ich tat. In diesem Fall war es ein std::map von std::string std::typeinfo. In jeder registrierte CPP-Datei ich die Klasse wie folgt:

static dummy = registerClass (typeid (MyNewClass));

registerClass nimmt type_info Objekt und gibt einfach true. Sie haben eine Variable zu initialisieren, dass registerClass, um sicherzustellen, während der Startzeit genannt wird. Einfach registerClass im globalen Namespace Aufruf ist ein Fehler. Und machen dummy statisch können Sie ohne einen Namen Kollision den Namen über Übersetzungseinheiten hinweg wieder zu verwenden.

I zu diesem Artikel bezeichnen eine selbstregistrier Klasse Fabrik ähnlich den in TIMW Antwort beschrieben zu implementieren, aber es hat den netten Trick, um eine Templat-Fabrik-Proxy-Klasse für die Verwendung der Objektregistrierung zu handhaben. Auch einen Blick wert:)

Selbst Registrieren von Objekten in C ++ -> http://www.ddj.com/184410633

Bearbeiten

Hier ist der Test-App habe ich (ein wenig aufräumte;):

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;
}

Ausgabe:

This is ClassA
ClassA Constructor
ClassA Destructor
This is ClassB
ClassB Constructor
ClassB Destructor

Wenn Sie unter Windows sind, und mit C ++ / CLI, wird dies ziemlich einfach. Der .NET-Framework bietet diese Möglichkeit durch Reflexion, und es funktioniert sehr sauber in verwaltetem Code.

In native C ++, wird dies ein wenig schwieriger, da es keine einfache Möglichkeit gibt, die Bibliothek oder Anwendung zur Laufzeitinformationen abzufragen. Es gibt viele Frameworks rel="nofollow, die diese bieten (suchen Sie einfach nach IoC, DI, oder Plugin-Frameworks), aber das einfachste Mittel es selbst zu tun ist eine Form der Konfiguration zu haben, die eine Factory-Methode selbst verwenden kann, um zu registrieren, und eine Implementierung Ihrer spezifischen Basisklasse zurück. Sie würden nur eine DLL geladen implementieren müssen, und die Factory-Methode Registrierung -. Sobald Sie, dass, es ist ziemlich einfach

Etwas, das man betrachten kann, ist ein Objektzähler. Auf diese Weise müssen Sie jeden Ort nicht ändern Sie zuweisen, sondern nur Definition Implementierung. Es ist eine Alternative zu der Fabrik Lösung. Betrachten Vor / Nachteile.

Ein eleganter Weg, das zu tun, ist den CRTP zu verwenden: Merkwürdiger wiederkehrend Schablonenmuster . Das wichtigste Beispiel ist ein solcher Zähler:)

Auf diese Weise müssen Sie nur in Ihrer konkreten Klasse Implementierung hinzu:

class X; // your interface

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

Natürlich ist es nicht anwendbar, wenn Sie externe Implementierungen verwenden Sie nicht beherrschen.

EDIT:

, um das genaue Problem umgehen müssen Sie einen Zähler haben, die nur die erste Instanz zählen.

my 2 cents

Es gibt keine Möglichkeit, die Unterklassen einer Klasse in (nativ) C ++ abzufragen. Wie erstellen Sie die Instanzen? Betrachten Sie eine Factory-Methode mit dem Sie alle Subklassen iterieren Sie arbeiten. Wenn Sie eine Instanz wie dies schaffen, wird es nicht möglich sein, zu vergessen, später eine neue Unterklasse hinzugefügt wird.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top