Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Problemchen beim übersetzen eines headers


Borsty - Sa 06.09.08 16:38
Titel: Problemchen beim übersetzen eines headers
Moinsen, ich hätte da ma 'n Problem ^^

Ich schreib zZ an einem Progrämmchen zum überwachen und steuern von Gameservern auf Source-Engine Basis (HL2,CSS,etc)
(Durch Zufall) bin ich dann vor kurzem auf eine Bibliothek direkt von Valve (von denen ist HL2 usw ^^) gestoßen um die dedizierten Server direkt anzusprechen. Geliefert wird eine .dll inkl. Hifle und Header datei für C/C++. (Link [http://developer.valvesoftware.com/wiki/Source_Server_Query_Library])

Eigentlich sollte es ja kein Problem sein das ganze für Delphi verwendbar zu machen, allerdings bin ich da auf was gestoßen womit ich (noch) nichts anfangen kann :(

Folgendes:

Quelltext
1:
typedef(WINAPI* SSQ_CALLBACK)(DWORD type,PSSQ_REPLY_UNION reply);                    


"SSQ_CALLBACK" ist ein "typedef union":

Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
typedef union
{
  PSSQ_BATCH_REPLY batch_reply;
  char* log_reply;
  PSSQ_RULES_REPLY rules_reply;
  char* rcon_reply;
  BOOL log_notify;
} SSQ_REPLY_UNION, *PSSQ_REPLY_UNION;


"SSQ_CALLBACK" wird auch später in einer Funktion als Parameter erwartet.

Im Anhang mal der gesamte header und mein bisheriger Versuch für Delphi (Der soweit auch funktioniert, nur halt alles was im zusammenhang mit diesem SSQ_CALLBACK ist funktioniert nicht :/).


Borsty - Mo 08.09.08 14:23

Keiner ne Idee? :/


BenBE - Mo 08.09.08 19:04

Kannst Du bitte mal schauen, ob Du die Offsets der einzelnen Felder in C (und das Sizeof des Datentyps) rausbekommst? Daraus sollte sich das ganze Rekonstruieren lassen ...


tommie-lie - Di 09.09.08 12:04

Hm, sehe ich den Wald aufgrund von Übermüdung vor lauter Bäumen nicht, oder ist

Quelltext
1:
typedef(WINAPI* SSQ_CALLBACK)(DWORD type,PSSQ_REPLY_UNION reply);                    
nicht vielmehr eine (Art) Deklaration eines Funktionszeigertyps als eines unions? Dann wäre die Pascal-Version davon mit meinen eingerosteten Delphi-Kenntnissen vermutlich sowas wie

Delphi-Quelltext
1:
type SSQ_CALLBACK: function(DWORD type; PSSQ_REPLY_UNION reply): ???                    
Den Rückgabetyp der Funktion kann ich gerade nicht bestimmen, normalerweise kenne ich WINAPI als __stdcall, hier scheint da entweder noch ein Typ mit reindefiniert worden zu sein, oder das ist kein valides C++. Je nach Belegung von WINAPI und Standardkonformität des ursprünglichen Compilers tut's dann auch ein procedure.


BenBE - Di 09.09.08 12:39

user profile icontommie-lie hat folgendes geschrieben:
Hm, sehe ich den Wald aufgrund von Übermüdung vor lauter Bäumen nicht, oder ist

Quelltext
1:
typedef(WINAPI* SSQ_CALLBACK)(DWORD type,PSSQ_REPLY_UNION reply);                    
nicht vielmehr eine (Art) Deklaration eines Funktionszeigertyps als eines unions? Dann wäre die Pascal-Version davon mit meinen eingerosteten Delphi-Kenntnissen vermutlich sowas wie

Delphi-Quelltext
1:
type SSQ_CALLBACK: function(DWORD type; PSSQ_REPLY_UNION reply): ???                    
Den Rückgabetyp der Funktion kann ich gerade nicht bestimmen, normalerweise kenne ich WINAPI als __stdcall, hier scheint da entweder noch ein Typ mit reindefiniert worden zu sein, oder das ist kein valides C++.

Ich kann dich schon mal beruhigen ... das ist valides C++ :P

user profile icontommie-lie hat folgendes geschrieben:
Je nach Belegung von WINAPI und Standardkonformität des ursprünglichen Compilers tut's dann auch ein procedure.

Jain ... Wenn dich der Rückgabewert nicht interessiert schon, sobald da aber was anderes als void für den Typ steht, IST es bei korrekter Übersetzung eine Function.

Übersetzen wir mal per Hand:

C#-Quelltext
1:
typedef(WINAPI* SSQ_CALLBACK)(DWORD type,PSSQ_REPLY_UNION reply);                    

typedef --> type

Erste Klammer:
WINAPI --> stdcall
* SSQ_CALLBACK --> Funktionszeiger, Typname SSQ_CALLBACK

Parameter:
DWORD type --> _type: DWORD;
PSSQ_REPLY_UNION reply --> reply: PSSQ_REPLY_UNION;

Setzt man das also zusammen:

Delphi-Quelltext
1:
type SSQ_CALLBACK: procedure(_type: DWORD; reply: PSSQ_REPLY_UNION); stdcall;                    


Bzgl. der Union mal die genannten Werte ermittelt?


tommie-lie - Di 09.09.08 13:12

user profile iconBenBE hat folgendes geschrieben:
Ich kann dich schon mal beruhigen ... das ist valides C++ :P
Hrmpf *patsch*
Nein, isses nicht, aber es ist valides ISO-C99. In C++ kannst du keine Funktionen ohne Rückgabetyp deklarieren (Ausnahmen siehe unten). In C ist es implizit int. War mir auch neu, aber ich habe nie viel mit ISO/ANSI-C gemacht, daß ich da so fit für solche Feinheiten bin. Das erklärt allerdings, warum einige Hempels in C++ main(void)(sic!) schreiben...
Aber C++ isses trotzdem nich ;-)

BenBE hat folgendes geschrieben:
user profile icontommie-lie hat folgendes geschrieben:
Je nach Belegung von WINAPI und Standardkonformität des ursprünglichen Compilers tut's dann auch ein procedure.

Jain ... Wenn dich der Rückgabewert nicht interessiert schon, sobald da aber was anderes als void für den Typ steht, IST es bei korrekter Übersetzung eine Function.
Ja, das weiß ich. Deswegen schrob ich "je nach Belegung von WINAPI". Enthält WINAPI keinen Typ, so ist es invalides C++ (oder aber valides C, siehe oben), steht da void, ist es eine procedure, steht da int oder sonstwas, ist es eine function.
Bei gängigen Belegungen von WINAPI ist der Code nur valid, wenn es C-Code sein soll, also ist es automatisch int, also ist es eine function SSQ_CALLBACK(...): Integer; (mit Vorsicht zu genießen, int ist garantiert mindestens 16 bit groß, normalerweise 32, auf IA64 64bit (auf x86-64 lustigerweise nicht/nicht immer), das mit den Delphi-Typen, die feste Größen vorschreiben, zu übersetzen, kann nach Hinten losgehen.)


<advocacy>
ISO-C++ Standard, 7.0.1 hat folgendes geschrieben:
Only in function declarations for constructors, destructors, and type conversions can the decl-specifier-seq be omitted.78)

Die Fußnote auf selbiger Seite hat folgendes geschrieben:
The “implicit int” rule of C is no longer supported.

</advocacy>


BenBE - Di 09.09.08 13:31

Man brauch (wenn man den Rückgabewert nicht benötigt) in seiner Delphi-Übersetzung diesen nicht beachten. Das Ergebnis wird bei stdcall nämlich in EAX zurückgegeben, wo Delphi von Haus aus davon ausgeht, dass das nach einem Prozeduraufruf undefiniert ist...

Ansonsten geb ich tommie-lie Recht. (Wobei die ISOs hab ich auch nicht immer im Kopf).

@tommie-lie: Ansonsten die Übersetzung aber soweit korrekt?


tommie-lie - Di 09.09.08 14:23

user profile iconBenBE hat folgendes geschrieben:
Ansonsten geb ich tommie-lie Recht. (Wobei die ISOs hab ich auch nicht immer im Kopf).
Göttin bewahre, im Kopf habe ich die Zitate auch nicht, du hast mich verunsichert, deshalb habe ich nachgeschlagen ;-)

