PChar/String erzwingen

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

PChar/String erzwingen

Beitrag von Mathias »

Wie kann ich erzwingen, das FPC 'x' auch als PChar nimmt ?
So wie es jetzt ist, wird 'x' als einfacher Char angenommen.

Code: Alles auswählen

const
  XtNwindow = 'window';
  XtNx = 'x';
Das Problem ist, das diese Parameter via "varargs" einer lib übergeben werden, und die braucht dringen einen PChar.

Code: Alles auswählen

const
  XtNx = PChar('x');
wird als "illegal expression" quittiert.

Dies wird kompiliert:

Code: Alles auswählen

const
  XtNx : PChar ='x';
Aber im späteren Ablauf wird es wieder mit "illegal expression" quittiert.

Code: Alles auswählen

const
  XmNx = XtNx;
Einer eine Idee, wie ich es machen kann, das bei einem einzelnen Zeichen auch ein PChar/String angenommen wird ?
Oder muss man dies wie über einen Umweg lösen ?

Im Prinzip müsste sich in meinem Fall "x" gleich verhalten wie "window".


Nachtrag:
Im späteren Verlauf würde dies gehen, aber das ist nicht Sinn der Sache:

Code: Alles auswählen

    XtVaSetValues(children[0],
      PChar(XmNx), 20, PChar(XmNy), 50,
      nil);
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: PChar/String erzwingen

Beitrag von Jorg3000 »

Moin!
PChar ist nur ein Pointer, d.h. er kümmert sich nicht um die Speicherreservierung für den Inhalt. Das macht ein String.
Was spricht dagegen, XtNx/XmNx als String zu lassen und es mittels PChar(XmNx) an die Lib zu übergeben?
Dabei findet keine Konvertierung des Strings statt, d.h. es ist kein Zeitverlust.

Und wenn du von der Lib einen neuen Pointer zurückbekommst, hat die Lib den Speicher dafür reserviert.
Dann muss man es zur eigenen Verwendung in einen String umkopieren, ich glaube mittels s:=String(einPChar);
Grüße, Jörg

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

Re: PChar/String erzwingen

Beitrag von Mathias »

Das Problem ist, die Xmn... Konstanten wurden von eine C-lib übersetzt. Und diese Konstanten werden später als Property verwendet.
Bei C funktioniert die natürlich wunderbar, da

Code: Alles auswählen

'x' != "x"
ist.
Aber nach mehreren probieren, bin ich auf folgende Lösung gestossen.

Code: Alles auswählen

const
  XtNx : PChar ='x' + '';
Jetzt nimmt Pascal die Constante auch als Zeichenkette an und nicht nur als einzelnes Zeichen.

Bei GTK war ich auch mal darüber gestolpert.

Code: Alles auswählen

    gtk_tree_store_set(treestore, @child, COLUMN, 'ABC', -1);        // geht, weil Zeichenkette     
    
    gtk_tree_store_set(treestore, @child, COLUMN, 'C', -1);          // SIGSEGV, weil einzelnes Zeichen     
    gtk_tree_store_set(treestore, @child, COLUMN, PChar('C'), -1);   // geht 
    gtk_tree_store_set(treestore, @child, COLUMN, 'C'+'', -1);       // neue Lösung
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Joh
Lazarusforum e. V.
Beiträge: 288
Registriert: Sa 26. Mai 2012, 17:31
OS, Lazarus, FPC: Win 10 (L 2.2.6 x64 FPC 3.2.2)
CPU-Target: 64Bit

Re: PChar/String erzwingen

Beitrag von Joh »

Mathias hat geschrieben: Di 18. Apr 2023, 08:40 Aber nach mehreren probieren, bin ich auf folgende Lösung gestossen.

Code: Alles auswählen

const
  XtNx : PChar ='x' + '';
Das sieht so bescheuert unlogisch aus, das kann nur funktionieren
just my two Beer

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

Re: PChar/String erzwingen

Beitrag von Jorg3000 »

