PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : dllexport und c++ stream problem


Chris Lux
2006-09-28, 11:34:55
hi,
folgendes:
in einer dll wird eine klasse X exportiert:

class __scm_export X
{
public:
X();
virtual ~X();

protected:
std::ostringstream _output_buffer;
std::stringstream _input_buffer;
...


dazu soll in der dll eine globale referenz auf ein solches object bestehen:

extern __scm_export X& __x__;

diese instanz wird in der dll auch instanziiert

X& __x__ = X();


der witz nun: das problem sind die streams in der klasse. sind diese darin vorhanden wird der konstruktor nicht aufgerufen. alle anderen member der klasse sind vorhanden, auf sie kann zugegriffen werden aber sie sind nicht initialisiert. beim zugriff (schreibend) auf die streams bekommt man einen laufzeitfehler. auch wenn die streams für nichts verwendet werden wird der konstruktur nicht aufgerufen. nehme ich die streams raus ist alles ok. füge ich sie als referenzen ein, die im konstruktor initialisiert werden ist bis auf den zugriff auf sie alles ok (zugriff -> laufzeitfehler). fürge ich sie als pointer ein, welche im konstruktor initialisiert werden ist alles ok, auch der zugriff.

der zweite witz. lasse ich alles so wie oben (dh direkt als member) und lege im hauptprogramm eine zweite instanz an ist dort alles ok, dh keine fehler usw.

was ist das problem mit streams (auch fstreams usw) als member von dll klassen, wenn diese in der dll auch instanziiert werden?

muhkuh_rs
2006-09-28, 11:58:09
Verwenden die dll und die exe die gleichen Laufzeitbibliotheken?

Chris Lux
2006-09-28, 11:59:04
Verwenden die dll und die exe die gleichen Laufzeitbibliotheken?
jap,
komisch ist für mich, dass es nur mit streams nicht geht. mit strings, vector, map, deque usw. geht es.

Gast
2006-09-28, 14:20:10
Sind Bezeichner die mit __ vom C++-Standard nicht für den Compiler reserviert?

muhkuh_rs
2006-09-28, 14:32:52
Poste doch mal mehr Code. Vielleicht __declspec(dllimport) beim Import der globalen Variablen in der exe vergessen und statt dessen nur extern verwendet?

Chris Lux
2006-09-28, 16:26:31
Poste doch mal mehr Code. Vielleicht __declspec(dllimport) beim Import der globalen Variablen in der exe vergessen und statt dessen nur extern verwendet?

das __scm_export ist je nach dem, ob die dll compiliert wird oder ein dll client __declspec(dllimport) oder __declspec(dllexport). da ist das problem wirklich nicht. der gepostete code ist im endeffekt schon alles, was den fehler auslöst...

muhkuh_rs
2006-09-29, 10:44:23
das __scm_export ist je nach dem, ob die dll compiliert wird oder ein dll client __declspec(dllimport) oder __declspec(dllexport). da ist das problem wirklich nicht. der gepostete code ist im endeffekt schon alles, was den fehler auslöst...
Wenn ich versuche das nachzuvollziehen, bekomme ich bei stream Membern Warnung C4251.

http://www.unknownroad.com/rtfm/VisualStudio/warningC4251.html

Chris Lux
2006-09-29, 12:10:02
hier mal ein minimales visual studio 2005 projekt zum nachvollziehen.

das problem ist, dass sobald der stream teil der klasse ist der kontruktor nicht mehr aufgerufen wird und somit alles in der klasse uninitialisiert bleibt.

muhkuh_rs
2006-09-29, 13:00:06
Wenn ich in dem Projekt statt einer globalen referenz ein globales Objekt verwende, funktioniert es. Statt

dll_export.cpp:
test_export& export_ref = test_export(std::string("sucess"));
test_export export_ref(std::string("sucess"));

dll_export.h:
extern __scm_export test_export& export_ref;
extern __scm_export test_export export_ref;

Bei warning level 4 meint VC2005 auch:
warning C4239: nonstandard extension used : 'initializing' : conversion from 'test_export' to 'test_export &'

Frag mich nicht wieso.

Obligaron
2006-09-29, 14:33:56
Hallo,

test_export& export_ref = test_export(std::string("sucess"));
kann nicht funktionieren, weil du einer Referenz aka Pointer eine lokale Variable zuweist, die nur während dieser Zeile vorhanden ist.
Z.B. liefert AfxIsValidAddress hier immer FALSE zurück.

test_export& export_ref = *(new test_export(std::string("sucess")));
würde z.B. gehen.

Es war also imho reines Glück, das du danach den string noch auslesen konntest.

Gruß,
obligaron

Chris Lux
2006-09-29, 14:37:13
Wenn ich in dem Projekt statt einer globalen referenz ein globales Objekt verwende, funktioniert es. Statt[...]
ok.

edit: korrektur

Chris Lux
2006-09-29, 14:39:54
test_export& export_ref = *(new test_export(std::string("sucess")));
würde z.B. gehen.

sehr interessant, das geht...

folgendes hatte ich vor dem repro projekt schon ohne erfolg getestet. jetzt geht es (ka was da noch schief ging). aber ob ich nun eine zweite variable anleg oder dies wie oben eine lokale variable nutze sollte an sich doch keinen unterschied machen. anders initialisiert man ja keine referenzen...

test_export export_ref2(std::string("sucess"));
test_export& export_ref = export_ref2;

muhkuh_rs
2006-09-29, 14:40:18
Du schriebst Du hättest in main eine zweite Instanz angelegt. Das ist doch was anderes oder was meintest Du?

Chris Lux
2006-09-29, 14:49:11
Du schriebst Du hättest in main eine zweite Instanz angelegt. Das ist doch was anderes oder was meintest Du?
das war nur zu testzwecken ob ich überhaupt eine instanz anlegen kann.

das seltsame für mich ist aber, dass ohne einen stringstream in der klasse die variante mit der temporären variable (wie im repro projekt) funktioniert. meiner meinung nach sollte es kein unterschied sein ob ich nun eine variable erzeuge und die zur initialisierung der referenz nutze oder dies mit einer temporären tue...

muhkuh_rs
2006-09-29, 16:20:35
das war nur zu testzwecken ob ich überhaupt eine instanz anlegen kann.

das seltsame für mich ist aber, dass ohne einen stringstream in der klasse die variante mit der temporären variable (wie im repro projekt) funktioniert. meiner meinung nach sollte es kein unterschied sein ob ich nun eine variable erzeuge und die zur initialisierung der referenz nutze oder dies mit einer temporären tue...

Das Problem scheint mir folgendes zu sein:
test_export export_ref = test_export(std::string("sucess"));

Dabei ruft VC2005 den copy constructor von test_export auf. Der ist nicht verfügbar, da der Compiler keinen default copy constructor generieren kann (dadurch dass stringstream keinen hat). Sobald man test_export einen gibt, ist wieder alles in Ordnung:

test_export::test_export(const test_export & other)
:_name(other._name)
{
}

Komisch finde ich nur, dass VC2005 da keinen compile time error wirft.
Edit: VC2003 lässt sich das nicht gefallen und mault zur compile time.



Das mit der Referenz sollte wie oben schon erwähnt gar nicht gehen.
test_export & export_ref = test_export(std::string("sucess"));

Dabei wird erst ein temporäres test_export erzeugt, die Referenz zeigt dann darauf und danach wird das temporäre Objekt wieder zerstört und die Referenz zeigt auf den Speicher wo das Objekt vorher war. Das bringt ja nun nichts.