Seite 1 von 1

Singletons und dll boundaries

Verfasst: 08.09.2010, 12:44
von Hellhound
Hallo zusammen,

um nicht diesen Thread http://zfx.info/viewtopic.php?f=4&t=881 weiter zuzumüllen habe ich mal einen neuen Thread bzgl. meiner Singletonproblematik aufgemacht. Wie gesagt ich komme aus der Linux Welt da läuft alles Bestens, aber Windoof bringt mich langsam zur Verzweifelung ...

Hierbei orientiere ich mich an dem Link den ich von Kimmi erhalten habe http://www.gamedev.net/community/forums ... _id=384292 und diesem Link http://www.gamedev.net/community/forums ... 1&#3507809 aber irgendwie bekomme ich das ganze nicht lauffähig...

Ich habe ein Singleton, das Templatebasiert ist, zur Behandlung globaler Konfigurationen, das im Modul brConfig abgelegt ist. Hier wird auch ein demo mit gebaut, dass zeigt, dass diese Config funktioniert (ConfigTest.exe).

Aus dem brConfig wird eine shared Library gebaut, die dll im Subverzeichnis lib abgelegt. Rufe ich nun in meiner Applikation dieses Singleton auf und setze einen beliebigen Wert (in diesem Fall ein String) und frage diesen Wert später in einem anderen Kontext (Modul) ab, dann erhalte ich 0. Mein Demo (SingletonTest.exe) zeigt auch wunderbar, dass in der dll das Singleton eine andere Adresse hat und daher der Wert in Konsequenz 0 ist weil das Objekt den Wert nicht kennt ...

Ich habe bereits an der brConfig die dllexport Definition via Makro gesetzt (brCore::brCommons.h) und auch im brConfigurations.cpp Modul den Template export berücksichtigt wie er im 2ten oben genannten Thread benannt wurde. Dennoch scheint hier was nicht zu klappen.

Zur Demonstration, falls jemand es probieren möchte ohne es zu bauen, liegen beide Exe-Dateien im lib Verzeichnis ab. Gebaut wird das ganze bei mir mit MSYS und MinGW, i.v.m. Boost 1.78. Die Test-Sourcen könnt Ihr hier downloaden: https://sourceforge.net/projects/binrevengine/files/ Im brCore Verzeichnis sind aktuell eigentlich nur die sourcen im singleton Subverzeichnis und brCommons relevant, der Rest mußte drin bleiben um nicht den kompletten Code anpassen zu müssen...

Wie gesagt, unter Linux läuft alles Bestens, Windoof streikt und ich verzweifel langsam. Bin dankbar für jede Hilfe...

Gruß
Christian

Re: Singletons und dll boundaries

Verfasst: 09.09.2010, 00:21
von Grinch
Probier mal das hier:

Code: Alles auswählen

    static T& getInstance()
    {
        if(m_instance == 0)
           new T();

        return *m_instance;
    }

    brSingleton() 
    {
	int offset = (int)(T*)1 - (int)(brSingleton <T>*)(T*)1;
	m_instance=(T*)((int)this + offset);
    }
Theoretisch müsste offset 0 sein, aber unter Windows scheint er durch das Template wohl den Pointer zu verschieben. Ich hatte das Codestück mit dem Offset aus der Ogre-Engine gezogen und eigentlich noch nie Probleme mit Singletons.

EDIT: TSingleton->brSingleton

Re: Singletons und dll boundaries

Verfasst: 09.09.2010, 01:12
von BeRsErKeR
Legst du die interne Instanz des Singletons statisch oder dynamisch an?

Re: Singletons und dll boundaries

Verfasst: 09.09.2010, 08:49
von Hellhound
Das mit dem Offset werde ich nachher mal ausprobieren, sieht ziemlich tricky aus ;)
Legst du die interne Instanz des Singletons statisch oder dynamisch an?
Was meinst Du konkret damit?

Re: Singletons und dll boundaries