Übrigens, bei mir wird

Code: Alles auswählen

const XtNx: PChar = 'x';
ohne Fehler kompiliert und funktioniert. Mit Compiler-Option {$mode objfpc}{$H+}

Anstelle von ='x'+'' sollte auch einfach =String('x') gehen.

Ein PChar muss/sollte auf einen Null-terminierten String zeigen, weil sonst das Ende bzw. die Länge der Zeichenkette nicht erkennbar ist.
Deshalb muss die Konstante für PChar intern immer ein String sein, kein einzelnes Char.
Bei der PChar-Konstante wird auf einen Null-terminierten String im kompilierten Programmcode zugegriffen.

Benutzeravatar
six1
Beiträge: 837
Registriert: Do 1. Jul 2010, 19:01

Re: PChar/String erzwingen

Beitrag von six1 »

demnach wäre es auch nicht XtNx : PChar ='x' + '';
sondern XtNx : PChar ='x' + chr(0);
Gruß, Michael

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

Re: PChar/String erzwingen

Beitrag von Mathias »

So funktioniert es:

Code: Alles auswählen

const
  XtNx = 'x' + '';
  XmNx = XtNx;  
Da ich versuche 2 Konstanten zusammen zu fügen, nimmt der Compiler an, das XtNx eine Zeichenkette ist, auch wen es nur aus einem Zeichen besteht.
Das #0 nicht gleich wie '' ist, beweist folgender Code:

