momentan arbeite ich an einer 3D-Abstraktionssoftware, wenn man es so nennen kann, damit soll die Arbeit bzw. die Erstellung von 3D-Spielen vereinfacht werden. ( Sie schließt sowohl Direct3D, als auch DirectSound, DirectShow, DirectInput und eine eigene Physikengine ein. Allaround Packet eben. ) Das Projekt ist ziemlich monstermäsig, aber ich habe ja als Schüler ordentlich Zeit zu viel.
Nun, das ganze ist in FreePascal geschrieben und basiert ebenfalls auf COM-Interfaces, allerdings hält es sich nicht ganz an jene Vorschriften. :3schau Ich versuche es so benutzerfreundlich wie möglich zu halten und da ist COM mit seinen Rückgabeparametern nicht so mein Ding ist, zumal man in FreePascal ja nicht mitten drin, so schön wie in C/++, eine Variable deklarieren kann, muss ich es so machen.
Aber naja nun zu meinem Problem, ich würde gerne ein Record bzw. in das C-Equivalent nennt sich struct (?) zurückgeben.
Funktion wird nun aus der DLL exportiert. Nun will ich eine Portierung der Header in C/++ umsetzen. Ich finde es ehrlich gesagt komisch, dass man in C/++ DLL Funktion so fucking kompliziert importieren muss. In FreePascal tuts das Schlüsselwort extrenal und dann der DLL-Name. In C/++ muss man sich erst ein teures Tool kaufen, bzw. sich mit der Testversion rum schlagen um überhaupt an die .lib-Datei zu kommen. Aber das ist ja auch nicht das Problem.
Die Funktionen gehen auch alle, zumindest Testfunktionen mit einfachen Rückgaben wie Int's bzw. Strings ( PChar, Char* ), allerdings wird bei einem Record von einer Zugriffsverletzung die Rede. Ich weiß nur nicht wieso? Bin ich da an die Grenzen der beiden Sprachen angekommen, sind sie in ihrem System zu unterschiedlich oder ist es einfach nur in simpler Denkfehler? Muss ich es etwa echt über Rückgabeparameter machen?
hm ... schwierig .. Intersprachenoperabilitaet ist etwas, um das ich mich lange Zeit gedrueckt habe ...
Zum einen .. Im Header definierst du keinen Variablentyp TMyRecord, sondern einen struct vom Typ MyRecord und eine Variable TMyRecord vom typ MyRecord.
(Falls ich gerade Bloedsinn schreibe, bitte ignorieren .. bin noch etwas Schlaftrunken ...)
Zum anderen solltest du mal nachsehen, ob stdcall fuer die Uebergabe das nonplusultra ist. Sowohl in Pascal als auch in C/++ muessen beide Funktionen die selbe Aufrufkonvention haben. Sprich ob sie selber den Stack aufraeumen, in welcher Reihenfolge sie Parameter uebergeben, etc.
Im Zweifelsfall machs, wie alle, und arbeite mit Handles. Erstelle in der Funktion einen Pointer auf deinen Record im Heap, gib den als funktionswert zurueck und mache eine Funktion, die den als Parameter bekommt und es am ende Frei gibt. Effektiv ist die Arbeit mit Interfaces auch nichts anderes ...
hm ... schwierig .. Intersprachenoperabilitaet ist etwas, um das ich mich lange Zeit gedrueckt habe ...
Zum einen .. Im Header definierst du keinen Variablentyp TMyRecord, sondern einen struct vom Typ MyRecord und eine Variable TMyRecord vom typ MyRecord.
(Falls ich gerade Bloedsinn schreibe, bitte ignorieren .. bin noch etwas Schlaftrunken ...)
Zum anderen solltest du mal nachsehen, ob stdcall fuer die Uebergabe das nonplusultra ist. Sowohl in Pascal als auch in C/++ muessen beide Funktionen die selbe Aufrufkonvention haben. Sprich ob sie selber den Stack aufraeumen, in welcher Reihenfolge sie Parameter uebergeben, etc.
Im Zweifelsfall machs, wie alle, und arbeite mit Handles. Erstelle in der Funktion einen Pointer auf deinen Record im Heap, gib den als funktionswert zurueck und mache eine Funktion, die den als Parameter bekommt und es am ende Frei gibt. Effektiv ist die Arbeit mit Interfaces auch nichts anderes ...
...
Allesklar, danke. Die Aufrufkonvention habe ich total vergessen. Hatte irgendwie im Kopf, dass bei C/++ stdcall Standart wäre. Ich habe nun die Variabeldeklaration weggelassen und einfach nur den Type definiert. Nun geht es.
Nun meine Frage, was ist denn die Standart-Aufrufkonvetion in C/++? Scheinbar mag mein Compiler die .lib-Datei nicht mehr, seitdem ich die Aufrufkon. auf __stdcall gesetzt habe. Dahher versuche ich lieber die Funktionen entsprechend in Pascal so zu exportieren, dass C/++ am wenigsten Probleme machen.
Das kommt auf die Funktion an ... stdcall ist glaub ich Standard fuer Windows API funktionen. Normales C/++ verwendet iirc cdecl.
Im Zweifelsfalle auf keine Standards verlassen und immer angeben, was genau du haben willst. Du kannst in C/++ genau so Funktionen mit Pascal kodierung verwenden, wie du in Pascal Funktionen aus C/++ oder der API benutzen kannst. Der Linker muss nur genau wissen, welche der etwa 5 bis 10 verschiedenen Standards du nun benutzt. Und natuerlich sollten die auf beiden Seiten gleich sein.
Zitat
Die Industrie liebt Standards. Deswegen hat jede Firma ihren eigenen.
Im Header definierst du keinen Variablentyp TMyRecord, sondern einen struct vom Typ MyRecord und eine Variable TMyRecord vom typ MyRecord.
(Falls ich gerade Bloedsinn schreibe [...])
...
Ja, tust du. :P
Hier wird die Struktur MyRecord definiert und ihr "Alias" TMyRecord. TMyRecord ist aber keine Definition einer Variablen im Speicher.
@Desmulator:
In Win32 ist die Aufrufkonvention für exportierte Funktionen aus DLLs __stdcall und das ist Pflicht. Die Definitionen dieser Funktionen innerhalb der DLLs und die Deklarationen in den ABIs müssen selbstverständlich in der Aufrufkonvention übereinstimmen. Man sollte sich jedoch nicht darauf verlassen, dass jeder Compiler/jedes Projekt dieselben Einstellungen besitzt und die Aufrufkonvention explizit angeben.
Die Aufrufkonvention ist allerdings nur eine Hürde von ABIs, eine weitere wäre die Speicherausrichtung. Wenn du verschiedene Compiler zulassen möchtest (das gilt sowohl für die FreePascal-Compiler, als auch für die C/C++-Compiler), musst du sicherstellen, dass Padding für zusammengesetzten Datentypen äquivalent aussieht. Eine Möglichkeit dies zu tun wäre Padding ganz auszuschalten, indem du die Ausrichtung während der Deklaration auf '1' setzt, allerdings mit dem bitteren Nachgeschmack, dass die Anweisungen dafür compilerabhängig sind und, dass fehlende Speicherausrichtung die Performance drücken kann, falls man mit den nicht ausgerichteten Datentypen in performancekritischen Schleifen arbeitet.
Für das letztere Problem, falls es eintreten kann, kannst du einfach Objekte mit nicht ausgerichteten Membern in Objekte mit ausgerichteten Membern umwandeln.
Für das erstere Problem musst du mit Präprozessordirektiven den Compiler feststellen und z.B. wie folgt (in C/C++) auflösen:
Was DLLs angeht, solltest du unbedingt auch aufpassen die DLL-Boundary nicht zu verletzen, also Daten, die innerhalb der DLL alloziert wurden auch innerhalb der DLL wieder freigeben.
Hier wird die Struktur MyRecord definiert und ihr "Alias" TMyRecord. TMyRecord ist aber keine Definition einer Variablen im Speicher.
...
Ah ... ich hatte das typedef ueberlesen. Ohne das Typedef haette er eben die Klasse MyRecord definiert und gleichzeitig eine Variable TMyRecord erzeugt. Wie gesagt, ich hab damals meinen Post im Halbschlaf verfasst, sorry.