3

Gibt es eine Möglichkeit, den Compiler (g ++ in meinem Fall) zu nicht bestimmten Code zu optimieren, auch wenn dieser Code nicht erreichbar ist? Ich will nur diese Symbole in der Objektdatei.Wie man Compiler sagt, um bestimmten Code NICHT zu optimieren?

Beispiel: Hier ist eine einfache Funktion, und ich möchte diese Funktion kompiliert werden, auch wenn sie nie aufgerufen wird.

Wenn es keine offizielle Compiler-Direktive gibt, gibt es einen Trick, damit der Compiler denkt, dass es eine wichtige Funktion ist? Oder lassen Sie es zumindest denken, dass es nicht sicher ignoriert werden kann? Ich habe so etwas versucht:

extern bool bar; 
void foo(){ 
    if(bar){ 
    Foo<int> v; 
    } 
} 

aber das schien es nicht zu tun.

(Wenn Sie wirklich wissen wollen, warum ich auf der Erde das wollen würde - es mit this Frage zu tun hat, wo ich einfach statt explizite Template-Instanziierung mit template class Foo<int> wollen in der Lage sein Foo<int> v zu schreiben, da in vielen Fälle, die einfacher ist, da es alle Funktionen benötigt implizit instanziiert, und es funktioniert im Debug-Modus ohne Optimierungen in Ordnung ...)

UPDATE:

Hier ist, was ich (als übersetzbar mini Beispiel tun wollen):

foo.h (solche Dateien sind mir gegeben und nicht veränderbar)

template<class T> 
struct Foo { 
    T val_; 
    Foo(T val) : val_(val) { 
     // heavy code, long compile times 
    } 
}; 

foo-instantiation.cpp

#include "foo.h" 
void neverCalled() { 
    Foo<int> f(1); 
} 

// The standard way to instantiate it is this: 
// template class Foo<int>; 
// but in reality it is often hard to find out 
// exactly what types I have to declare. 
// Usage like Foo<int> f(1); will instantiate all 
// dependent types if necessary. 

foo-decl.h (eine Schnittstelle, Ich extrahierte von foo.h)

template<class T> 
struct Foo { 
    T val_; 
    Foo(T val); // no heavy code, can include anywhere and compile fast 
}; 

main.cpp

#include <iostream> 
#include "foo-decl.h" 

int main(int argc, char** argv){ 
    Foo<int> foo(1); 
    return 0; 
} 

Compilation (keine Optimierung)

g++ -c main.cpp 
g++ -c foo-instantiation.cpp 
g++ main.o foo-instantiation.oo 

Compilation (Optimierung)

g++ -O2 -c main.cpp 
g++ -O2 -c foo-instantiation.cpp 
g++ main.o foo-instantiation.oo 
main.o(.text+0x13): In function `main': 
: undefined reference to `Foo<int>::Foo(int)' 
collect2: ld returned 1 exit status 
  • Ich versuchte stattdessen vorkompilierte Header, aber die temp späte Instanziierungsmethode ermöglicht eine viel schnellere Kompilierung.
  • Kompilieren foo-instantiation.cpp ohne Optimierung ist nicht so ideal, weil dann der Bibliothekscode (foo.h und andere) langsamer ausgeführt wird.
+0

Könnten Sie einen kompilierbaren Code veröffentlichen, der veranschaulicht, was Sie meinen? –

Antwort

7

Sie befinden sich in der One Definition Rule. In einer Datei haben Sie eine Definition:

template<class T> 
struct Foo { 
    T val_; 
    Foo(T val) : val_(val) { 
     // heavy code, long compile times 
    } 
}; 

und in einem anderen eine andere Definition:

template<class T> 
struct Foo { 
    T val_; 
    Foo(T val); // no heavy code, can include anywhere and compile fast 
}; 

Dies ist ausdrücklich nicht in C erlaubt ++ (nur eine identische Definition erlaubt), und wenn Sie die Regel brechen Sie Ihre Code scheint manchmal zu funktionieren, aber was Sie tatsächlich haben, ist das gefürchtete "undefinierte Verhalten" - alles kann je nach Mondphase passieren (aber eher der interne Zustand des Compilers an bestimmten kritischen Stellen).

Grundsätzlich können Sie Code nicht so schreiben - sorry.

+0