BenBE hat folgendes geschrieben:
@tommie-lie: Ansonsten die Übersetzung aber soweit korrekt?
Ich hatte mir nur das aus der Frage angeschaut, aber beim Drüberschauen fällt folgendes auf:

Quelltext
1:
2:
3:
4:
#define SSQ_GS_TIMEOUT 1<<0
#define SSQ_LOG_TIMEOUT 1<<1
#define SSQ_MS_TIMEOUT 1<<2
#define SSQ_RCON_TIMEOUT 1<<3

wird zu

Delphi-Quelltext
1:
2:
3:
4:
5:
const
  SSQ_GS_TIMEOUT = 1;
  SSQ_LOG_TIMEOUT = 2;
  SSQ_MS_TIMEOUT = 4;
  SSQ_RCON_TIMEOUT = 8;
und nicht zu 0, 1, 2 und 3.


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
enum
{
  SSQ_USA_EAST = 0,
  SSQ_USA_WEST,
  SSQ_SOUTH_AMERICA,
  SSQ_EUROPE,
  SSQ_ASIA,
  SSQ_AUSTRALIA,
  SSQ_MIDDLE_EAST,
  SSQ_AFRICA,
  SSQ_WORLD = 255
};
Werte in enums, die nicht explizit festgelegt werden, haben immer einen um 1 erhöhten Wert als der vorher in der Liste stehende, also ist

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
    SSQ_USA_EAST = 0;
    SSQ_USA_WEST = 1;
    SSQ_SOUTH_AMERICA = 3;
    SSQ_EUROPE = 7;
    SSQ_ASIA = 15;
    SSQ_AUSTRALIA = 31;
    SSQ_MIDDLE_EAST = 63;
    SSQ_AFRICA = 127;
    SSQ_WORLD = 255;
