extern einer Klasse
-
Ich würde gerne aus übersetzungstechnischen eine Instanz einer Klasse als extern deklarieren und diese in einer anderen Übersetzungseinheit definieren.
Macht das überhaupt Sinn?
Der Code sieht so aus:class MyClass { struct Foo { int irgendwas; }; virtual inline getFoo() { return foo;} protected: Foo foo; };Kann ich das struct Foo komplett in eine andere h Datei legen, aus Foo foo; extern Foo foo; machen?
Wo ist der Unterschied, wenn ich es nicht extern mache? Ich muss so oder so die h Datei inkludieren, damit Foo bekannt ist. Forward decl kann ich hier nicht machen.
MyClass dauert bei mir sehr lange zu kompilieren und bastel aber primär an der Foo Klasse rum (h und cpp)
Kann mir da extern eine Hilfe sein?
-
Häh? Wieso musst du MyClass neu compilieren, wenn du an Foo herumbastelst? Außerdem habe ich irgendwie nicht den Eindruck, dass du verstehst was extern überhaupt macht.
Das hier sollte reichen:
myclass.h#include"foo.h" class MyClass { struct Foo { int irgendwas; }; virtual inline getFoo() { return foo;} protected: Foo foo; };
-
Ich denke er verändert vieles an Foo, z.B. Methoden umbenennen oder Member hinzufügen/entfernen.
Wenn dann einmal dein Interface von Foo steht, solltest du dir überlegen die Interna per Pimpl zu implementieren. Also Private Funktionen und Variablen in eine private Klasse packen und nur noch eine Pointer in der Klasse:class Foo { struct FooImpl; FooImpl* m_pimpl; public: /** whatever goes here **/ };Und in deiner Foo.cpp dann
struct Foo::FooImpl { /** deine ehemaligen privaten Member von Foo **/ };Damit kannst du so viel an den Interna von Foo rumbasteln wie du willst (geschieht in FooImpl), Foo benutzende Klassen müssen nicht neu kompiliert werden.
-
Hallo,
ich denke Pimpl hilft mir da nicht viel, wenn das Interface steht kann ich in der cpp auch so rumbasteln und muss nur diese Übersetzungseinheit kompilieren, ohne dass der Rest betroffen ist.
Wird wohl nur über forward decl gehen, hmmm
-
Seikilos schrieb:
Hallo,
ich denke Pimpl hilft mir da nicht viel, wenn das Interface steht kann ich in der cpp auch so rumbasteln und muss nur diese Übersetzungseinheit kompilieren, ohne dass der Rest betroffen ist.
Wird wohl nur über forward decl gehen, hmmmSicher hilft dir Pimpl da weiter! Füge eine neue Membervariable im Header hinzu -> alle Nutzer müssen neukompilieren, obwohl sich am public-Interface nichts ändert.
Füge eine neue private Helper-Funktion hinzu -> alle Nutzer müssen neukompilieren, obwohl sich am public-Interface nichts ändert.
-
Seikilos, ich habe Dein Problem nicht verstanden. Kannst Du das noch mal umschreiben, was Du erreichen willst?
extern macht bei nicht-statischen Elementen absolut keinen Sinn.
-
Änderst du was am Interface (oder an der restlichen Definition) müssen alle die dieses benutzen neu kompilieren, da hilft goar nix.
Die Implemntierung der Methoden interessiert -sofern sie in der cpp steht- keine Sau. Die Daten die in der Definiton stehen bestimmen die Größe des Objekts, daher müssen bei Änderungen auch alle Benutzer neu kompilieren.
Um die öffentlichen Änderungen zu minimieren bietet sich Pimpl an. Etwas ähnliches kann man mit einer abstrakten Interfaceklasse machen. Pimpl bietet da aber die schöneren Möglichkeiten.
-
Danke an alle, werde mich morgen mal damit beschäftigen

