Leere Records für C/C++ libs.

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Mathias
Beiträge: 6164
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Leere Records für C/C++ libs.

Beitrag von Mathias »

In der Unit "xlib" bin ich auf leere Records gestossen. ZB. diesen:

Code: Alles auswählen

   PXDisplay = ^TXDisplay;
   TXDisplay = record
     end;
Da habe ich mal folgenden Test gemacht:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
type
  tr = record
  end;
  pr = ^tr;
var
  r: tr;
  p: pr;
begin
  WriteLn(SizeOf(r));
  WriteLn(int64(@r));
  WriteLn(SizeOf(p));
  WriteLn(int64(@p));
end;
Die Variable "r" ist wie erwartet 0 Byte gross. Und im Speicher ist sie vorhanden @r gibt einen Integerwert aus. Der Pointer "p" ist wie erwartet 8 Byte.
Was ist der Sinn hinter solchen Records ?
Ich kann mir dies höchstens als Alternative für einen Pointer sehen.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Mathias
Beiträge: 6164
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Leere Records für C/C++ libs.

Beitrag von Mathias »

Was ich gerade bei der xlib Funktionen festgestellt habe.
Wen ich folgendes

Code: Alles auswählen

var
  dis: PDisplay;
durch

Code: Alles auswählen

var
  dis: Pointer;
ersetze, wird anstandslos kompiliert und das Programm läuft sogar.

Und hier wird "dis" verwendet:

Code: Alles auswählen

  dis := XOpenDisplay(nil);
  if dis = nil then begin
    WriteLn('Kann nicht das Display öffnen');
    Halt(1);
  end;
  scr := DefaultScreen(dis);
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2636
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Leere Records für C/C++ libs.

Beitrag von m.fuchs »

Mathias hat geschrieben:
Di 8. Nov 2022, 13:35
Was ist der Sinn hinter solchen Records ?
Ich kann mir dies höchstens als Alternative für einen Pointer sehen.
Das wird einfach die Übersetzung aus der C-Library sein. Mache ich auch immer so.

Code: Alles auswählen

typedef struct VoskModel VoskModel;

/* ... */

VoskModel *vosk_model_new(const char *model_path);
Übersetzt nach Pascal wäre das dann:

Code: Alles auswählen

  TVoskModelRecord = record
  end;
  PVoskModel = ^TVoskModelRecord; 
  
  (* ... *)
  function vosk_model_new(const model_path: PChar): PVoskModel; external LIBNAME;
Klar kann man auch einfach einen Pointer nehmen, aber so bleibt ungefähr der Aufbau der Original-Header-Datei erhalten und man ist sauberer als beim Nutzen von Pointer als Typ.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Leere Records für C/C++ libs.

Beitrag von Warf »

Der Grund dafür ist ganz einfach typsicherheit. Beispiel, sagen wir mal ich habe eine C bibliothek die 2 structs benutzt, deren interne struktur mich aber in Pascal nicht interessiert da ich einfach nur den pointer davon bekomme und benutze:

Code: Alles auswählen

function GetStructA: Pointer; external 'MeineCBibliothek';
function GetStructB: Pointer; external 'MeineCBibliothek';
procedure DoSomethingWithStructA(StructA: Pointer); external 'MeineCBibliothek';
procedure DoSomethingWithStructB(StructB: Pointer); external 'MeineCBibliothek';
...
Das würde so kompilieren und funktionieren. Allerdings könnte ich dann das machen:

Code: Alles auswählen

DoSomethingWithStructB(GetStructA);
Das würde der Compiler Zulassen da DoSomethingWithStructB einen Pointer erwartet und GetStructA einen Pointer liefert. Allerdings würde das dann zu Fehlern in C führen, weil der eine StructB erwartet aber eine StructA bekommt.

Macht man das jetzt mit Dummy Typen sieht das so aus:

Code: Alles auswählen

type
  PStructA = ^TStructA;
  TStructA = record end;
  PStructB = ^TStructB;
  TStructB = record end;

function GetStructA: PStructA; external 'MeineCBibliothek';
function GetStructB: PStructB; external 'MeineCBibliothek';
procedure DoSomethingWithStructA(StructA: PStructA); external 'MeineCBibliothek';
procedure DoSomethingWithStructB(StructB: PStructB); external 'MeineCBibliothek';
Dann wirft der Code von obem

Code: Alles auswählen

DoSomethingWithStructB(GetStructA);
Den folgenden Fehler

Code: Alles auswählen

project1.lpr(23,36) Error: Incompatible type for arg no. 1: Got "PStructA", expected "PStructB"
Somit macht das zwar semantisch keinen unterschied, allerdings erlaubt es dem Compiler Fehler zu finden die sonst eventuell untergehen würden.

Dummy typen sind generell ein sehr mächtiges Werkzeug um dem Compiler die Möglichkeit zu geben Korrektheit zu checken, wollte dazu seit längerem schonmal was zu schreiben, denn das ist auch in reinem Pascal Code recht nützlich

Mathias
Beiträge: 6164
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Leere Records für C/C++ libs.

Beitrag von Mathias »

Das wird einfach die Übersetzung aus der C-Library sein. Mache ich auch immer so.
Dies macht Sinn.
Das mit der Fehlermeldung kann ich nachvollziehen und macht auch Sinn um Fehler zu vermeiden.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Antworten