Hallo zusammen,
ich möchte gerne eine kleine
API entwickeln und möchte gerne alles richtig und sauber machen und frage daher hier um Hinweise. Wert legen möchte ich auch auf gute Verträglichkeit mit C/C++ (z.B. Nutzung von C-Strings anstelle von PASCAL-Strings).
Ich habe hierfür 3 Fragen:
1) Redundanter Code bei den API "Headers"
Bei C ist es üblich, Header (*.h) und Code (*.c) in zwei Dateien zu trennen. In Delphi hat man hierfür 2 Abschnitte in einer einzigen
PAS Datei. Ich habe gemerkt, dass durch diese Praxis große Teile des Codes redundant sind und ich diese immer synchron halten muss.
Als Beispiel erstelle ich eine
DLL "MyDLL" mit einer Funktion "MyFunction", die ein record/struct "TMyType" zurückliefert. Ich muss den jenigen, die meine
DLL nutzen wollen, die Deklaration von "TMyType" zur Verfügung stellen. Problematisch ist, dass ich bei der Entwicklung der
DLL diesen TMyType definieren muss, allerdings auch bei der Nutzung.
- Entwicklung der DLL -
Datei MyDLL.dpr:
Delphi-Quellcode:
library MyDLL;
uses
MyAPI1 in 'MyAPI1.pas';
function MyFunction: TMyType; cdecl;
begin
// ...
end;
exports
MyFunction;
end.
Datei MyAPI1.pas:
Delphi-Quellcode:
unit MyAPI1;
interface
type
// REDUNDANTER CODE :-(
TMyType =
record
zahl: Cardinal;
string: PAnsiChar;
end
implementation
end.
- Nutzung der DLL -
Datei MyProgram.dpr:
Delphi-Quellcode:
program MyProgram;
uses
MyAPI2
in '
MyAPI2.pas';
begin
MyFunction;
// auf die DLL wird zugegriffen
end.
Datei MyAPI2.pas:
Delphi-Quellcode:
unit MyAPI2;
interface
type
// REDUNDANTER CODE :-(
TMyType =
record
zahl: Cardinal;
string: PAnsiChar;
end
{$EXTERNALSYM MyFunction}
function MyFunction: TMyType;
cdecl;
implementation
function MyFunction;
external '
MyDll.dll'
name '
MyFunction';
end.
Hier wird deutlich, dass MyAPI1.pas und MyAPI2.pas die SELBEN Inhalte haben, mit Unterschied dass MyAPI2.pas (zur Benutzung der
DLL) zusätzlich noch die Importe zur Verfügung stellt.
Wie kann ich verhindern, dass MyAPI1.pas ("Entwickler-Deklarationen") und MyAPI2.pas ("Benutzer-Deklarationen") so extreme Coderedundanzen aufweisen? (Ich wünschte, Delphi hätte so eine interface/implementation Trennung wie in C...)
(FYI: Ich entwickle die
API gleichzeitig zu einer Client-Applikation, die diese
DLL/
API nutzt. Daher habe ich das unmittelbar das Problem, MyAPI1.pas und MyAPI2.pas ständig per Copy-Paste synchron zu halten, was keine gute Programmierpraxis sein kann...)
2) C++ Interoperatibilität
Sollte ich, wenn ich einfache Handhabung/Kompatibilität mit C/C++ wünsche, "cdecl" oder "stdcall" nutzen?
Sollte ich "packed record" oder "record" nutzen, damit es mit "struct" am besten Kompatibel ist?
3) Versionsinfo: Sprachneutralität
Ich nutze Turbo Delphi (da ich OpenSource entwickle) und habe festgestellt, dass jede
DLL mit der Locality "DE-DE" erstellt wird. Es scheint keine Möglichkeit zu geben, die VersionsInfo auf "Sprachneutral" (VALUE "Translation", 0x0000 0x04E4) anstelle "Deutsch (Deutschland)" (VALUE "Translation", 0x0407 0x04E4) umzustellen. Sobald ich die *.res in einem ResourceEditor auf 0000/04E4 ändere, erkennt Delphi die Versionsinformation nicht mehr an. Es wäre schon schön, wenn die
DLL als Sprachneutral dargestellt würde, denn sie stellt in keinster Form eine
GUI oder Dialoge zur Verfügung.
Ich freue mich über Antworten und Hinweise um die Best-Practise für
API-Entwicklung unter Delphi zu finden.
Gruß
Daniel Marschall