Gemeine Falle mit C Boolean

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

Gemeine Falle mit C Boolean

Beitrag von Mathias »

Ich bin gerade über eine Falle gestolpert. Bei libs welche auf C-Bibliotheken zugreifen, werden vielfache eigene Booleans definiert. Die gl Unit ist ein gutes Beispiel dafür.
Das not funktioniert nicht richtig mit denen.

Code: Alles auswählen

uses
  gl;

begin
  WriteLn(GL_TRUE);  // -> 1
  WriteLn(GL_FALSE); // ->01
  WriteLn();
  WriteLn(not GL_TRUE);  // -> -2 ??
  WriteLn(not GL_FALSE); // -> -1 ??
  WriteLn();
  WriteLn(not not GL_TRUE); // -> 1
  WriteLn(not not GL_FALSE); // -> 0 
  WriteLn();
  WriteLn(not 1);  // -> -2 ??
  WriteLn(not 0); // -> -1 ??
 
Der C-Compiler implementiert sowas richtig.

Code: Alles auswählen

   SDL_Log("%i", SDL_FALSE ); // -> 0
   SDL_Log("%i", SDL_TRUE ); // -> 1
   SDL_Log("%i", !SDL_FALSE ); // -> 1
   SDL_Log("%i", !SDL_TRUE ); // -> 0
   SDL_Log("%i", !0 ); // -> 1
   SDL_Log("%i", !1 ); // -> 0
Kann man die bei FPC auch irgendwie erzwingen, das es richtig geht ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Stevie
Beiträge: 173
Registriert: Di 27. Feb 2024, 22:40

Re: Gemeine Falle mit C Boolean

Beitrag von Stevie »

Ich glaube, Du tust dem armen Pascal unrecht.

Im Gegensatz zu C behandelt es eine Zahl eben als eine Zahl und nicht mal als Zahl und mal als logischen Wert, je nachdem, was gerade passend scheint.

Ganz konkret habe ich mir mal gl.pp angeschaut und da sind GL_TRUE und GL_FALSE definiert als...

Code: Alles auswählen

const
 GL_TRUE                           = 1;
 GL_FALSE                          = 0;
... sprich, es handelt sich um Zahlen.

Wenn man nun die Bits der Zahl 1 (der Einfachheit halber hier mal als 8-bit: 00000001/binär) negiert, dann bekommt man als 8-Bit Zahl 11111110/binär. Und im Zweierkomplement, in dem negative Zahlen repräsentiert werden, ist das genau 00000010/binär, d.h. die -2, die Du im "writeln(not GL_TRUE)" gesehen hast. Das lässt sich auf https://www.allmath.com/de/zweierkomplement.php ganz einfach nachvollziehen.

Wenn man das konkrete Problem hier umgehen und 0 bzw. 1 typsicher interprertiert wissen will, muss man sich in Pascal eine Helperfunktion schreiben, die GL_TRUE in 'true' übersetzt und GL_FALSE in 'false'.

In diesem Beispiel mag die opportunistische Art von C, Zahlen zu interpretieren, mal in Deinem Sinne wirken. Deutlich öfter schießt man sich damit aber auch den Fuß weg.

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

Re: Gemeine Falle mit C Boolean

Beitrag von Mathias »

Ganz konkret habe ich mir mal gl.pp angeschaut und da sind GL_TRUE und GL_FALSE definiert als...
Genau dort sind sie wie in vielen C-Libs mit 0 und 1 definiert.

Jetzt habe ich gerade noch was dümmeres entdeckt. In der veralteten unit SDL welche bei FPC dabei ist.

Code: Alles auswählen

type
  SDL_Bool  = (SDL_FALSE, SDL_TRUE);
  TSDL_Bool = SDL_Bool;   
Dort ist not wie erwartet gar nicht möglich.

Nach meiner Meinung ist der normale FPC Boolean als Basis die beste Lösung.

Code: Alles auswählen

type
  TGLBoolean = Boolean;
const  
  GL_TRUE = True;
  GL_FALSE = False;
Natürlich muss man dann auch die externen Funktionen richtig deklarieren.

Code: Alles auswählen

procedure glDepthMask(flag: TGLboolean); external;
Die geht natürlich alles nur, wen man selbst C-Header übersetzt, bei bestehenden in der FPC-Package ist dies schwierig.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1629
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: Gemeine Falle mit C Boolean

Beitrag von corpsman »

Ich nutze ja immer die Unit "ctypes", da gibt es dann einen CBool, dann muss man sich nichts selbst ausdenken ...
--
Just try it

Stevie
Beiträge: 173
Registriert: Di 27. Feb 2024, 22:40

Re: Gemeine Falle mit C Boolean

Beitrag von Stevie »

