Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (ANSI-Standard) » Symbol erzwingen

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 <
000
23.06.2005, 19:38 Uhr
~bahaman
Gast


Hallo,

ich möchte eine Klasse die nirgendwo im Programm referenziert wird trotzdem zu meinem Programm linken. Die Klasse, nennen wir sie Foo, hat statische Member deren Initialisierung dazu führt, dass die Klasse in einer Factory angemeldet wird.
Bisher ist es leider so, dass ohne eine explizite Referenz auf Code von Foo der Linker den Code der Klasse einfach rausschmeisst.
Meine Frage: Gibt es einen portablen Trick mit dem sich sicherstellen lässt das die Klasse trotzdem zum Programm gelinkt wird?

Danke schon mal!
bahaman
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
23.06.2005, 21:22 Uhr
FloSoft
Medialer Over-Flow
(Administrator)


optimierungen abschalten
--
class God : public ChuckNorris { };
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
23.06.2005, 21:23 Uhr
~bahaman
Gast


Sind schon aus! VS Studio Debug modus! Neuer Versuch?
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
23.06.2005, 21:48 Uhr
virtual
Sexiest Bit alive
(Operator)


Hast Du die Klasse mal in eine DLL gepackt und diese DLL explizit mit LoadLibrary geladen?
--
Gruß, virtual
Quote of the Month
Ich eß' nur was ein Gesicht hat (Creme 21)
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
23.06.2005, 22:09 Uhr
~bahaman
Gast


Nein, habe ich nicht. Ich wollte aber die Klasse auch direkt zu Anwendung linken. Später soll die Anwendung durch mal durch Module (DLLs) erweiterbar sein. Die sollen dann den selben Ansatz nutzen, sprich sich bei Initialisierung des Moduls in der entsprechenden Factory anmelden. Ich bin mir ziemlich sicher das das mit LoadLibrary klappt weil da ja zumindestens über DllMain mal die Kontrolle an den die DLL übergibt und dort die Klasse in der Factory anmelden könnte.

Was ich allerdings schon mal probiert habe ist in einer anderen CPP-Datei (nicht die mit der Klassendefinition von Foo), die auch zur Anwendung gelingt wird, den Code zur Factory-Registrierung zu schreiben:

Code:
// file x.cpp

namespace
{
    int register_type()
    {
         // do registration
        return 0;
    }
}

static int dummy = register_type();

// end file x.cpp



Das hat aber auch nichts gebracht, vermutlich weil die Codeeliminierung des Linkers transitiv funktioniert. Der dummy int wird ja nirgends referenziert. Selbst wenn der int global ist, also ohne static, eleminiert der Linker den Funktionsaufruf.

Gruss,
bahaman
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
005
26.06.2005, 14:25 Uhr
~HumeSikkins
Gast


Hallo,
das Problem tritt nur auf, wenn der entsprechende Code teil einer statischen Bibliothek ist. Beim hinzulinken einer statischen Bibliothek wird nur solcher Code verwendet, der ein "external symbol" auflöst. Da deine Anwendung kein Element aus Foo.o referenziert, Foo.o damit nicht zur Auflösung eines Symbols benötigt wird, wird Foo.o auch nicht hinzugelinkt. Mit dem beschriebenen Ergebnis. Wichtig ist hierbei, dass die Anwendung die Referenz erzeugt. Es hilft also nicht, wenn nur andere Teile deiner Lib Elemente aus Foo referenzieren.

Lösungen gibt es verschiedene. Alle basieren darauf die Abhängigkeit explizit zu machen. Leider gibt es keine portable und gleichzeitig vollautomatische Möglichkeit dafür. Das Ziel "automatische Registrierung beliebiger Klassen einer statischen Bibliothek in einer Factory ohne explizite Referenzierung dieser Klassen in einer Anwendung die gegen die Lib linkt" lässt sich portabel nicht erreichen.

Variante 1: Explizites Linken gegen die obj-Dateien

Wenn du deine Anwendung statt gegen eine statische Lib einfach explizit gegen alle obj-Dateien linkst (die aus der Lib) tritt das Problem nicht auf. Diese Variante lässt sich wohl am Einfachsten automatisieren, da man ja einfach gegen *.o eines speziellen Unterverzeichnisses linken kann, man also nicht alle Dateien explizit benennen muss.

Variante 2: Die unportable Lösung:

Compiler/Linker bieten in der Regel eine Möglichkeit bestimmte Symbole zu erzwingen. Beim Visual Studio gibt es dafür z.B. eine Pragma-Direktive.
Mit

C++:
#pragma comment(linker, "/include:__meinSymbol")


