Vermeiden Sie eine Initialisierungsmethode

12

Ich habe diesen vorhandenen Code, in dem sie eine Klasse und eine Initialisierungsmethode in dieser Klasse haben. Es wird erwartet, dass das Objekt der Klasse nach seiner Erstellung initialize aufgerufen werden muss.

Grund für die Existenz der Initialisierungsmethode Das Objekt wird frühzeitig mit einem globalen Gültigkeitsbereich erstellt und die Initialisierungsmethode wird später aufgerufen, nachdem eine DLL geladen wurde, von der es abhängt.

Problem mit der Initialisierung Die Klasse hat jetzt den Bool isInitialized, der in jeder Methode überprüft werden muss, bevor er fortfährt, und einen Fehler zurückgibt, wenn er nicht initialisiert ist. Einfach ausgedrückt, es ist ein großer Schmerz.

Eine mögliche Lösung Initialisieren Sie im Konstruktor. Haben Sie nur einen Zeiger auf das Objekt im globalen Bereich. Erstellen Sie das eigentliche Objekt, nachdem die DLL geladen wurde.

Problem mit der obigen Lösung Jeder, der ein Objekt dieser Klasse erstellt, muss wissen, dass es erst erstellt werden muss, nachdem die DLL geladen wurde, da es sonst fehlschlägt.

Ist das akzeptabel?


quelle
Ich hatte das gleiche Problem beim Erstellen von OpenGL-bezogenen Objekten, die instanziiert werden müssen, bevor ein OpenGL-Kontext existiert, aber OpenGL-abhängige Objekte wie Anruflisten, Texturen usw. enthalten sollen.
3
Dumme Frage 1: Warum kann der Objektkonstruktor die DLL nicht laden, wenn sie noch nicht geladen ist?
John R. Strohm
1
Entschuldigen Sie, dass Sie eine alte Frage wiederbelebt haben. Wenn dies jedoch im Jahr 2017 gelesen wird, verwenden Sie call_onceC ++ 11 . Projekte, die sich noch nicht in C ++ 11 befinden, sollten untersuchen, wie call_once in C ++ 11 implementiert wird (konzentrieren Sie sich darauf, welches Problem es löst, und dann wie), und es dann in ihrer (veralteten) Version von C ++ erneut implementieren. Es benötigt ein Multithread-sicheres Synchronisationsprimitiv, dessen Status statisch initialisiert werden muss (mit einem konstanten Wert). Beachten Sie, dass Compiler vor C ++ 11 möglicherweise andere Besonderheiten aufweisen, die erfüllt werden müssen.
Rwong

Antworten:

7

Klingt nach einer Arbeit für einen virtuellen Proxy.

Sie können einen virtuellen Proxy erstellen, der einen Verweis auf das betreffende Objekt enthält. Solange die DLL nicht geladen ist, kann der Proxy Clients ein bestimmtes Standardverhalten bieten. Sobald die DLL geladen ist, leitet der Proxy alle Anforderungen einfach an den realen Betreff weiter.

Der virtuelle Proxy ist für die Überprüfung der DLL-Initialisierung verantwortlich und entscheidet auf dieser Grundlage, ob die Anforderung an den realen Betreff delegiert werden soll.

Proxy-Muster in Wikipedia

Wie findest du diese Idee?

edalorzo
quelle
Sie lösen hier nicht wirklich viel. Für jede im tatsächlichen Objekt hinzugefügte Methode müssten Sie diese Methode auch dem Proxy hinzufügen. Nein?
Dies vermeidet das Problem, sich daran zu erinnern, das Init aufzurufen. Funktion, aber es scheint, als müsste jede Funktion im Proxy noch prüfen, ob die DLL geladen ist.
1
@ Sriram, es gibt jetzt einen großen Unterschied, wenn Sie einen Proxy verwenden, haben Sie die Wartung im Zusammenhang mit der DLL-Prüfung auf eine einzige Klasse beschränkt: den Proxy. Die Clients der Klasse müssen nicht einmal über die DLL-Prüfung Bescheid wissen. Da die meisten Methoden die Delegierung übernehmen, sehe ich kein großes Problem darin, die Schnittstelle sowohl im eigentlichen Betreff als auch im Proxy zu implementieren. Mit anderen Worten, Sie können die Erstellung des realen Betreffs verhindern, bis Sie sicher sind, dass die DLL geladen wurde, und müssen dann nicht jedes Mal nach dem DLL-Status suchen.
@edalorzo: Während die Idee gut ist, ist das Problem hier, dass wir bereits über einen Proxy sprechen, und das OP beschwert sich darüber, dass jede Methode seines Proxys eingecheckt werden muss ...
Matthieu M.
4