Ich glaube, das bringt Mathias nicht weiter. Denn während CBool zwar tatsächlich auf Long Integer basieren und damit deutlich mehr wie unter C realisiert sind, bekommt man GL_FALSE, GL_TRUE usw. damit leider auch nicht kompatibel zum intuitiven Verständnis von Logikwerten und scheitert dieses mal schon beim Übersetzen an der Typstrenge von Pascal:

Code: Alles auswählen

program BoolTest;

uses
 ctypes, gl;

var
  myBool : CBool;

begin
  myBool := GL_FALSE;
  writeln(myBool);
  writeln(not myBool);
end.
... scheitert mit:

Code: Alles auswählen

$ fpc booltest.pas
Free Pascal Compiler version 3.2.0 [2020/06/27] for x86_64
Copyright (c) 1993-2020 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling booltest.pas
booltest.pas(10,13) Error: Incompatible types: got "ShortInt" expected "LongBool"
booltest.pas(14) Fatal: There were 1 errors compiling module, stopping
Fatal: Compilation aborted
Error: /usr/bin/ppcx64 returned an error exitcode
$

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

Re: Gemeine Falle mit C Boolean

Beitrag von Mathias »

corpsman hat geschrieben: So 14. Apr 2024, 12:24 Ich nutze ja immer die Unit "ctypes", da gibt es dann einen CBool, dann muss man sich nichts selbst ausdenken ...
Auch da kann man stolpern.

Der "cbool" funktioniert hier gut, wen es ein echter "bool "ist. Und der ist 32bit.

Code: Alles auswählen

extern void test( bool b);
Hier könnte es Probleme geben, da er nur 8Bit ist.

Code: Alles auswählen

typedef unsigned char	GLboolean;
extern void test( GLboolean b);
Ich vermute das dies auch mit dem "cbool" gut gehen kann, aber sobald ein record oder ein Array im Spiel ist, könnte die Bit-Breite Probleme machen.

Daher verwende ich je nach dem was gebraucht wird.
boolean8, boolean16, boolean32.


Bei war der Auslöser für das Problem, eine SDL3 Funktion, welche einen 32bit Integer als Boolean braucht. Und blöderweise frisst diese Funtkion nur 0 oder 1, alle anderen Werte lösen einen SIGSEV aus. Also hat e bei mir geknallt, als ich not "0" macht, weil dies "-1" ist.
Ich vermute das es ein Bug in der SDL3-Funktion ist.
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: Gemeine Falle mit C Boolean

Beitrag von PascalDragon »

Mathias hat geschrieben: So 14. Apr 2024, 13:23 Daher verwende ich je nach dem was gebraucht wird.
boolean8, boolean16, boolean32.
Wenn du mit C interagierst ist es meist sinnvoller die Typen ByteBool, WordBool, LongBool oder QWordBool (je nach nötiger Bitbreite), zu verwenden. Da selbst wenn SDL_TRUE als 1 deklariert ist, wird in C durch die Verwendung von ! einfach nur auf gleich 0 bzw. ungleich 0 abgeprüft. Für die genannten Typen gilt das Gleiche: sie sind False wenn sie 0 sind und True bei allen anderen Werten. Im Gegensatz dazu sind die von dir genannten Typen False für 0 und nur True für den Wert 1, alle anderen Werte sind entsprechend undefiniert.
Mathias hat geschrieben: So 14. Apr 2024, 13:23 Bei war der Auslöser für das Problem, eine SDL3 Funktion, welche einen 32bit Integer als Boolean braucht. Und blöderweise frisst diese Funtkion nur 0 oder 1, alle anderen Werte lösen einen SIGSEV aus. Also hat e bei mir geknallt, als ich not "0" macht, weil dies "-1" ist.
Ich vermute das es ein Bug in der SDL3-Funktion ist.
Wahrscheinlich verwendet die irgendein Array mit zwei Elementen je nach Wert des Parameters. Ist natürlich recht dumm, wenn man keine ordentliche Validierung der Parameter macht... In dem Fall wäre womöglich tatsächlich die Nutzung der BooleanX-Typen richtiger... :roll:
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: Gemeine Falle mit C Boolean

Beitrag von Mathias »

Wahrscheinlich verwendet die irgendein Array mit zwei Elementen je nach Wert des Parameters. Ist natürlich recht dumm, wenn man keine ordentliche Validierung der Parameter macht... In dem Fall wäre womöglich tatsächlich die Nutzung der BooleanX-Typen richtiger... :roll:
Ich vermute ich habe den Fehler gesehen.
In dieser Routine werden 2 Booleans verglichen, welche als int deklariert sind.
Obwohl der Eingabe bool True ist und der der vergleichswert dies auch ist, sind sie trozdem unterschiedlich.
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: Gemeine Falle mit C Boolean

Beitrag von Warf »