Verfasst: 09.09.2010, 11:50
von Hellhound
So hab die Mittagspause mal genutzt um Deinen Vorschlag mit dem Offset zu testen. Bringt keinen Erfolg, das Verhalten ist nach wie vor das Selbe. D.h. in der dll erhalte ich eine andere Objektinstanz und habe daher keinen Zugriff auf meine gesetzten Werte aus dem Applikationskontext :( Die Anpassungen habe ich mal neu hochgeladen https://sourceforge.net/projects/binrevengine/files/

Re: Singletons und dll boundaries

Verfasst: 09.09.2010, 12:38
von CodingCat
Mir scheint dein Problem eher zu sein, dass du die Template-Klasse zwar ordentlich mit dllexport exportierst, aber nie importierst. Sprich _declspec( dllimport ) fehlt bei Einbindung von brSingleton<brConfiguration> in Dateien außerhalb der Dll. Btw. wäre eine Kurzzusammenfassung deines Quelltextes im Post vorteilhaft gewesen, ich habe jetzt 2 Tage gebraucht, bis ich mich überwinden konnte, dein Testprojekt anzusehen. ;-)

Re: Singletons und dll boundaries

Verfasst: 09.09.2010, 13:06
von Hellhound
Oh man importieren muß ich auch noch, gnarf ist das ein murx ... Kannst Du mich mal bitte Abholen, wie das konkret aussehen müsste? Laut dieser Spec http://msdn.microsoft.com/de-de/library ... 80%29.aspx reicht ja eine Präprozessoranweisung zum exportieren oder importieren ... Irgendwie bin ich da grad gedanklich blockiert, grad auch im Zusammenhang mit CMake ...

Und sorry werde mir nächstes mal mehr Mühe geben, die Situation klar darzustellen ;)

Re: Singletons und dll boundaries

Verfasst: 09.09.2010, 13:31
von CodingCat
Vmtl reicht es, auf MY_DLL_EXPORTS zu checken und wenn NICHT exportiert wird, eine explizite Template-Instanziierung von brSingleton<brConfiguration> mit dllimport im Configuration-Header vorzunehmen.

WICHTIG: Definiere dir für jede Dll eine eigene Konstante fürs Exportieren, die du nur beim Compilieren genau dieser Dll setzt, sonst bekommst du bei mehreren Dlls, die sich gegenseitig nutzen, nur noch Exportchaos (das von dir gepostete MSDN-Beispiel ist da nämlich mehr als ungünstig mit einem schlichten _EXPORTING, sobald sowas in mehreren Dlls gesetzt wird). In den Visual Studio Standardprojekteinstellungen für Dlls ist eine solche namensgesicherte Konstante bereits vordefiniert, diese findest du in den Projekteinstellungen unter C++/Präprozessor.

Re: Singletons und dll boundaries

Verfasst: 09.09.2010, 15:14
von Hellhound
Habe mir mal Deinen Vorschlag zu Herzen genommen und für das brConfig Modul einen eigene
Konstante im Header der brConfiguration definiert:

Code: Alles auswählen

#if defined(__MINGW32__)
  #ifdef MY_DLL_EXPORTS
      #define MY_DLL_API __declspec(dllexport)
  #else
      #define MY_DLL_API __declspec(dllimport)
  #endif 