Code: Alles auswählen

  WriteLn('' = #0);          // -> FALSE
  WriteLn(Length(''));       // -> 0
  WriteLn(Length(#0));       // -> 1
  WriteLn(Length(#0#0));     // -> 2
  WriteLn(Length('c' + '')); // -> 1
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: PChar/String erzwingen

Beitrag von Jorg3000 »

Mit einem nullterminierten String meinte ich nicht, dass man selber eine #0 anhängen muss.
Sobald der Compiler einen String erkennt (z.B. durch die String-Addition/Verkettung), legt er im Modus {$H+} einen langen String (AnsiString) an, der im Speicher automatisch immer nullterminiert ist.
Darum braucht man sich bekanntlich nicht durch das Anhängen einer #0 selber zu kümmern. Es geht nur darum, dass der Compiler zwischen einem einzelnen Char und einem String unterscheidet.
Und wenn man einer DLL/Lib ein PChar übergibt, muss die Zeichenkette nullterminiert sein, was bei PChar(einAnsiString) in Delphi/FP immer automatisch gegeben ist.

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

Re: PChar/String erzwingen

Beitrag von Mathias »

Darum braucht man sich bekanntlich nicht durch das Anhängen einer #0 selber zu kümmern.
Dies habe ich nicht gewusst, das dies bei String automatisch geht.
Somit müsste man einen String ohne Probleme einer C-Lib übergeben können ?
Dann darf man ohne schlechtes Gewissen folgendes machen ?

Code: Alles auswählen

CFunc(@s[1]);
Es geht nur darum, dass der Compiler zwischen einem einzelnen Char und einem String unterscheidet.
Genau dies war mein Problem.
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: PChar/String erzwingen

Beitrag von Jorg3000 »

@s[1] geht auch!
Der Vorteil von PChar(s) ist, dass es NIL zurückliefert, falls s leer ist.
Hingegen führt @s[1] bei einem leeren String zu einer Exception (was beim Debuggen auch ein Vorteil sein kann).
Ansonsten ist PChar(s) das Gleiche wie @s[1].

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

Re: PChar/String erzwingen

Beitrag von Mathias »

Hingegen führt @s[1] bei einem leeren String zu einer Exception
Wieso jetzt dies ?
Wen Pascal angeblich bei String immer ein #0 hinten anhängt, müsste dies doch funktionieren da s[1]=#0 ist.
Oder habe ich etwas übersehen ?
Anders bei einem PChar, der kann auch nil sein.

Ich habe folgendes probiert, anscheinend ist bei Länge 0 doch etwas anders, als wen mindestens ein Zeihen im String ist.

Code: Alles auswählen

var
  s: string;
begin
  SetLength(s, 1);
  WriteLn(PtrUInt(s[2]));  // geht -> 0
  WriteLn('io');
  
  SetLength(s, 4);
  WriteLn(PtrUInt(s[5]));  // geht -> 0
  WriteLn('io');

  SetLength(s, 0);
  WriteLn(PtrUInt(s[1])); // Knall
  WriteLn('knall');
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
theo
Beiträge: 10921
Registriert: Mo 11. Sep 2006, 19:01

Re: PChar/String erzwingen

Beitrag von theo »

Mathias hat geschrieben: Mi 19. Apr 2023, 15:02 // geht -> 0
Nein, das geht nicht. Schalte mal Range Checking ein, dann knallt's.

Und nur weil ein String intern irgendwelche Daten hält, heisst das nicht, dass du wie bei einem Buchstaben darauf zugreifen kannst.

https://wiki.freepascal.org/Character_a ... g_types/de

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

Re: PChar/String erzwingen

Beitrag von Warf »

Mathias hat geschrieben: Mi 19. Apr 2023, 15:02
Hingegen führt @s[1] bei einem leeren String zu einer Exception
Wieso jetzt dies ?
Wen Pascal angeblich bei String immer ein #0 hinten anhängt, müsste dies doch funktionieren da s[1]=#0 ist.
Oder habe ich etwas übersehen ?
Anders bei einem PChar, der kann auch nil sein.

Ich habe folgendes probiert, anscheinend ist bei Länge 0 doch etwas anders, als wen mindestens ein Zeihen im String ist.
Weils so dokumentiert ist: https://www.freepascal.org/docs-html/re ... 360003.2.4
If the string is empty (’’), then the internal pointer representation of the string pointer is Nil. If the string is not empty, then the pointer points to a structure in heap memory.

The internal representation as a pointer, and the automatic null-termination make it possible to typecast an ansistring to a pchar. If the string is empty (so the pointer is Nil) then the compiler makes sure that the typecast pchar will point to a null byte.
Du darfst also nicht s[1] benutzen, sondern musst PChar(s)[0] (null indiziert weil array zugriff auf pointer und kein string zugriff) benutzen.

Generell ist die konvertierung zwischen Pascal Strings und C Strings (PChar) eines dieser sachen die zwar extrem nützlich sind, aber durch so viele spezialfälle und alles drum herum eigentlich extrem unschön (und an manchen stellen nicht ganz koherent) ist.

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

Re: PChar/String erzwingen

Beitrag von Mathias »

Generell ist die konvertierung zwischen Pascal Strings und C Strings (PChar) eines dieser sachen die zwar extrem nützlich sind, aber durch so viele spezialfälle und alles drum herum eigentlich extrem unschön (und an manchen stellen nicht ganz koherent) ist.
Dies ist leider so. Als Pascal-Programmierer ist man sehr verwöhnt, vor allem was Ansi-Strings und dynamische Array anbelangt. Da kann man einfügen, löschen, grösse ändern, ohne das man sich das Gedanken machen muss, was das eigentlich passiert.
Bei C sind solche Sachen immer sehr aufwändig. In C geht nicht einfach ein.

Code: Alles auswählen

PChar3 := PChar2 + PChar1;

Bei einem Pascal-String kann man dies gedankenlos machen.
Will man eine C-Lib ansprechen, kommt man kaum um die Pxxx-Array rum.
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: PChar/String erzwingen

Beitrag von PascalDragon »

Mathias hat geschrieben: Mi 19. Apr 2023, 15:02
Hingegen führt @s[1] bei einem leeren String zu einer Exception
Wieso jetzt dies ?
Wen Pascal angeblich bei String immer ein #0 hinten anhängt, müsste dies doch funktionieren da s[1]=#0 ist.
Ein leerer String ist immer Nil.
FPC Compiler Entwickler

Antworten