Sebastian Pizer schrieb:
Seikilos, ich habe Dein Problem nicht verstanden. Kannst Du das noch mal umschreiben, was Du erreichen willst?
extern macht bei nicht-statischen Elementen absolut keinen Sinn.
Naja mein Problem ist, dass MyClass äußerst lange kompiliert, ich aber primär im Struct Foo arbeite und verhindern will, dass änderungen an Foo sich im Neukompilieren niederschlagen
-
Änderst du denn dauernd das Interface?
Es wurde ja schon mehrfach erwähnt, dass dir das Pimpl-Idiom weiterhilft. Hast du dich schon genauer darüber erkundigt?
-
Noch ein paar Sachen:
- Wir reden davon (auf dein Beispiel bezogen) Foo zu Pimpln, nocht MyClass
- kannst du in jedem Fall die Implementierung von MyClass aufsplitten in einen Teil der von Foo abhängt, und in einen von Foo unabhängigen. So muss nicht ein fettes MyClass.cpp neu kompiliert werden, sondern nur der Tel, der wirklich Foo verwendet.
- Von wie langen Kompilierungszeiten reden wir hier?
Ich habe ein Projekt, in dem ich mittels Swig Python-Bindings aus 15 recht umfangreichen Klassen generieren lasse. Daraus resultiert ein ~15000 Zeilen .cpp und ein .h mit ~600 Zeilen. Auf meinem 7 Jahre alten Rechner kompiliert das in
Sekunden (incl. swig-Parse), also im verschmerzbaren Bereich. Ich kompilier ja nicht alle 5 Sekunden, weil ich in einem String grad rumbastel 
Und da kommen wir zu
4) Um Gottes Willen, splitte deine klasse
Wenn die echt so fett ist, dass die so lange braucht schaut das nach nem Designfehler aus
-
Bin dran

Die Klasse dauert auf diesem Rechner 3-4 Minuten zu kompilieren und das wegen Boost, diese Klasse hat halt noch n privates Struct (Foo), was ich ja gerade rausfaktorisiere, um schneller zu kompilieren.
Auf dem Core2Duo 6600 (ist schon in die Jahre gekommen) dauert das ewig.
Es liegt an Boost::Spirit (um genau zu sein qi, die anderen Header wir karma werden nicht benötigt und nicht included).
Bedingt durch Spirit hab ich sehr viele Templates in dieser Klasse.Es wird empfohlen, nur eine include Datei für qi, die aber ne Gruppe von Includes hat. Als nächstes werde ich gucken, ob ich die nicht manuell include und nicht benötigte Header rauslasse.
Ist auch sehr unbequem damit zu arbeiten.
Edit:
Was mich eigentlich interessiert. Pimpl erzeugt ja immer mindestens eine Indirektion der Methodenaufrufe auf den versteckten Teil.
Wenn es eine Klasse ist, die massiv in performancekritischen Bereichen eingesetzt wird, ist die einfache oder doppelte Indirektion da signifikant? Es wird bei den meisten Compilern auf eine Extraanweisung hinauslaufen, oder?
-
Hm, Pimpl scheint hier ein wenig umständlich. Foo erbt bei mir von nem Objekt.
Also ungefähr so:#include "big_header.h" struct MyClass { struct Foo : big_header::zeug { Foo(typ a); int zahl; }; MyClass(typ a); virtual inline getFoo() { return foo;} protected: Foo foo; };Wenn ich Foo pimpln würde, würd ich doch die privaten Innereien in FooImpl packen, das ist mir klar.
MyClass hat dann ne forward decl von Foo als Pointer.
Der MyClass Constructor in der cpp datei muss aber Foo nun initialisieren, sprich new Foo(a) übergeben. Da aber das Foo struct von big_header::zeug erbt, muss in MyClass.cpp der include her, oder? Dann ist das wieder Grütze.Oder läuft es so, dass Foo von nichts erbt, aber die FooImpl erbt?
Dann muss ich den getter von Foo soweit abändern, dass er mir FooImpl, zurück gibt, nicht wahr?
-
Ob diese zusätzliche Indirektion Performanceprobleme auslöst, hängt ganz davon ab, wie komplex die übrigen Berechnungen sind.
Zu den anderen Sachen:
Wenn du von big_header::zeug erbst, und sich eben in big_header::zeug nichts ändert, bist du immer noch mit Foo pimpln gut bedient!
Sollte sich big_header::zeug ändern hast du aber wieder das gleiche Problem.
Selbiges gilt natürlich auch, wenn du jetzt nicht Foo sondern FooImpl von big_header::zeug erben lässt. Denn um jetzt FooImpl "bedienen" zu können müssen Details bekannt sein - Header includen unerlässlich! Und du hast wieder die selben Probleme.Aber das ist jetzt DEINE Entscheidung! Entweder Indirektion in Kauf nehmen und leichte Performance-Einbußen hinnehmen, oder bei jeder Änderng in Foo.h 3-4 Minuten Kompilieren ertragen.
Du kannst jetzt versuchen mit Macros zu steuern, ob Pimpl oder nicht. So dass du während des Testens/Erweiterns/... ein #define setzt und Pimplst, für Produktiveinsatz (sprich Release) aber nicht. Ist sicher möglich aber kompliziert und kann wieder Fehler produzieren - und das ganze wird schwerer wartebar...
-
wenn du ur mit dem Foo rumhantierst und nicht den qi-templatekram jedesmal neu compilieren willst, kannst du auch die .cpp von MyClass in zwei ÜEs aufteilen, vorausgesetzt du hast nicht überall Foo UND qi Kram vermischt.