Es tritt nichts Nützliches auf, wenn die DLL noch nicht geladen ist. Das Objekt erzeugt nur einen Fehler. Ist es ein schwerwiegender Fehler? Wie wird der Fehler behandelt?

Stellt Ihre Testmethode sicher, dass dieser Fehler im fertigen Produkt niemals auftritt?

Es klingt wie ein Job zum Testen, nicht für architektonisches Design. Gib nicht einfach einen Fehler zurück, assertder niemals passiert.

Korrigieren Sie alle Clients der Klasse, die den Fehler derzeit abfangen und ignorieren, und versuchen Sie nicht, ihn an erster Stelle auszulösen.

Ein Multithread-Muster kann darin bestehen, nach dem Laden der DLL eine gemeinsam genutzte Bedingungsvariable festzulegen und den Konstruktor des Objekts warten zu lassen (und andere Threads zu blockieren), bis die DLL geladen ist.

Kartoffelklatsche
quelle
Ich denke, Sie haben Recht, es ist nichts Falsches daran, beim Erstellen des betreffenden Objekts eine Ausnahme auszulösen, wenn die DLL nicht ordnungsgemäß initialisiert wurde. Der Client-Code müsste sich dann nur mit dieser Ausnahme befassen, und das Testen sollte sicherstellen, dass die Ausnahme angemessen behandelt wird.
2

Das erste: Vermeiden Sie globale Objekte als Schädling, es sei denn, ihr Zustand ändert sich nie (Konfiguration).

Nun, wenn Sie aus irgendeinem Grund nicht weiterkommen, gibt es verschiedene Designs, die Ihnen wahrscheinlich helfen könnten.

Ich hatte zwei Ideen:

  1. Verwenden Sie eine Fassade, um die DLL zu laden. Auf das Objekt kann nur über die Fassade zugegriffen werden. Die Fassade lädt die DLL bei der Erstellung und instanziiert das Objekt gleichzeitig.

  2. Verwenden Sie einen Proxy, aber die kluge Art;)

Lassen Sie mich auf den zweiten Punkt näher eingehen , da ich befürchte, die Antwort von @edalorzo könnte Sie erschrecken:

// Private
static Object& GetObjectImpl() { static Object O; return O; }

// Public
static Object& GetObject() {
  Object& o = GetObjectImpl();
  assert(o.isInitialized() && "Object not initialized yet!");
  return o;
}

Jetzt haben Sie nur noch einen Scheck.

Dies kann auch durch eine Art Smart Pointer erfolgen:

Pointer<Object> o;

Hier reservieren Sie nur Platz für einen Zeiger, anfangs null, und erst wenn die DLL geladen ist, ordnen Sie das Objekt tatsächlich zu. Alle vorherigen Zugriffe sollten eine Ausnahme auslösen (NullException: p?) Oder das Programm (sauber) beenden.

Matthieu M.
quelle
1

Das ist eine knifflige Frage. Ich habe das Gefühl, dass Architektur bei dem Problem helfen kann.

Aus dem Beweis, es klingt wie die DLL nicht geladen wird, wenn das Programm geladen wird. Es klingt fast wie ein Plugin.

Eine Sache, die in den Sinn kommt, ist, dass Sie die DLL initialisieren müssen. Der Programmierer muss davon ausgehen, dass das Objekt sowieso nicht zuverlässig zurückkommt. Möglicherweise können Sie dies ausnutzen ( bool isObjectLoaded() { return isInitialized; }), aber Sie erhalten definitiv mehr Fehlerberichte über Ihre Überprüfungen.

Ich dachte an einen Singleton.

Wenn Sie den Singleton aufrufen, sollte er das richtige Objekt zurückgeben, wenn er eines abrufen kann. Wenn es nicht das richtige Objekt zurückgeben kann, sollten Sie einen Fehlerwert / nullptr / empty base object erhalten. Dies würde jedoch nicht funktionieren, wenn das Objekt an Ihnen sterben könnte.

Wenn Sie mehrere Instanzen des Objekts benötigen, können Sie stattdessen eine Factory verwenden.

Joe Plante
quelle