Automatisch trennen Klassendefinitionen von Erklärungen?
-
19-08-2019 - |
Frage
Ich verwende eine Bibliothek, die fast ausschließlich aus Templat Klassen und Funktionen in Header-Dateien , wie folgt zusammen:
// foo.h
template<class T>
class Foo {
Foo(){}
void computeXYZ() { /* heavy code */ }
};
template<class T>
void processFoo(const Foo<T>& foo) { /* more heavy code */ }
Nun, das ist schlecht, weil kompilieren Zeiten sind unerträglich , wenn ich gehöre einer dieser Header-Dateien (und ich bin tatsächlich viele von ihnen in jedem meiner Kompilierungseinheiten).
Da als Template-Parameter nur ich eine oder zwei Arten verwende sowieso Ich plane, für jede Bibliothek Header-Datei zu erstellen, eine Datei, die nur Erklärungen enthält, ohne den schweren Code wie folgt:
// NEW: fwd-foo.h
template<class T>
class Foo {
Foo();
void computeXYZ();
};
template<class T>
void processFoo(const Foo<T>& foo);
Und dann eine Datei, die alle Instanzen erstellt, die ich benötigen. Diese Datei kann werden separat einmal kompiliert und für alle :
// NEW: foo.cpp
#include "foo.h"
template class Foo<int>;
template class Foo<double>;
template void processFoo(const Foo<int>& foo);
template void processFoo(const Foo<double>& foo);
Jetzt kann ich nur schließen in meinem Code fwd-foo.h
und haben kurze Zeiten kompilieren. Ich werde verknüpfen gegen foo.o
am Ende.
Der Nachteil ist natürlich, dass ich diese neue fwd-foo.h
und foo.cpp
-Dateien selbst erstellen haben. Und natürlich ist es ein Wartungsproblem: Wenn eine neue Version der Bibliothek freigegeben wird, ich habe sie auf die neue Version anzupassen. Gibt es noch andere Nachteile?
Und meine Hauptfrage ist:
Gibt es eine Chance, dass ich diese neue Dateien erstellen kann, insbesondere fwd-foo.h
, automatisch aus dem ursprünglichen foo.h
? Ich habe dies für viele Bibliothek Header-Dateien zu tun (vielleicht 20 oder so) und eine automatische Lösung wäre besonders am besten sein, falls eine neue Version der Bibliothek freigegeben wird, und ich habe dies mit der neuen Version wieder zu tun. Sind irgendwelche Werkzeuge für diese Aufgabe zur Verfügung?
EDIT:
Weitere Frage: Wie kann das neu unterstützte extern
Stichwort helfen Sie mir in diesem Fall
Lösung
Wir verwenden LZZ , die eine einzelne Datei in einen separaten Header und Übersetzungseinheit teilt aus. Standardmäßig wäre es normalerweise die Vorlage Definitionen in den Header setzen jedoch zu können, geben Sie an, dass Sie dies nicht wollen passieren.
Um Ihnen zu zeigen, wie Sie es sich anschauen sollten Sie Folgendes ein:
// t.cc
#include "b.h"
#include "c.h"
template <typename T>
class A {
void foo () {
C c;
c.foo ();
b.foo ();
}
B b;
}
Nehmen Sie die obige Datei und kopieren Sie sie in ‚t.lzz‘ Datei. Legen Sie alle #include Richtlinien in separate $ hdr und $ src Blöcke nach Bedarf an:
// t.lzz
$hdr
#include "b.h"
$end
$src
#include "c.h"
$end
template <typename T>
class A {
void foo () {
C c;
c.foo ();
b.foo ();
}
B b;
}
Nun endlich laufen LZZ über die Datei angeben, dass sie die Vorlage Definitionen in der Quelldatei platziert. Sie können entweder tun dies mit einem $ Pragma in der Quelldatei, oder Sie können die Befehlszeilenoption „-ts“ verwenden:
Das in den folgenden Dateien führen wird generiert:
// t.h
//
#ifndef LZZ_t_h
#define LZZ_t_h
#include "b.h"
#undef LZZ_INLINE
#ifdef LZZ_ENABLE_INLINE
#define LZZ_INLINE inline
#else
#define LZZ_INLINE
#endif
template <typename T>
class A
{
void foo ();
B b;
};
#undef LZZ_INLINE
#endif
Und:
// t.cpp
//
#include "t.h"
#include "c.h"
#define LZZ_INLINE inline
template <typename T>
void A <T>::foo ()
{
C c;
c.foo ();
b.foo ();
}
#undef LZZ_INLINE
Sie können diese dann durch einige grep / sed Befehle ausführen, um die LZZ Helfer Makros zu entfernen.
Andere Tipps
Versuchen Sie vorkompilierte Header verwenden. Ich weiß, GCC und MSVC diese Funktion unterstützen. Die Verwendung ist vender spezifische, though.
Ich habe eine ganze Weile auf dem gleichen Problem gearbeitet, jetzt. In der Lösung Sie vorschlagen, definieren Sie Ihre Template-Klassen zweimal. Es wird in Ordnung sein, wenn es das gleiche Material definiert (in der gleichen Reihenfolge), aber Sie sind verpflichtet, Probleme haben, früher oder später.
Was ich habe kommen mit ist das Problem umgekehrt zu betrachten. Solange Sie Ihre Implementierung nicht spezialisiert sind, funktioniert es ordnungsgemäß.
Es werden zwei Makros, die Vorlage Argumente in Implementierungsdatei aktualisieren avois mit (Vorsicht, obwohl, wenn Sie Standardvorlage Argumente zur Klasse hinzufügen möchten).
// foo.h
#define FOO_TEMPLATE template<typename T>
#define FOO_CLASS Foo<T>
FOO_TEMPLATE
class Foo {
Foo();
void computeXYZ();
};
// foo_impl.h
#include "foo.h"
FOO_TEMPLATE
FOO_CLASS::Foo(){}
FOO_TEMPLATE
void FOO_CLASS::computeXYZ() { /* heavy code */ }
Auf diese Weise arbeiten Sie im Wesentlichen auf die gleiche Weise mit Nicht-Template-Klassen zu tun (Sie die gleiche Sache mit Template-Funktionen zu tun, natürlich).
EDIT: über das Schlüsselwort extern in C ++ 0x
Ich glaube, das Schlüsselwort extern in C ++ 0x helfen, aber es wird nicht alles auf magische Weise lösen!
Externe Vorlagen
Jedes Modul, das eine instanziiert Vorlage erstellt im Wesentlichen eine Kopie es in dem Objektcode. Dann geht es nach oben an den Linker aller die Verfügungs redundanter Objektcode in dem allerletzten Stufe verlangsamt somit die kritische edit-Kompilierung-link-Zyklus, der nach oben macht ein Tag Programmierer (oder manchmal Wachträume). Kurzzuschließen dieser Objektcode garbage collection, a Anzahl der Compiler-Anbieter haben bereits implementiert ein Schlüsselwort extern das kann vor Vorlagen gehen. Dies ist ein Fall, der Normung kodifiziert bestehende Industriepraxis (Wortspiel beabsichtigt). In der Praxis ist dies umgesetzt durch eine Mitteilung an das Senden Compiler grundsätzlich „nicht instanziiert das hier ":
extern template class std::vector;
C ++ 0x Ihre Kompilierung Probleme mit externen Vorlagen beheben. Ich weiß nicht, eine automatische Art und Weise zu tun, was Sie fragen, though.