Das problem ist Relativ simpel, Pascal benutzt Operator überladung für Logische und Bitweise operationen. Der "not" Operator ist für sowohl Logische negation, also auch für Bitweise Negation, die Unterscheidung ist auf basis des Typsystems, also wenn not vor einem Boolean typen steht wird Logisches Not verwendet, vor einem Ordinaltypen dann die Bitweise.

C hingegen hat zwei verschiedene Operatoren "!" für Logische negation und "~" für Bitweise, außerdem hat C keinen echten bool typen (naja mit späteren versionen schon, ich glaube C99 hat den eingeführt, bzw. C++ hat auch einen), sondern benutzt ordinaltypen. Diese beiden Sachen einzeln sind zwar kein Problem für Pascal, in Kombination allerdings schon, denn wenn man Bool werte aus C bekommt, bekommt man Ordinaltypen, womit die Operatorüberladung dann natürlich den Bitwise Operator und nicht den Logischen Operator auswählt.

Das Problem hierbei ist das Bitwise Operationen in Pascal eher Mäßig umgesetzt wurden (sollten ja Ursprünglich gar nicht Teil der Sprache sein, wurde ja Nachträglich hinzugefügt), und damit hier uneindeutigkeiten entstehen.

Die "einfachste" Lösung ist Casts:

Code: Alles auswählen

WriteLn(Ord(not Boolean(GL_TRUE)));
PS: Deshalb gibt es auch die Integer Kompatiblen Booleans wie "LongBool", bei denen False = -1 = not 0 ist und somit Bitweise und Logische Operatoren identisch sind

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

Re: Gemeine Falle mit C Boolean

Beitrag von Mathias »

Ich vermute ich habe den Fehler gesehen.
In dieser Routine werden 2 Booleans verglichen, welche als int deklariert sind.
Obwohl der Eingabe bool True ist und der der vergleichswert dies auch ist, sind sie trozdem unterschiedlich.
Ich habe den Fehler gefunden.

Code: Alles auswählen

void SDL_SetEventEnabled(Uint32 type, SDL_bool enabled)
{
    SDL_bool current_state;
    Uint8 hi = ((type >> 8) & 0xff);
    Uint8 lo = (type & 0xff);

    if (enabled) { enabled = SDL_TRUE; } // New

    if (SDL_disabled_events[hi] &&
        (SDL_disabled_events[hi]->bits[lo / 32] & (1 << (lo & 31)))) {
        current_state = SDL_FALSE;
    } else {
        current_state = SDL_TRUE;
    }

    if (enabled != current_state) {
        if (enabled) {
#ifdef _MSC_VER /* Visual Studio analyzer can't tell that SDL_disabled_events[hi] isn't NULL if enabled is true */
#pragma warning(push)
#pragma warning(disable : 6011)
#endif
            SDL_disabled_events[hi]->bits[lo / 32] &= ~(1 << (lo & 31));
#ifdef _MSC_VER
#pragma warning(pop)
#endif

            /* Gamepad events depend on joystick events */
            switch (type) {
            case SDL_EVENT_GAMEPAD_ADDED:
                SDL_SetEventEnabled(SDL_EVENT_JOYSTICK_ADDED, SDL_TRUE);
                break;
            case SDL_EVENT_GAMEPAD_REMOVED:
                SDL_SetEventEnabled(SDL_EVENT_JOYSTICK_REMOVED, SDL_TRUE);
                break;
            case SDL_EVENT_GAMEPAD_AXIS_MOTION:
            case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
            case SDL_EVENT_GAMEPAD_BUTTON_UP:
                SDL_SetEventEnabled(SDL_EVENT_JOYSTICK_AXIS_MOTION, SDL_TRUE);
                SDL_SetEventEnabled(SDL_EVENT_JOYSTICK_HAT_MOTION, SDL_TRUE);
                SDL_SetEventEnabled(SDL_EVENT_JOYSTICK_BUTTON_DOWN, SDL_TRUE);
                SDL_SetEventEnabled(SDL_EVENT_JOYSTICK_BUTTON_UP, SDL_TRUE);
                break;
            case SDL_EVENT_GAMEPAD_UPDATE_COMPLETE:
                SDL_SetEventEnabled(SDL_EVENT_JOYSTICK_UPDATE_COMPLETE, SDL_TRUE);
                break;
            default:
                break;
            }
        } else {
            /* Disable this event type and discard pending events */
            if (!SDL_disabled_events[hi]) {
                SDL_disabled_events[hi] = (SDL_DisabledEventBlock *)SDL_calloc(1, sizeof(SDL_DisabledEventBlock));
            }
            /* Out of memory, nothing we can do... */
            if (SDL_disabled_events[hi]) {
                SDL_disabled_events[hi]->bits[lo / 32] |= (1 << (lo & 31));
                SDL_FlushEvent(type);
            }
        }

        /* turn off drag'n'drop support if we've disabled the events.
           This might change some UI details at the OS level. */
        if (type == SDL_EVENT_DROP_FILE || type == SDL_EVENT_DROP_TEXT) {
            SDL_ToggleDragAndDropSupport();
        }
    }
}
Diese Abfrage läuft falsch, wen unterschiedliche True ankommen. Und es werden irgendwelche Speicher Reservierungen umgangen, deshalb hat es geknallt.

