Funktionsdefinitionen in Headern einer DLL
-
Hallo,
ich habe ein Problem mit einem Projekt, dass bisher wunderbar unter Windows (mit VC7) und Linux (mit gcc) lief. Seitdem aber ein Teil des Projekts in einer DLL liegt, funktioniert es nicht mehr unter Windows. Der Zusammenhang ist recht kompliziert, ich habe das Problem aber folgendermassen isolieren können:
In einer DLL gibt es eine Basisklasse A, die eine statische Methode generate_id() besitzt, welche bei jedem Aufruf eine neue Id zurückliefert. Die Unterklassen von A haben eine statische Methode static_id(), welche beim ersten Aufruf generateId() benutzt, um eine neue Id zu erzeugen, welche die Klasse eindeutig repräsentieren soll. Das Problem dabei ist nun, dass die Implementierung von static_id() direkt in der Klasse im Header stehen muss, da die Unterklassen von A in meinem Projekt durch Makros erzeugt werden.
Unter Linux funktioniert das wie gesagt wunderbar, aber unter Windows hat eine Klasse nun innerhalb der DLL eine andere static_id als ausserhalb der DLL.
Folgender Code illustriert das Problem:
TestDll.h
#define EXPORT __declspec(dllexport) class A { public: EXPORT static int generate_id(); }; class B : public A { public: EXPORT static int static_id() { static int id = 0; if( !id ) id = A::generate_id(); return id; }; }; class Container { public: EXPORT Container(); A* a; };TestDll.cpp
int A::generate_id() { static int id_counter = 0; return ++id_counter; } Container::Container() { a = new B(); cout << ( (B*)a )->static_id(); }Das Problem zeigt sich nun an folgendem Verhalten in einem Aufrufer der Dll:
Container* t = new Container();liefert 1, während ein direkt darauf folgender Aufruf von
cout << B::static_id();2 liefert.
Das Problem wird wohl daran liegen, dass Client und DLL, durch die Definition im Header mit unterschiedlichen Versionen der Funktion static_id() arbeiten. Das zeigt auch der Umstand, dass, sobald ich static_id() in der cpp-Datei definiere (was aber in meinem Kontext wg. des Makros nicht möglich ist), wieder alles funktioniert.
Dass der Linker sich dabei nicht über eine Mehrfachdefinition beschwert, dürfte wohl an der One-Definition-Rule liegen, welche für die inline-Definition greift. Ob die Funktion tatsächlich inline expandiert wird, weiss ich nicht, spielt aber auch keine Rolle, da ein explizites Unterbinden der Expansion durch /Ob0 oder #pragma auto_inline( off ) auch keinen Unterschied macht.Hat jemand eine Idee?
Für jede Hilfe dankbar,
Marcel Otto
-
Warum exportierst Du nicht die ganze Klasse?
-
Habe ich auch schon versucht. Keine Veränderung.
-
Hallo,
ich geh mal davon aus, das du dich immer im selben Thread bewegst:
Ob die Funktion tatsächlich inline expandiert wird, weiss ich nicht, spielt aber auch keine Rolle
wie kommst Du darauf? Ich bin mir gerade nicht 100% sicher, aber ich kann mir nicht vorstellen, dass unter VC irgendein Schalter eine mehrfache inline deklaration der static int variablen verhindert.
-> Hast du versucht die Deklaration von
static int id = 0;innerhalb von static_id() mal in den Klassenheader zu schieben und die initialisierung, dann außerhalb der Funktion zu machen?
Falls eine inline Expansion stattfindet, könnte zumindest
static int id = 0;
in
static_id()
mehrfach vorkommen, was das Problem erklären würde, da dann
A::generate_id();
mehrfach aufgerufen wird ?
-
Manu76 schrieb:
Hallo,
ich geh mal davon aus, das du dich immer im selben Thread bewegst:
Korrekt.
Manu76 schrieb:
Ob die Funktion tatsächlich inline expandiert wird, weiss ich nicht, spielt aber auch keine Rolle
wie kommst Du darauf? Ich bin mir gerade nicht 100% sicher, aber ich kann mir nicht vorstellen, dass unter VC irgendein Schalter eine mehrfache inline deklaration der static int variablen verhindert.
100% sicher, bin ich mir da nun auch nicht. Aber Inlining dient doch der Optimierung, bei der man den Compiler durch Explizit-Machung unterstützen kann. Schon klar "There is no guarantee that functions will be expanded inline. You cannot force the compiler to inline a particular function." [ http://msdn2.microsoft.com/en-us/library/47238hez(VS.71).aspx ] Da (und auch bei anderen Quellen die ich zu Rate gezogen habe) steht aber nichts davon, dass es auch keine Garantie gibt, dass eine Funktion NICHT inline expandiert wird.
Ich zumindest wüsste auch keine Gründe, die eine Inline-Expansion zwingend machen.Ausserdem vergass ich den Umstand zu erwähnen, dass eine Verlagerung der Definition in die cpp-Datei, aber eine Kennzeichnung mit dem inline-Qualifizierer NICHT mehr zu dem Problem führt.
BTW: Inwiefern handelt es sich um "eine mehrfache inline deklaration"?
Manu76 schrieb:
-> Hast du versucht die Deklaration von
static int id = 0;innerhalb von static_id() mal in den Klassenheader zu schieben und die initialisierung, dann außerhalb der Funktion zu machen?
Das geht ja leider auch nicht, wegen des Makros.
Manu76 schrieb:
Falls eine inline Expansion stattfindet, könnte zumindest
static int id = 0;
in
static_id()
mehrfach vorkommen, was das Problem erklären würde, da dann
A::generate_id();
mehrfach aufgerufen wird ?
Das eben das passiert, glaube ich ja auch. Da mir die oben genannten Gründe aber immer noch gegen eine Inline-Expansion sprechen, verwundert mich das Verhalten aber schon ziemlich.
-
Hallo,
mit meiner Wortschöpfung meinte ich einfach, dass er die inline Funktion mehrfach einfügt!, dann hast Du auch mehrere "static int id", die voneinander in verschiedenen Gültigkeitsbereichen unabhängig sind! Ich kenne den ISO-Standard nicht, aber hört sich für mich logisch an, das er sowas zusammen mit inline Funktionen machen könnte (Die Funktion existiert physisch einfach mehrmals im compilierten Code!).
Was ich nicht verstehe, bzw hast Du das getested, ist:
class B : public A { static int id; public: EXPORT static int static_id() { if( !id ) id = A::generate_id(); return id; }; };und in der cpp, dann "id" mit 0 initialisieren. Das müßte er doch compilieren, welches Makro genau soll das verhindern?
-
Wie ich erwähnte werden die Unterklassen von A in meinem Programm durch Makros definiert (Ich weiss, dass klingt erst mal nicht sehr vernünftig, macht aber im Kontext meines Programms durchaus Sinn). Dadurch muss die Definition eben in einem Rutsch in der Header-Datei erfolgen. Kann dem Makro ja nicht sagen: schreib diese Definition von static_id (oder wie in Deinem Code, die Initialisierung von id) noch in die cpp-Datei.