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
tommie-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
tommie-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
BenBE 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: |
tommie-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
BenBE 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..255] of Char; map: Array[0..31] of Char; game_directory: Array[0..31] of Char; game_description: Array[0..255] of 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..31] of 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
tommie-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..255] of Char; map: Array[0..31] of Char; game_directory: Array[0..31] of Char; game_description: Array[0..255] of 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..31] of 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).
tommie-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.
tommie-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.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!