namespace binrev{
namespace brConfig{

class MY_DLL_API brConfiguration : public brCore::brSingleton<brConfiguration>
{ ...
Im cpp Modul setze ich folgendes:

Code: Alles auswählen

#define MY_DLL_EXPORTS // DLL code - wir exportieren die funktionen

#include <brConfig/brConfiguration.h>

namespace binrev {
namespace brConfig {

// You must also export the template instantiation Singleton< MyClass > into 
// the DLL you are compiling. That should be it!
template MY_DLL_API class binrev::brCore::brSingleton<brConfiguration>; 
D.h. wenn ich Dich richtig verstanden habe sollte so der Import und Export sichergestellt sein oder?
Leider brachte die Umstellung keinen Erfolg, ich habe in der Applikation und in der dll noch unterschiedliche Objektreferenzen ...

Re: Singletons und dll boundaries

Verfasst: 09.09.2010, 15:16
von CodingCat
Nein, du hast ja immer noch nirgends den Import von binrev::brCore::brSingleton<brConfiguration> sichergestellt. Die Cpp wird schließlich nur mit Export kompiliert. Im Übrigen empfiehlt es sich, den EXPORT-Schalter zentral zu definieren (Projekteinstellungen / Makefile / globaler Header).

Was ich vorgeschlagen hatte, war: wenn NICHT exportiert wird (in der Cpp wird dagegen IMMER exportiert), im HEADER (brConfiguration.h) brSingleton<brConfiguration> explizit mit dllimport zu instanziieren, wie du es in der Cpp mit dllexport tust.

Re: Singletons und dll boundaries

Verfasst: 09.09.2010, 15:49
von Hellhound
Sorry aber ich kann Dir grad nicht folgen (mag sein das ich meine kleine Rübe z.Z. zu viel mit JAVA gedöns in meinem ach so geliebten "Nebenjob" nebenbei zugedröhnt bekomme :evil: ). Könntest Du mir das an einem Code-Snipped mal genauer darstellen was Du meinst?

P.S.: Globale Einstellungen habe ich nicht, arbeite ohne IDE ganz brav mit cmake skripten auf der Konsole ...

Re: Singletons und dll boundaries

Verfasst: 09.09.2010, 19:26
von CodingCat
Wasimmer daran brav ist, eine IDE erspart einem ja schon einiges. ;-)

Ich weiß jetzt nicht, was dir noch unklar ist. In brConfiguration.h müsste vermutlich sowas wie

Code: Alles auswählen

#ifndef MY_DLL_EXPORTS
template __declspec(dllimport) class binrev::brCore::brSingleton<brConfiguration>;
#endif
rein, das wars eigentlich. Ob das dann tatsächlich funktioniert, wissen wir erst, wenn dus ausprobiert hast, Templates mit DLLs sind immer etwas Glückssache, wenn mans ne Weile nicht mehr gemacht hat. ;-)

Re: Singletons und dll boundaries

Verfasst: 09.09.2010, 20:19
von Hellhound
Genau der Part ist mir nicht klar, wie ich Ihn in den Header rein bekommen soll :?

Code: Alles auswählen

    #ifndef MY_DLL_EXPORTS
    template __declspec(dllimport) class binrev::brCore::brSingleton<brConfiguration>;
    #endif
Vor der Klasse ist brConfiguration unbekannt, was mir ja auch noch verständlich ist. Plaziere ich das ganze nach der Klassendeklaration jammert der Compiler wg. doppelter Instanziierung der Klasse binrev::brCore::brSingleton<brConfiguration> im Header ...

Re: Singletons und dll boundaries

Verfasst: 09.09.2010, 23:00
von CodingCat
Okay, jetzt verstehe ich dein Problem. Das überrascht mich, bei mir compiliert z.B. folgender Test-Code problemlos:

Header:

Code: Alles auswählen

template <class T>
struct A
{
	static T *t;

	static T& i() { if(!t) t = new T; return *t; }
	static void k() { delete t; t = NULL; };
};

template <class T>
T* A<T>::t = NULL;

struct __declspec(dllimport) B : public A<B> { };

template __declspec(dllimport) struct A<B>;
Source:

Code: Alles auswählen

int main()
{
	B::i().k();
	return 0;
}
Aber gut, wenn das dann in Echt nicht hinhaut, habe ich im Moment auch keine Idee.

EDIT: Ok, das Problem, warum das bei mir geht, ist wohl, dass zuvor das Template schonmal automatisch ohne DllImport instanziiert wird. Näher am Ziel scheint dagegen folgendes:

Header:

Code: Alles auswählen

template <class T>
struct A
{
	static T *t;