kannst du dort eine Symbolreferenz für meinSymbol erzwingen. Blöd ist hierbei, dass der Symbolnamen bereits ein "mangled name" sein muss. Du kannst also nicht einfach


C++:
#pragma comment(linker, "/include:Foo")


schreiben.
Am einfachsten ist es wohl, wenn du dir vom Compiler eine Map-Datei (mit allen Symbolen) erstellen lässt. Dann suchst du dir z.B. das Symbol für den Default-Ctor (in der Art: ??0Foo@@QAE@XZ) und packst am Ende alle pragma-Direktiven in eine Datei die du dann in der Anwendung inkludierst.

Hier braucht man für die Automatisierung mindestens ein kleines Script, dass die Dateien der zu registrierenden Klassen durchgeht und eine passende pragma-Direktive extrahiert.

Variante 3: Die Naive:

Die einfachste (und imo auch schlechteste) Lösung besteht darin die Registrierung einfach in der cpp-Datei der Factory zu zentralisieren und damit auf die Automatik zu verzichten.

Variante 4: Die obligatorische die da wo Templates verwendet.

Variante 3 lässt sich mit ein paar Tricks auch so realisieren, dass nicht eine Datei alle Klassendefinitionen kennen muss. Vielmehr kann man die Abhängigkeiten auf Vorwärtsdeklarationen beschränken.

Das Ganze geht so:

1. In einer Datei (z.B. factory_init.h) deklarieren wir eine Template-Funktion namens registerType:

C++:
///////////////////////////////////////////////////////////////////////////////
// specialize this function for each object type in the type's cpp-file
///////////////////////////////////////////////////////////////////////////////
template <class T>
void registerType();



2. In der selben Datei definieren wir eine Templateklasse die in ihrem Konstruktor die passende registerType-Instanziierung aufruft:

C++:
///////////////////////////////////////////////////////////////////////////////
// ignore this class
///////////////////////////////////////////////////////////////////////////////
template<class T>
class RegisterType {
public:
  RegisterType() {
    registerType<T>();
  }
};



3. In der cpp-Datei jeder zu registrierenden Datei spezialisieren wir nun registerType und implementieren dort alles was zur Registrierung notwendig ist:

C++:
// foo.cpp
#include <foo.h>
#include <factory.h>
#include <factory_init.h>
...

// an explicit speicalization is not a template and can be defined in a cpp-file
template <>
void registerType(Foo*)
{
    Factory::instance().registerObj("Foo", ...);
    ...
}



4. Nun müssen wir noch irgendwo für jeden zu registrierenden Typ ein RegisterType-Objekt anlegen. Dazu eignet sich z.B. eine Klasse mit vielen RegisterType-Membern.

C++:
///////////////////////////////////////////////////////////////////////////////
// add an instance of RegisterType to InitFactory for each object type
// you want to register. Use a forward declaration as the template argument.
//
// Finally, instantiate an object of this class either in your application's main function
// or in the factory's cpp-file.
///////////////////////////////////////////////////////////////////////////////
class InitFactory {
  RegisterType<class Foo> fooReg;
  RegisterType<class Bar> barReg;
  ...  
};



5. Als letztes muss noch irgendwo eine InitFactory-Instanz angelegt werden. Eine Möglichkeit wäre z.B. in der main-Funktion der Anwendung. Alternativ kann man aber z.B. auch eine globale Instanz in der cpp-Datei der Factory anlegen. Da die Factory von der Anwendung referenziert wird, wird ihre obj-Datei garantiert gelinkt und damit auch file-statische Variablen initialisiert.

C++:
// factory.cpp
namespace {
    InitFactory registerKnownTypes;
}





Hier wird jetzt auch klar, warum registerType bzw. RegisterType Templates sein müssen. Als Template-Argumente können auch Vorwärtsdeklarationen verwendet werden.

Diese Lösung hat also den Vorteil, dass die Registrierungslogik dezentral (jeweils in der cpp-Datei des zu registrierenden Typs). Außerdem sind die Abhängigkeiten alle nur "by name". Factory, InitFactory und Co sind also nicht von der Definition der zu registrierenden Klassen abhängig. Es bleibt aber natürlich nach wie vor der Nachteil, dass die zu registrierenden Typen irgendwo einmal explizit benannt werden müssen.

Letztlich lässt sich mit dieser Technik Variante 2 portabel implementieren.

Abschließend:
Warum Templates? Nur Templates ermöglichen beliebig viele Implementation für ein und den selben Namen. Außerdem erlauben Templates als Argumente vorwärtsdeklarierte Typen.

Warum sowohl eine Funktion registerType und eine Klasse RegisterType? Weil's hübscher ist. Man könnte auch registerType weglassen und stattdessen jeweils den Ctor von RegisterType spezialisieren.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 <     [ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: