Universal long wie in C/C++

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

Universal long wie in C/C++

Beitrag von Mathias »

In C kann ich bei XtSetArg als 2. Parameter, verschiedene Bezeichner mitgeben, egal ob Pointer oder Integer. Das sieht dann etwa so aus:

Code: Alles auswählen

    Arg  wargs[10];
    XtSetArg(wargs[0], XtNwidth, 320);
    XtSetArg(wargs[1], XtNlabel, "Click = Quit");
    XtSetArg(wargs[2], XtNbwert_mit_bol, false);

    XtSetValues(command, wargs, 3);
Im C-Header sieht dies so aus:

Code: Alles auswählen

typedef char *String;
typedef intptr_t	XtIntPtr;  // intptr_t ist ein "long" (64Bit Integer)
typedef XtIntPtr	XtArgVal;

...
typedef struct {
    String	name;
    XtArgVal	value;
} Arg, *ArgList;
...
#define XtSetArg(arg, n, d) ((void)( (arg).name = (n), (arg).value = (XtArgVal)(d) ))
Wie ich dies ermittelt habe, ist "value" der springe Punkt, ein Allesfresser.

In Pascal habe ich folgende Lösung gemacht, welche auch funktioniert. Ich habe XtSetArg(...) einfach mit allen bis jetzt bekannten typen überladen.

Code: Alles auswählen

  TArg = record
    Name: TString;
    case byte of
      0: (valueP: PChar);
      1: (valueI: LongInt);
      2: (valueB: Boolean);
  end;
...
procedure XtSetArg(var arg: TArg; n: TXtString; pc: PChar); overload; inline;
procedure XtSetArg(var arg: TArg; n: TXtString; p: Pointer); overload; inline;
procedure XtSetArg(var arg: TArg; n: TXtString; int: PtrInt); overload; inline;
procedure XtSetArg(var arg: TArg; n: TXtString; bol: TBoolean); overload; inline;
...
procedure XtSetArg(var arg: TArg; n: TXtString; pc: PChar);
begin
  arg.Name := n;
  arg.valueP := pc;
end;
...
procedure XtSetArg(var arg: TArg; n: TXtString; bol: TBoolean);
begin
  arg.Name := n;
  arg.valueB := bol;
end;
Nun zur Frage, kann man dies irgendwie eleganter lösen, als für jeden Type eine Überlagerung zu schreiben ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
Jorg3000
Lazarusforum e. V.
Beiträge: 385
Registriert: So 10. Okt 2021, 10:24
OS, Lazarus, FPC: Win64
Wohnort: NRW

Re: Universal long wie in C/C++

Beitrag von Jorg3000 »

Hi!
Man könnte für Value den Typ Variant nehmen.

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

Re: Universal long wie in C/C++

Beitrag von Mathias »

Müsste man probieren. Nur Variant ist eine sehr komplexe Sache mit viel Overhead.
Und ob es da einen direkten Pointer auf value gibt und ob es auch dann der richte Wert ist, ist die 2. Frage.

Ich habe eher an so was gedacht, wie es bei der "array of const" möglich ist. Nur mit einer einzelnen const geht es nicht, es wird nicht mal kompiliert.

Code: Alles auswählen

procedure Test1(a:array of const); // geht
begin
end;

procedure Test2(c: const); // geht nicht
begin
end;
Ein Muster für "array of const" sieht man hier gut: MessageDlg(...);
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

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

Re: Universal long wie in C/C++

Beitrag von Warf »

Das in C funktioniert nur weil es ein Makro ist, und damit typlos ist. Ich würde die C lösung auch in keiner weise als Elegant bezeichnen (probier einfach mal was passiert wenn du XtSetArg(wargs[i++], ...) aufrufst).

Überladene Funktionen sind viel besser, und vermutlich wurde für die C Bibliothek das Makro verwendet weil es in C keine überladenen Funktionen gibt. Von daher ist so wie du es machst schon ok. Das gesagt, ich würde es sogar noch weiter umstellen und eine Funktion draus machen. Ich vermute mal das das C Makro keine "Funktion" ist, weil Struct Initialization Expressions erst ab C89/99 existieren, und um alte versionen zu unterstützen daher noch die Feldweise Initialisierung benutzt wurde.

Also schöner ist definitiv:

Code: Alles auswählen

function XTArg(Name: TXTString; Value: XXX): TArg;
Was du auch machen kannst ist du kannst den XtArgVal mit Impliziten Casts bauen:

Code: Alles auswählen

type TXtArgVal = record
  data: IntPtr;
end;

operator :=(const AString: PChar): TXtArgVal; inline;
begin
  Result.data := IntPtr(AString);
end;

operator :=(const APtr: Pointer): TXtArgVal; inline;
begin
  Result.data := IntPtr(AString);
end;

[...]

function XtArg(Name: TXtString; AValue: TXtArgVal): TArg; inline;
begin
  Result.Name := Name;
  Result.Value := AValue.data; // Oder einfach AVAlue übergeben
end;

//Nutzung
  arg := XtArg(argName, 42); // Automatische Konvertierung von IntPtr
  arg := XtArg(argName, @str); // Automatische Konvertierung von Pointer
  // ...
PS: Die vorschläge oben, egal ob Variant oder untyped const sind keine gute Idee, da es sich hierbei um Laufzeit Polymorphismus handelt, und damit massiv viel fehleranfälliger ist als Kompilezeitpolymorphismus, bzw überladene typcasts oder funktionen

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

Re: Universal long wie in C/C++

Beitrag von Mathias »

Das gesagt, ich würde es sogar noch weiter umstellen und eine Funktion draus machen.
Im Grund könnte ich noch weiter gehen, und mit TypenHelper arbeiten. Aber ich will möglichst kompatibel zu den C-Funktionen sein, so das man möglichst einfach C-Code übernehmen kann, ZB. von einem Tutorial.

Ich habe es probiert und sieht so aus:

Code: Alles auswählen

type
  TArgs = array of TArg;
  TArgsHelper = type helper for TArgs
    procedure Add(n: TXtString; i: ptrint);
    function GetPointer: PArg;
    function GetLength: SizeInt;
  end;
  
    procedure TArgsHelper.Add(n: TXtString; i: ptrint);
  var
    l: SizeInt;
  begin
    l := Length(Self);
    SetLength(self, l + 1);
    self[l].valueI := i;
    self[l].Name := n;
  end;

  function TArgsHelper.GetPointer: PArg;
  begin
    if Length(Self) = 0 then begin
      Result := nil;
    end else begin
      Result := @self[0];
    end;
  end;

  function TArgsHelper.GetLength: SizeInt;
  begin
    Result := Length(Self);
  end;

Code: Alles auswählen

var
  argdyn: TArgs = nil;
begin
  ...
    argdyn.Add(XtNwidth, 75);
    argdyn.Add(XtNheight, 25);
    argdyn.Add(XtNbackground, $FF8888);
    argdyn.Add(XtNforeground, $FFFFFFF);

    button3 := XtCreateManagedWidget('Buttton 3', commandWidgetClass, box, argdyn.GetPointer, argdyn.GetLength);
Mit den dynamischen Array und Strings von FPC ist man schon recht verwöhnt. Wen man bedenkt, das Zeiger-Gewürge es in C braucht.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

PascalDragon
Beiträge: 962
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Universal long wie in C/C++

Beitrag von PascalDragon »

Mathias hat geschrieben: Fr 7. Apr 2023, 17:51 Im C-Header sieht dies so aus:

Code: Alles auswählen

typedef intptr_t	XtIntPtr;  // intptr_t ist ein "long" (64Bit Integer)
Die Größe von long hängt von der verwendeten Platform ab. Unter 32-bit Systemen ist long eigentlich immer ebenfalls ein 32-bit Integer. Unter 64-bit Systemen hängt es davon ab, ob es LP64 oder LLP64 ist: bei ersteren (wie *nix-likes) ist es 64-bit, bei letzeren (wie Windows 64-bit) wiederum ist es trotzdem 32-bit (erst long long ist 64-bit).

Und der Sinn von intptr_t ist es einen int-artigen Typ zu haben, der einen Zeiger halten kann. Der richtige Typ für TArg.Value ist also PtrUInt (Randnotiz: unter Windows 64-bit ist intptr_t entsprechend als long long deklariert).

Deinen Code kannst du außerdem mit Generics vereinfachen:

Code: Alles auswählen

type
  TArg = record
    name: TString;
    value: PtrUInt;
  end;
  
generic procedure XtSetArg<T>(out arg: TArg; n: TXtString; v: T);

// implementation

generic procedure XtSetArg<T>(out arg: TArg; n: TXtString; v: T);
begin
  arg.Name := n;
  arg.Value := PtrUInt(v);
end;

// Nutzung:

begin
  specialize XtSetArg<LongInt>(arg1, 'Foobar', 42);
  specialize XtSetArg<PChar>(arg2, 'Blubb', 'Test');
end
(ich nehme out statt var, da die Eingabewerte von arg eh nicht übernommen werden).
FPC Compiler Entwickler

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

Re: Universal long wie in C/C++

Beitrag von Mathias »

(ich nehme out statt var, da die Eingabewerte von arg eh nicht übernommen werden).
Dies macht Sinn, habe da gar nicht daran gedacht.
Deinen Code kannst du außerdem mit Generics vereinfachen:
In der Unit wird es kompakter, aber im Hauptprogramm wird es einiger komplizierter.

Ich sehe schon, am besten wird es mit den überlagerten Proceduren sein.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

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

Re: Universal long wie in C/C++

Beitrag von Mathias »

Ich habe wieder so was lustiges gefunden.
Wie kann ich dies in Pascal umsetzen ?

Code: Alles auswählen

#define XtNumber(arr)		((Cardinal) (sizeof(arr) / sizeof(arr[0])))
Im Grunde ist es nichts anderes als "Length(...)".

So hätte ich es gewünscht, aber dies geht leider nicht.

Code: Alles auswählen

function XTNumber(a:array): SizeInt;
begin
  Result := Length(a);
end; 
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6848
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Universal long wie in C/C++

Beitrag von af0815 »

ist das nicht Result:= High(a)-Low(a);
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2822
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: Universal long wie in C/C++

Beitrag von m.fuchs »

Mathias hat geschrieben: Di 11. Apr 2023, 14:01 Ich habe wieder so was lustiges gefunden.
Wie kann ich dies in Pascal umsetzen ?

Code: Alles auswählen

#define XtNumber(arr)		((Cardinal) (sizeof(arr) / sizeof(arr[0])))
Im Grunde ist es nichts anderes als "Length(...)".
Wenn du schon weißt, dass es im Grunde ein Length ist - warum benutzt du dann kein Length?
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

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

Re: Universal long wie in C/C++

Beitrag von Warf »

Wie bereits schon oben erwähnt, C makros sind keine Funktionen, und es gibt (zum guten wie zum schlechten) nicht für alles was man mit C Makros Bauen ein Pascal Equivalent.

Das gesagt, da im grunde hier Length genau das macht was du willst, kannst du einfach ein Makro benutzen das XtNumber auf Length mapped:

Code: Alles auswählen

{$Macro On}
{$Define XtNumber:=Length}

var
  arr: Array[0..9] of Integer;
begin
  WriteLn(XtNumber(arr));
Doch ob du damit Glücklich wirst, sei mal dahin gestellt

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

Re: Universal long wie in C/C++

Beitrag von Mathias »

Doch ob du damit Glücklich wirst, sei mal dahin gestellt
Glücklich schon, da es kompatibel zur C-Lib ist, aber es wird wohl kaum funktionieren, da mir mal gesagt wurde, das Macros nicht Unit-Übergreifend sind.

So nebenbei, ist dies nicht ein gefährlicher Macro ?
Was passiert mit arr[0], wen die Array 0 Elemente hat ?

Code: Alles auswählen

#define XtNumber(arr)		((Cardinal) (sizeof(arr) / sizeof(arr[0])))
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Universal long wie in C/C++

Beitrag von Socke »

Mathias hat geschrieben: Di 11. Apr 2023, 18:11 Glücklich schon, da es kompatibel zur C-Lib ist, aber es wird wohl kaum funktionieren, da mir mal gesagt wurde, das Macros nicht Unit-Übergreifend sind.
Du kannst ja auch eine Inline-Funktion definieren:

Code: Alles auswählen

function XtNumber(const myArray: TMyArrayType): SizeInt; inline;
  Result:=Length(myArray);
end;
Ist hier natürlich nur auf einen Datentypen beschränkt.
Mathias hat geschrieben: Di 11. Apr 2023, 18:11 So nebenbei, ist dies nicht ein gefährlicher Macro ?
Was passiert mit arr[0], wen die Array 0 Elemente hat ?

Code: Alles auswählen

#define XtNumber(arr)		((Cardinal) (sizeof(arr) / sizeof(arr[0])))
Das kommt ein wenig darauf an, was "arr" genau ist. Bezogen auf die Pascal-Syntax:
Ist arr ein statischer Array, wird er nie 0 Elemente haben. Bei einem dynamsichen Array ergibt sizeof(arr) ohnehin die Größe des Zeigers auf die Array-Daten - hier ergibt die Berechnung keinen Sinn (Größe des Zeigers geteilt durch die Größe eines Array Elements ergibt was?).

Auf der anderen Seite ist sizeof() keine Funktion sondern ein Compiler-Intrinsic, was bereits durch den Compiler-Aufgelöst wird. Im Code steht da nur noch der tatsächliche Wert.
Damit ist das Macro nicht unsicher im Sinne "der Zugriff auf arr[0]" bei einem nil-Pointer sondern es rechnet dir einfach Mist aus, wenn du etwas hineingibst, was syntaktisch in Ordnung aber semantischer Müll ist.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

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

Re: Universal long wie in C/C++

Beitrag von Warf »

Mathias hat geschrieben: Di 11. Apr 2023, 18:11 So nebenbei, ist dies nicht ein gefährlicher Macro ?
Was passiert mit arr[0], wen die Array 0 Elemente hat ?

Code: Alles auswählen

#define XtNumber(arr)		((Cardinal) (sizeof(arr) / sizeof(arr[0])))
Nein, SizeOf wird immer zur Kompilezeit ausgewertet, und kann daher keine Laufzeitfehler werfen. SizeOf(arr[0]) ist damit so zu sagen ein "Gib mir die größe die das element hätte wenn es da wäre".

Einzige ausnahme sind VLAs (variable length arrays), praktisch das C äquivalent zu Pascals dynamischen Arrays (nur anders) und da ist SizeOf(arr) das selbe wie length. Funktioniert also solang das element von arr kein VLA ist, denn dann muss es zur laufzeit ausgewertet werden. Aber ich weis grade ad-hoc nicht mal ob arrays von VLAs möglich sind (VLAs sind neumodischer kram dens erst seit 25 jahren in C gibt)

Antworten