falsch, richtig wäre 0, 1, 2, 3, ..., 7, 255 hat. Das gleiche gilt für die anderen enums, die als Konstanten übersetzt wurden (warum eigentlich nicht als Delphi-enum übersetzen?).

Bei

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
  SSQ_INFO_REPLY = record
    version: Char;
    hostname: Array[0..255of Char;
    map: Array[0..31of Char;
    game_directory: Array[0..31of Char;
    game_description: Array[0..255of Char;
    app_id: SmallInt;
    num_players: Char;
    max_players: Char;
    num_of_bots: Char;
    dedicated: Char;
    os: Char;
    password: Char;
    secure: Char;
    game_version: Array[0..31of Char;
  end;
lassen sich die Typen für version, app_id, num_players, max_players und num_of_bots wohl besser durch einen ShortInt übersetzen. Anstatt arrays of Char sind ShortStrings (hostname: String[256];) wahrscheinlich handlicher. Und für die restlichen Chars dürfte ein Byte oder ShortInt auch besser passen (eigentlich ByteBool, aber ich kenne die genaue Semantik dieses Typs in Delphi nicht mehr, möglicherweise entstehen unliebsame "-1 == True != True == 1"-Probleme).
Ähnliches gilt für die Records SSQ_PLAYER_ITEM und SSQ_PLAYER_REPLY. Weiterhin an allen Records das Schlüsselwort packed nicht vergessen. Die ganzen vars in den Funktionsdeklarationen haben da nichts zu suchen, im C-Header stehen da weder Pointer, noch nur in C++ existierende Referenzen, also nix var, sondern by-value!

Mehr fällt mir erstmal nicht auf.


BenBE - Di 09.09.08 14:54

user profile icontommie-lie hat folgendes geschrieben:
Bei

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
  SSQ_INFO_REPLY = record
    version: Char;
    hostname: Array[0..255of Char;
    map: Array[0..31of Char;
    game_directory: Array[0..31of Char;
    game_description: Array[0..255of Char;
    app_id: SmallInt;
    num_players: Char;
    max_players: Char;
    num_of_bots: Char;
    dedicated: Char;
    os: Char;
    password: Char;
    secure: Char;
    game_version: Array[0..31of Char;
  end;
lassen sich die Typen für version, app_id, num_players, max_players und num_of_bots wohl besser durch einen ShortInt übersetzen. Anstatt arrays of Char sind ShortStrings (hostname: String[256];) wahrscheinlich handlicher.

Aber falsch ;-) Bei Shortstrings in Delphi ist das erste Byte die Längenangabe, diese ist in char-Arrays bei C nicht vorhanden. Daher ist die Übersetzung mit Array of char schon korrekt. Kann in Delphi genauso wie ein normaler String behandelt werden (bis auf einige kleine Ausnahmen).

user profile icontommie-lie hat folgendes geschrieben:
Und für die restlichen Chars dürfte ein Byte oder ShortInt auch besser passen (eigentlich ByteBool, aber ich kenne die genaue Semantik dieses Typs in Delphi nicht mehr, möglicherweise entstehen unliebsame "-1 == True != True == 1"-Probleme).

Nur wenn man True = 1 ausgeht. Die Semantik in Delphi für ByteBool ist wie bei C: =0 --> false, !=0 --> True.

user profile icontommie-lie hat folgendes geschrieben:
Ähnliches gilt für die Records SSQ_PLAYER_ITEM und SSQ_PLAYER_REPLY. Weiterhin an allen Records das Schlüsselwort packed nicht vergessen. Die ganzen vars in den Funktionsdeklarationen haben da nichts zu suchen, im C-Header stehen da weder Pointer, noch nur in C++ existierende Referenzen, also nix var, sondern by-value!

Maximal const darf ergänzt werden ...


Borsty - Mi 10.09.08 11:06

Hmm


Quelltext
1:
typedef(WINAPI* SSQ_CALLBACK)(DWORD type,PSSQ_REPLY_UNION reply);                    

Hab mir die Doku mal angeguckt und da gibts als Rückgabewert einen BOOL also von daher denk ich mal:

Delphi-Quelltext
1:
SSQ_CALLBACK = function( typ: DWORD; reply: PSSQ_REPLY_UNION ):BOOL; StdCall;                    


Bzgl. der union hatte ich noch kein Glück.
Das mit den enums ist mir im nachhinein auch aufgefallen, trotzdem danke ^^
'var' in den Funktionsdefinitionen wurden durch 'const' ersetzt.