	static T& i() { if(!t) t = new T; return *t; }
	static void k() { delete t; t = NULL; };
};

template <class T>
T* A<T>::t = NULL;

struct __declspec(dllimport) B;
template __declspec(dllimport) struct A<B>;

struct __declspec(dllimport) B : public A<B> { };
Nun erhält man "error C2491: 'A<T>::t' : definition of dllimport static data member not allowed", was zumindest darauf hindeutet, dass die statische Membervariable nun tatsächlich importiert wird. Nur mit der Definition wirds jetzt halt schwierig. ;-)

Dazu sagt Stackoverflow: http://stackoverflow.com/questions/1566 ... n-in-a-dll / http://stackoverflow.com/questions/7389 ... -instances kurz: "Mission impossible!" :P

Re: Singletons und dll boundaries

Verfasst: 09.09.2010, 23:18
von Krishty
Ohne das im Detail verfolgt zu haben: Wäre es nach einer halben Woche Debugging und hunderten Zeilen Code nicht einfacher, eine globale Variable anzulegen (zumal du ja nun sowieso mit dllexport/dllimport vertraut bist)? Ich finde, dass man DLLs minimal benutzen sollte … und wenn man es muss, dann sollte man geteilte Templates vermeiden … und Polymorphie auch … und Singletons („Singletones“ wäre übrigens ein toller Name für eine Programmiererband) rühre ich selbst ohne DLL nicht mit der Kneifzange an … aber bei dir kommt alles zusammen.

Ich weiß nichts über die Umstände und die Komplexität des Projekts, also vergiss den Rat, falls er realitätsfern ist …

Re: Singletons und dll boundaries

Verfasst: 10.09.2010, 08:40
von Hellhound
Ah ok, ich schau mir das noch mal an. Irgendwie sollte es ja möglich sein ...

Krishty Deine Argumentation ist zwar nachvollziehbar, aber wir entwickeln in erster Linie unter Linux und da läuft alles bestens. Warum sollte ich etwas sauberes vermurxen nur weil das §$!"§"§!! Winmurx mit den einfachsten Basismechanismen nicht klar kommt? Außerdem ist doch das Glücksgefühl gleich noch viel größer, wenn man den bösen Widows Dämonen am Ende doch besiegt hat ;)

Re: Singletons und dll boundaries

Verfasst: 10.09.2010, 18:50
von Hellhound
So ich denke ich habe die Lösung, das Demo läuft zumindest wie es sollte :D

CodingCat Deine Hinweise waren schon mal sehr gut. Ich habe letztendlich im Header der konkreten Singleton-Klasse (brConfig) doch den Export unterbekommen,
allerdings als extern Definition die nur angezogen wird, wenn exportiert werden soll (Definition im cpp-Modul):

Code: Alles auswählen

#ifndef MYLIB_EXPORTS
   extern template MYLIB_API class binrev::brCore::brSingleton<brConfiguration>;
#endif 


Das hat jedoch nicht ausgereicht da ich hierduch in linkerfehler gelaufen bin, die getInstance Methode konnte nicht mehr aufgerufen werden :evil:.
Letztendlich bin ich bei meiner Recherche über den folgenden Link im Ogre Forum gestolpert:
http://www.ogre3d.org/forums/viewtopic. ... 6f4e2718c6 Entscheidend war hier die Info, dass sie alle
Singleton Getter überladen, weil sie sonst linkerfehler bekommen :idea:

Ich habe nun "nur noch" letztendlich den statischen Getter mit ins konkrete Singleton mit aufgenommen und die 0-Instanzierung der
Instanzvariablen aus dem globalen Singleton ins konkrete Singleton gezogen so dass das cpp-Modul dieses Singletons nun so
ausschaut:

Code: Alles auswählen

// Ensure that the dll hader will be exported
#define MYLIB_EXPORTS
#include <brConfig/brConfiguration.h>

namespace binrev {
namespace brConfig {

template <> brConfiguration* brCore::brSingleton<brConfiguration>::m_instance = 0;

brConfiguration::brConfiguration() 
: binrev::brCore::brSingleton<brConfiguration>()
{}

brConfiguration& brConfiguration::getInstance()
{
   if(!m_instance){
       printf("Neue brConfiguration Instanz erzeugt!");
       m_instance = new brConfiguration();
   }
   assert(m_instance);
   return *m_instance;
}

[...]
Tataaaa und das ganze funzt :mrgreen:
Da ich mir kaum vorstellen kann, dass ich mit dem Problem allein auf dieser Welt bin, haben wir mal für interessierte unseren SingletonTest
auf das minimalste reduziert und bei uns die Sourcen inkl. Demofiles im Projekt unter Sourceforge zum Schnuppern und lernen bereitgestellt: https://sourceforge.net/projects/binrev ... z/download

Danke für den Schubbs in die Korrekte Richtung! Und wieder Evil Windows in die Schranken vewiesen ;)