Ich denke, Sie haben Recht. Schade. :( – Frank

+1

dehmann, ich habe immer noch nicht verstanden, was zum Teufel ist besser mit Ihrer Art und Weise, es als explizite Instantiierung zu tun. Beide haben die gleichen Nachteile, nicht wahr? (Und Ihr ist zusätzlich undefined Verhalten) –

+0

Es ist nicht Ein ODR-Verstoß Problem.Der Compiler ist eindeutig besagt, dass es keine Definition der Klasse gibt, nicht, dass es mehrere. ODR-Verstöße mit Vorlagen ist ein heikles Thema, und ein Kommentar hat keinen Platz für eine vollständige Beschreibung, aber das ist nicht –

2

Durchsuchen Sie die Dokumentation unter dem Thema #pragma. Diese Definition ist eine Art von Escape-Schraffur, mit der Sie alle Arten von Eigenschaften angeben können. gcc unterstützt, also gibt es eine gute Wette, dass g ++ auch wird.Seien Sie gewarnt, dass diese wahrscheinlich nicht übertragbar sind, was für Ihr Projekt von Belang sein könnte.

+0

Diese sind nicht Standard. #pragma ist ein Precompiler-Schlüsselwort, um Implementierungsdetails des Compilers zu ändern. Wenn Sie auf #pragma angewiesen sind, verlieren Sie die Portabilität. –

+0

Ich erwähnte, dass sie wahrscheinlich nicht tragbar und für einige Projekte ist dies kein Problem Ich weiß, das ich als solche angegeben. Ich überlasse es dem Fragesteller, zu entscheiden, ob dies ein für ihr Projekt geeignetes Mittel ist. – MikeJ

4

Die Compiler kann keine Funktion Körper weg optimieren, ob Sie erklären es extern oder nicht, weil es nicht wissen kann, dass die Funktion nicht von einer anderen Übersetzungseinheit genannt wird. Es könnte es weg optimieren, wenn Sie es statische deklarierten, aber ich glaube nicht, dass irgendwelche Compiler dies tatsächlich tun.

Der Compiler optimieren entfernt können Funktionsaufrufe:

while(false) { 
    foo(); 
} 

In der über dem Aufruf (foo) elided werden kann.

OTOH, der Linker kann Funktionskörper aus der endgültigen Excel-Datei entfernen, wenn sie nicht aufgerufen werden.

Aus den oben genannten und anderen Gründen müssen wir wirklich echten Code sehen, um Ihr Problem zu diagnostizieren.

+0

Danke, ich habe den Code hinzugefügt, siehe Update. Sie haben recht, dass der Compiler es tatsächlich nicht optimieren kann, da es nicht weiß, was von außen aufgerufen wird. Also ich weiß nicht, warum Kompilierung mit Optimierung einen Fehler gibt, dann und ohne Optimierung ist es in Ordnung. – Frank

+0

Eigentlich ja, der Compiler kann sehen, dass Foo v nie verwendet wird und optimieren es weg. – Frank

+0

@dehmann - bitte ein paar Beweise für diese Behauptung posten –

0

Aus der Hand bin ich mir nicht sicher. Vielleicht wird Precompiled headers dieses Problem lösen?

Nur um dies ein wenig zu ändern, wird dies offensichtlich nicht mit dem Problem helfen, die kleinere Vorlage Header in Ihrem Code verwenden, aber es könnte mit der Kompilierzeit Problem helfen (und damit die Notwendigkeit für die Vorlage).

+0

Der Compiler kann Vorlagen nicht "vorkompilieren", ohne zu wissen, welche Typen definiert werden zum. –

+0

@ DavidRodríguez-dribeas - Eigentlich kann es "vorkompilieren". "Precompile" ist jedoch ein bisschen falsch, es ist eher "Pre-Parsing und evaluieren" als tatsächlich kompilieren. Der Compiler kann immer noch die gesamte Arbeit des Übersetzens des Vorlagencodes in seine eigene interne Darstellung einer Vorlage durchführen. –

1

Dies wird im Allgemeinen durch eine Compiler-Direktive getan. In C wird es ein #pragma sein, und in Delphi Pascal ist es {$ O-}, {$ O +} um den fraglichen Code. Die genaue Syntax und Methode ist implementationsspezifisch. Es geht also darum, die Dokumentation für das von Ihnen verwendete System zu überprüfen.

Die Optimierung einer Funktion ist nicht ganz einfach, aber ein- oder zweimal habe ich Fälle gesehen, in denen es notwendig war, dem Compiler mitzuteilen, dass er keinen spezifischen Code optimieren soll. Dies ist extrem selten und nicht etwas, das ich seit sehr langer Zeit begegnet bin, aber es kann gelegentlich passieren. Fälle, in denen dies der Fall ist, sind im Allgemeinen, wo man sich mit einem alten Legacy-Code kompiliert, der vor einer späteren Entwicklung in der CPU-Technologie gebaut wurde - Hyperthreading ist ein klassisches Beispiel.

2

Der Compiler optimiert eine Variable, die niemals verwendet wird, sie kann eine Funktion nicht optimieren, da sie nicht verwendet wird, da sie von einer anderen Kompilierungseinheit verwendet werden kann.Sie könnten versuchen, den Compiler zu zwingen, in die Variable unter Berücksichtigung wie mit etwas verwendet, verwandt:

void instantiation() 
{ 
    Foo<int> f; 
    f; // mark the variable as if it is used. 
} 

// or: 
Foo<int>* instantiation() 
{ 
    Foo<int> *p = new Foo<int>(); 
    return p; // The compiler cannot know if p will be used, it must compile 
} 

Eine bessere Lösung explizit wäre, um die Vorlage zu instanziieren, wenn Sie es wollen:

// .h 
template <typename T> 
class Foo 
{ 
public: 
    Foo(T const & value); 
    void set(T const &); // whatever else 
private: 
    T value_; 
}; 

// template implementation another file, not included from .h 
// instantiation.cpp?? 
template <typename T> 
Foo<T>::Foo<T>(T const & value) : value_(value) {} 

template <typename T> 
void Foo<T>::set(T const & v) 
{ 
    value_ = value; 
} 

// explicit instantiation 
template class Foo<int>; 
template class Foo<double>; 

// test.cpp 
#include "header.h" 
int main() 
{ 
    Foo<int> f(5); 
    f.set(7); 

    Foo<char> f2; // linker error Foo<char>() not defined 
} 

Benutzercode wird Sehen Sie nur den Header und wissen Sie, welche Methoden existieren, aber nicht die eigentliche Implementierung. Die Implementierung wird in einer Kompilierungseinheit kompiliert, in der die explizite Template-Instanziierung stattfindet.

Beachten Sie, dass, wenn Sie vergessen, einen Typ explizit zu instanziieren, es ein Linker-Fehler ist, kein Kompilierungsfehler.

Die One Definition Rule

Die eine Definition Regel in C++ heißt es, dass es nur eine Definition für jedes Symbol oder Klasse sein kann. Mehrere Definitionen können leicht für reguläre Symbole erkannt werden (wenn Sie zwei void f() { } definieren, erkennt der Linker das duplizierte Symbol), ist aber mit Vorlagen etwas komplizierter. Bei Vorlagen ist es schwieriger, da sie normalerweise in Header-Dateien deklariert und definiert werden. Der Compiler generiert die verwendeten Symbole in jeder Kompilierungseinheit [1] und der Linker findet normalerweise mehr als ein äquivalentes Symbol (std :: vector :: push_back() wird in jede Kompilierungseinheit kompiliert, die einen std :: Vektor hat und push_back aufruft) Der Compiler markiert den Vorlagencode als "schwaches" Symbol. Dies bedeutet, dass während das Symbol hier definiert ist, es auch in einer anderen Kompilierungseinheit definiert werden kann und der Linker das Symbol ohne Angabe eines Verbindungsfehlers verwerfen kann. Dies ist eine Voraussetzung, wenn Sie unterschiedliche Übersetzungseinheiten verknüpfen möchten, die beispielsweise die gleichen STL-Funktionen mit den gleichen Typen verwenden.

Bis gcc 4.2 gcc linux linker löscht alle bis auf eines der schwachen Symbole ohne weitere Prüfung. Einige Linker (gcc linker in linux werden in naher Zukunft nicht 4.2, 4.3 oder 4.4 nicht kennen, da könnte es schon sein) überprüfen, dass die verschiedenen "schwachen" Symbole tatsächlich gleich sind und einen Fehler/eine Warnung liefern für den Benutzer.

Ihr Code ist breaking der ODR in dem Sie die Vorlage an einem anderen Ort neu deklarieren. Sie sollten die Vorlage einmalig deklarieren und die Methoden wie oben beschrieben extern implementieren. Wenn beide Definitionen kompatibel sind (wie sie in dem von Ihnen geposteten Snippet sind): alle Methoden und Attribute von Mitgliedern sind genau gleich und mit den gleichen Qualifizierern (virtual/const-ness ...) sollte sie vom Compiler als dort akzeptiert werden ist nur eine Definition (ach wiederholt) der Vorlage.

[1] Nur jene Methoden, die tatsächlich in dem Code aufgerufen werden, wird kompiliert werden:

template <typename T> 
struct Test 
{ 
    void f() { std::cout << "f()" << std::endl; } 
    void g() { std::cout << "g()" << std::endl; } 
}; 
int main() 
{ 
    Test<int> t; 
    t.f(); // compiler generates Test<int>::f, but not Test<int>::g 
} 
0

Erklären Sie Ihre Variable als flüchtig:

volatile Foo<int> v; 

Normalerweise verhindert es keine Optimierungen. Ich habe es mit Intel C++ - Compiler und Microsoft Visual Studio 2008 überprüft.