Hallo zusammen!
Ich habe mal wieder eine Frage und wollte mich bei euch vergewissern, dass ich nicht noch was übersehen habe, bevor ich dann einen
QC Eintrag dazu eröffne.
Einführung & Problem
Es ist bekannt, dass der UInt64 nur halbherzig unterstützt wird und leider nie richtig durchgezogen wurde. In Delphi selbst ist es soweit ein fast nutzbarer Typ und auf C++ Seite steht dem ein
unsigned __int64 zur Seite. Nun ist es aber ein Problem, wenn man beide Sprachen zusammen in einem Projekt nutzt.
Grundlegend wird UInt64 gebraucht, da die Werte grösser als $7fffffffffffffff werden und somit uns das invertieren durch das Vorzeichen in die Seite haut.
Es ist ein bekanntes Problem, dass eine UInt64 Deklaration in einer Pascalquelle vom Header (.hpp) Generator falsch in ein
__int64 übersetzt wird (
QC: 57612). Nun führt dies aber wiederrum beim C++ Linker zu einem Problem, da der Header was von einem
signed __int64 erzählt, aber der Linker keine Funktion mit dieser Signatur findet (
QC: 57613), sondern nur mit einer vom Pascal Compiler erstellten (richtigen) Signatur mit einem
unsigned __int64.
Ok, nun, da hat man ein Problem. Der Umweg über den nicht berechenbaren Struct (z.B. Int64Rec, TFileTime, etc) wollten wir definitiv nicht gehen und somit musste eine andere Lösung her.
Lösungen
Die bisherigen Lösungen:
1. Man ändert immer nach deren Generierung die Header files ab zu den jeweiligen Pascal Quellen zu einer
unsigned __int64 Deklaration, so dass der Linker und Compiler glücklich sind.
2. Man nutzt andere Typen wie z.B. einen Record um die Daten zu übergeben (z.B. Int64Rec, TFileTime, etc).
3. Man definiert einen Linker Alias Eintrag
#pragma alias "@unit@class@method$typelist@resulttype"="@unit@cl ass@method$typelist@resulttype"
Beim Lösung 1 und 3 muss jeder Autor, welcher UInt64 nutzen will, eine extra Zeile bzw Aufwand betreiben, damit im Buildskript (1. Lösung) bzw. in der Quelle die richtigen
zusätzlichen Angaben gemacht werden. Dies lässt sich somit für nicht-Programmierer schwer durchsetzen, vor allem die 3. Lösung benötigt schon einen gewissen Aufwand und Wissen. Die erste Lösung ist in der
IDE nicht praktizierbar und die zweite Lösung fällt aufgrund des unhandlichen Typs weg.
So kam nun die Idee auf vorhandene Möglichkeiten zurück zu greifen: Borland bietet mit $NODEFINE, $EXTERNALSYM und $HPPEMIT eine recht interessante Möglichkeit die Headergenerierung zu beeinflussen. Somit haben wir uns einfach dazu entschlossen einen neuen Typ zu definieren. Dieser wird dann anstatt des UInt64 benutzt und somit würde jeder auf die Nase fallen, der nicht den Typ benutzt (Linker kann nicht auflösen) und wir haben einen Typ der auf beiden Seiten bekannt ist und somit aufgelöst werden kann.
Also entstand folgende kleine Pascal Datei:
Delphi-Quellcode:
unit FixUInt64;
{$Z4}
interface
type
TFixUINT64 =
type UInt64;
implementation
end.
(Die $Z4 kann man getrost ignorieren, aber in der C++/Delphi Mischprojektumgebung gibt es einen Bug, dass die Pascaloptionen nicht mehr an implizit angezogene Pascalquellen angewendet werden. Dazu wird noch ein
QC Eintrag geschrieben werden)
Daraus macht der Header Generator (leider erfahrungsgemäß):
Code:
typedef __int64 TFixUINT64;
... also den falschen Typ. Somit bekommt der Linker mit dem Typ wieder ein Problem. Somit: selbst deklarieren:
Delphi-Quellcode:
unit FixUInt64;
{$Z4}
interface
type
TFixUINT64 =
type UInt64;
{$EXTERNALSYM TFixUINT64}
{$NODEFINE TFixUINT64}
{$HPPEMIT 'typedef unsigned __int64 TFixUINT64;'}
implementation
end.
Ok, somit kommt schonmal das gewünschte Ergebnis in den Header:
Code:
typedef unsigned __int64 TFixUINT64;
. Nun ist diese Hürde genommen, also losarbeiten:
Delphi-Quellcode:
unit PasInterfaceImpl;
{$Z4}
interface
uses
FixUInt64;
function UseUint64(
const param: TFixUINT64 ): TFixUINT64;
implementation
function UseUint64(
const param: TFixUINT64): TFixUINT64;
begin
result := param;
end;
end.
Eigentlich alles super. Von Delphi Seite nutzbar, aber bei C++....
Nun ja, der Header Generator macht folgendes draus:
Code:
extern
PACKAGE __int64 __fastcall UseUint64(const __int64 param);
Also wieder der unbrauchbare Int64 Typ.
Der Fehler?
Der Fehler liegt hier zum einen in der bekannten falschen Umsetzung des UInt64 auf __int64. Aber das wollten wir ja mit dem neuen Typ umgehen. Das eigentliche Problem ist eher, dass ich mit der Deklaration in der Pascal Quelle extra
kein Alias angelegt habe sondern definitiv einen neuen Typ (das zusätzliche
type Keyword bei der Deklaration) und somit verstehe ich nicht, wie der Header Generator diesen zwar übernimmt aber ihn bei seiner Nutzung nicht entsprechen übernimmt. Der neue Typ ist definiert und wird explizit von der Funktion genutzt und er kann ihn so 1:1 übertragen, tut dies aber nicht.
Hätte ich nicht einen neuen Typ sondern einen Alias angelegt, kann ich es verstehen, dass er zum Basistyp zurückkehrt, aber nicht, wenn ich einen explizit neuen Typ anlege. Ich sehe hier den Fehler darin, dass er diesen Typ nicht überträgt.
Bei den einfachen Funktionen könnte man auch mit $NODEFINE und $HPPEMIT arbeiten, nicht aber bei Methoden, da dort nur die gesamte Klassendeklaration nicht übernommen werden kann, nicht aber einzelne Methoden.
Die Frage
Kennt ihr noch andere mögliche Lösungen bzw. gibt es noch weitere Angaben oder Schlüsselwörter, die den Header Generator zur Mitarbeit überreden lassen? Seine Automatismen bzw. Anpassungen vllt. mal zu unterlassen?
/EDIT: Beschriebenes Problem bezieht sich auf das BDS2006! Bisher besteht gleiches Problem aber noch bei RAD2007, die Lösung wird aber für BDS2006 gesucht.