Code: Alles auswählen

    if (enabled != current_state) {
Dies kann man mit folgender Zeile ungehen.

Code: Alles auswählen

    if (enabled) { enabled = SDL_TRUE; } // New
Ich habe beim SDL3 Team ein Bugtracker geschrieben.
Wen man einen Integer als Boolean wählt, dann sollte jeder Wert der ein Integer ist funktionieren, ohne das es einen SIGSEV auslöst.
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: Gemeine Falle mit C Boolean

Beitrag von Mathias »

Das Problem hierbei ist das Bitwise Operationen in Pascal eher Mäßig umgesetzt wurden (sollten ja Ursprünglich gar nicht Teil der Sprache sein, wurde ja Nachträglich hinzugefügt), und damit hier uneindeutigkeiten entstehen.
Wen man rein Pascal codet ist dies auch kein Problem. Wie du schon schreibt in Verbindung mit C-Libs muss man recht aufpassen, das sie keine einheitliche Linie haben. Wie oben schon erwähnt, wird sogar ein enum dafür verwendet.
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: Gemeine Falle mit C Boolean

Beitrag von Mathias »

Der Fehler ist schon behoben.
https://github.com/libsdl-org/SDL/issues/9544

Sie habe es ein wenig anders gelöst als in meinem Vorschlag.
Die haben ein Doppel not gemacht.

Code: Alles auswählen

    enabled = !!enabled;  // make sure this is definitely either SDL_TRUE or SDL_FALSE.
Da sieht man wieder die Vorteile von Opensource, da gehen Fehlerbehebungen zT. sehr schnell.
Im Vergleich zu dem geschlossenen Windoof und anderen Microschrott Produkten, wo es Jahre gehen kann.

Auch das FPC/Lazarus Team ist das super.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

BeniBela
Beiträge: 321
Registriert: Sa 21. Mär 2009, 17:31
OS, Lazarus, FPC: Linux (Lazarus SVN, FPC 2.4)
CPU-Target: 64 Bit

Re: Gemeine Falle mit C Boolean

Beitrag von BeniBela »

Warf hat geschrieben: So 14. Apr 2024, 23:13 außerdem hat C keinen echten bool typen (naja mit späteren versionen schon, ich glaube C99 hat den eingeführt, bzw. C++ hat auch einen), sondern benutzt ordinaltypen.
Das ist wirklich dämlich

Manchmal muss ich in C programmieren. Da habe vor Jahren ein C-Programm geschrieben und mir ein bool definiert. Nun musste ich das updaten, und eine Library verwenden, und dann stürzt es ab.

Da habe ich stundenlang untersucht, und dann gemerkt, dass mein bool, das bool aus dem Libraryheader überschreibt. Da hatte die struct dann die falsche Größe

siro
Beiträge: 761
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: Gemeine Falle mit C Boolean

Beitrag von siro »

Ich habe von Anfang an mein TRUE und FALSE in "C" selbst definiert:

Code: Alles auswählen

#define FALSE (0)           /* only the 0 is FALSE */
#define TRUE  (!FALSE)      /* all others are TRUE */
somit ist sichergestellt, dass alles was keine 0 ist TRUE entspricht.

Wenn man TRUE als 1 definiert, was in vielen Headern zu finden ist,
dann sind alle Werte zwischen 1 und xxx (je nach Datentyp) undefiniert....
somit ist mein TRUE, unabhängig vom Datentyp, immer TRUE wenn es nicht FALSE (also 0) ist.

Ist bei fpc anscheinend auch so, hab grad einige Versuche gemacht.
Ungleich 0 entspricht TRUE, also auch 13 oder 457....
Die Ordinalzahl von TRUE ist aber 1
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

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: Gemeine Falle mit C Boolean

Beitrag von Joh »

Da hatte ich auch meine schmerzhaften Augenblicke: Umstellung einer bestehenden Software auf Lazarus mit Firebird als Datenbank.
In einigen Tabellen waren boolsche Werte als Bool deklariert; alles passte.
Aber bei den mit Int deklarierten schrieb mit Lazarus/oder Firebird statt der vorhandenen 1 eine -1 bei true rein.

Und in einigen Abfragen stand latürnich
SELECT ... FROM ... WHERE wert=1

Das der Fehler nur bei einigen (neuen) Datensätzen auftauchte und nur bei einigen Abfragen, machte die Fehlersuche nicht einfacher.
just my two Beer

Antworten