c++ (Arduino) : Was macht if (object), wenn man doch Objekte nicht null setzen kann ?

Gast #6978601
Lesenswert?

Im Beispielcode für Bluetooth LE mit einem Arduino Nano 33 BLE heißt es 
:
1
  BLEDevice peripheral = BLE.available();
2

3
  if (peripheral) {
4
    Serial.print("Found ");
5
   }

Wobei "peripheral" doch wohl ein Objekt der Klasse "BLEDevice" ist. Bei 
Stackoverflow ist man sich einig, dass man Objekte nicht null setzen 
kann. Was überprüft also das if im Beispiel ?

Ich würde nämlich gerne an einer Stelle in meinem Programm "peripheral" 
auf etwas setzen, so dass die Bedingung falsch ist.

Wie kann ich das machen ?
#6978617
Lesenswert?

Wie ist BLEDevice denn definiert?
Wenn es ein Zeiger ist, wird der im if auf ungleich NULL geprüft.

Ansonsten kann man sich in C++ für jeden Datentyp eine Konversion nach 
bool definieren und damit ein Objekt auf true oder false testen (z.B. 
Iteratoren, smart pointer, optional<> etc.).

FOp schrieb:
> Wobei "peripheral" doch wohl ein Objekt der Klasse "BLEDevice" ist. Bei
> Stackoverflow ist man sich einig, dass man Objekte nicht null setzen
> kann.

Wo steht das?

Bei stackoverflow laufen ganz clevere Leute rum.
Ich glaube kaum, daß so eine pauschale und in ihrer Pauschalität 
unrichtige Aussage dort so steht.
Gast #6978633
Lesenswert?

Klaus W. schrieb:
> und in ihrer Pauschalität
> unrichtige Aussage dort so steht.
Denke doch, dass das stimmt....

Pointer kann man auf NULL oder nullptr setzen, aber keine Instanzen.

Klaus W. schrieb:
> Ansonsten kann man sich in C++ für jeden Datentyp eine Konversion nach
> bool definieren und damit ein Objekt auf true oder false testen

So ist es!

Klasse::operator bool() {return istNebelig();}
#6978657
Lesenswert?

EAF schrieb:
> Pointer kann man auf NULL oder nullptr setzen, aber keine Instanzen.

Nicht im Sinne von Java, sodaß das Objekt tatsächlich ungültig ist.
Aber man kann sich einen (logisch) ungültigen Zustand definieren, z.B. 
in einer bool.Memervariablen und z.B. mit einem operator entsprechend 
setzen.


Siehe die diversen smart-Pointer oder std::optional<>.
Gast #6978670
Lesenswert?

Meistens führt eine Initialisierung mit dem default constructor bei 
einem Aufruf von "operator bool()" zu false (wenn einer vorhanden ist). 
So auch hier (laut link von Tom).

Also:
1
BLEDevice peripheral;
2

3
if (peripheral) {
4
  Serial.print("Found ");
5
} else {
6
  Serial.print("not found"); // <--
7
}
oder
1
BLEDevice peripheral = BLE.available();
2

3
peripheral = BLEDevice();
4

5
if (peripheral) {
6
  Serial.print("Found ");
7
} else {
8
  Serial.print("not found"); // <--
9
}
Gast #6978672
Lesenswert?

Klaus W. schrieb:
> Aber man kann sich einen (logisch) ungültigen Zustand definieren, z.B.
> in einer bool.Memervariablen und z.B. mit einem operator entsprechend
> setzen.
Natürlich.
Wäre ja auch eine armselige OOP Implementierung, wenn ein Objekt keine 
inneren Zustände halten könnte.
Das wird auch bei Stackoverflow sicherlich niemand bezweifeln.
Ist also eine ganz andere Baustelle.

Ansosnten:
Die Frage war, wie if (peripheral) da oben funktioniert, und die hast du 
richtig beantwortet.
#6978866
Lesenswert?

Mikro 7. schrieb:
> Tom schrieb:
>>
> 
https://github.com/arduino-libraries/ArduinoBLE/blob/8d4d1dcca552f33871181fa18f6558b016ab16a2/src/BLEDevice.cpp#L213
>
> Was sich die Programmierer dabei gedacht haben den operator bool statt
> einer Funktion wie bspw. isAvailable() zu nutzen grenzt imho schon an
> code obfuscation.

Ich bin mir nicht sicher, ob die Existenz, die Semantik oder die 
Implementierung schlimmer ist.
Gast #6978870
Lesenswert?

Mikro 7. schrieb:
> operator bool

Es gibt keinen operator bool.
Aber es gibt einen Operator, welcher beim Konvertieren verwendet wird.
"Konvertierungsoperator" wäre als oder etwas bessere Begriff.

Schlussendlich kann die betreffende Klasse selber entscheiden WIE sie 
konvertiert wird.

Mikro 7. schrieb:
> imho
Eigentlich ein übliches Verfahren in C++.
Es wundert mich, dass du das nicht kennst.
#6978892
Lesenswert?

Minus M. schrieb:
> Mikro 7. schrieb:
>> BLEDevice::operator bool() const
>
> Und
> Zwiebelkuchen Klasse::operator Zwiebelkuchen() const .....
> ist dann ein Zwiebelkuchen operator?
> Nee...

Nein! Es ist operator Zwiebelkuchen. Es ist ja auch operator bool und 
nicht bool operator ...

EAF schrieb:
> Mikro 7. schrieb:
>> imho
> Eigentlich ein übliches Verfahren in C++.
> Es wundert mich, dass du das nicht kennst.

Was genau meinst du mit "üblich"?
Gast #6978898
Lesenswert?

Mombert H. schrieb:
> Was genau meinst du mit "üblich"?

"üblich" heißt weit verbreitet, Normalität.

Der Default wäre wohl:
BLEDevice::operator bool() {return true;}

Abschaltbar, in der Klassendefinition,  mit
operator bool() = delete;

Das überladen von Operatoren  und Methoden ist eine gut dokumentierte 
C++ Spracheigenschaft.
Der Casting Operator ist da keine Besonderheit.
Damit eigentlich auch kein Grund sich zu wundern.
(Firma: irre) #6978911
Lesenswert?

Mombert H. schrieb:
> Nein! Es ist operator Zwiebelkuchen. Es ist ja auch operator bool und
> nicht bool operator ...

Hach...
Es ist der Konvertierungs Operator.
Einmal kann er nach Zwiebelkuchen konvertieren und einmal nach bool.

Dass es schon eine vorgefertigte Implementierung gibt, tut dabei nix zur 
Sache, denn er wird überschrieben, falls es ihn schon gibt..
So wie auch string den Operator = überschreibt, oder zumindest überläd.
#6978917
Lesenswert?

EAF schrieb:
> W.A. schrieb:
>> Das wird dir verraten, ob es dein Objekt gibt
>
> Falsch!
> Wenn der eben genannte Operator nicht definiert wird, ist eine Instanz
> immer true.

Um dich zu zitieren:
> Falsch!

Wenn der Operator nicht definiert ist, bricht der Compiler mit einem 
Fehler ab, da if einen booleschen Wert fordert, das Objekt aber keiner 
ist und dann eben mangels Operator auch nicht in einen konvertiert 
werden kann.
Gast #6978922
Lesenswert?

Rolf M. schrieb:
> Wenn der Operator nicht definiert ist, bricht der Compiler mit einem
> Fehler ab, da if einen booleschen Wert fordert, das Objekt aber keiner
> ist und dann eben mangels Operator auch nicht in einen konvertiert
> werden kann.
Stimmt!
Du hast war.

Ich nehme den Default zurück.
Ein Irrtum meinerseits.

Das "üblich" allerdings nicht.
#6978927
Lesenswert?

EAF schrieb:
> Mombert H. schrieb:
>> Was genau meinst du mit "üblich"?
>
> "üblich" heißt weit verbreitet, Normalität.
Dann bewegst du dich in anderen C++ Kreisen als ich. Wenn eine 
Konvertierung nötig ist, erfolgt das normalerweise über einen 
entsprechenden explicit Konstruktor.

> Der Default wäre wohl:
> BLEDevice::operator bool() {return true;}
>
> Abschaltbar, in der Klassendefinition,  mit
> operator bool() = delete;
Default? Abschaltbar? Was meinst du damit?

> Das überladen von Operatoren  und Methoden ist eine gut dokumentierte
> C++ Spracheigenschaft.
> Der Casting Operator ist da keine Besonderheit.
> Damit eigentlich auch kein Grund sich zu wundern.
Es gibt meiner Meinung nach nur sehr sehr selten einen guten Grund 
diesen Operator zu implementieren.
#6978938
Lesenswert?

Mombert H. schrieb:
>> "üblich" heißt weit verbreitet, Normalität.
> Dann bewegst du dich in anderen C++ Kreisen als ich. Wenn eine
> Konvertierung nötig ist, erfolgt das normalerweise über einen
> entsprechenden explicit Konstruktor.

Man kann keine Konstruktoren für bool schreiben.

> Es gibt meiner Meinung nach nur sehr sehr selten einen guten Grund
> diesen Operator zu implementieren.

Das gibt es schon in der Standardbibliothek häufiger:
https://en.cppreference.com/w/cpp/io/basic_ios/operator_bool
https://en.cppreference.com/w/cpp/utility/functional/function/operator_bool
https://en.cppreference.com/w/cpp/memory/shared_ptr/operator_bool
https://en.cppreference.com/w/cpp/utility/optional/operator_bool
https://en.cppreference.com/w/cpp/error/error_code/operator_bool
Gast #6978954
Lesenswert?

> Ich bin mir nicht sicher, ob die Existenz, die Semantik oder die
> Implementierung schlimmer ist.

Für sich alleine genommen sind sowohl Existenz, Semantik als auch 
Implementierung in Ordnung.

Das Problem ist, nach 40 Jahren haben sich tausende von Features und 
Designpattern angesammelt, die eigentlich alle ok sind. Aber wie dieser 
Thread zeigt - es sind zu viele. Jeder C++ Programmierer kennt und 
benutzt unterschiedliche Teile der Features und Pattern.
Gast #6978962
Lesenswert?

Mombert H. schrieb:
> Default? Abschaltbar? Was meinst du damit?
Nix default!
Das war ein Irrtum.

Default/Dokumentierte Konvertierungen gibt es nur für die mitgelieferten 
Datentypen, wie z.B.  alle Integer, Pointer

Abschaltbar: delete

Ohne operator bool() {return true;}
> error: could not convert 'test' from 'BLEDevice' to 'bool'

Mit operator bool() = delete;
> error: use of deleted function 'BLEDevice::operator bool()'


Mombert H. schrieb:
> Dann bewegst du dich in anderen C++ Kreisen als ich
Möglich.
#6978966
Lesenswert?

Rolf M. schrieb:
> Mombert H. schrieb:
>>> "üblich" heißt weit verbreitet, Normalität.
>> Dann bewegst du dich in anderen C++ Kreisen als ich. Wenn eine
>> Konvertierung nötig ist, erfolgt das normalerweise über einen
>> entsprechenden explicit Konstruktor.
>
> Man kann keine Konstruktoren für bool schreiben.
Es gibt auch selten einen guten Grund implizit nach bool zu 
konvertieren.

>> Es gibt meiner Meinung nach nur sehr sehr selten einen guten Grund
>> diesen Operator zu implementieren.
>
> Das gibt es schon in der Standardbibliothek häufiger:
> https://en.cppreference.com/w/cpp/io/basic_ios/operator_bool
> https://en.cppreference.com/w/cpp/utility/functional/function/operator_bool
> https://en.cppreference.com/w/cpp/memory/shared_ptr/operator_bool
> https://en.cppreference.com/w/cpp/utility/optional/operator_bool
> https://en.cppreference.com/w/cpp/error/error_code/operator_bool
Und bei welchem dieser Beispiele ist die Bedeutung dieser Konvertierung 
nach bool offensichtlich? Was der Wahrheitswert eines basic_ios? Was ist 
der Wahrheitswert eines shared_ptr? Was ist der Wahrheitswert eines 
shared_ptr<bool*>? Was ist der Wahrheitswert eines std::optional<bool>? 
Ich kann bei keinem, der von dir gelisteten Typen, die Frage "Was ist 
der Wahrheitswehrt von X?" beantworten.

Noch ein Kommentar schrieb:
>> Ich bin mir nicht sicher, ob die Existenz, die Semantik oder die
>> Implementierung schlimmer ist.
>
> Für sich alleine genommen sind sowohl Existenz, Semantik als auch
> Implementierung in Ordnung.
Echt? Wie kann es in Ordnung sein, dass der Wahrheitswert eines 
Bluetooth-Devices mit seiner Adresse zu tun hat? Und wie kann es in 
Ordnung sein, dass in C++ die Adresse mit memcpy verglichen wird?
Gast #6979017
Lesenswert?

Mombert H. schrieb:
> Echt? Wie kann es in Ordnung sein, dass der Wahrheitswert eines
> Bluetooth-Devices mit seiner Adresse zu tun hat? Und wie kann es in
> Ordnung sein, dass in C++ die Adresse mit memcpy verglichen wird?

Naja....

Vielleicht, weil es der Programmierer so definiert hat?
Weil es zum Interface der Klasse gehört?
Und hoffentlich dokumentiert ist.

Es ist völlig ok, wenn dir das Verfahren nicht schmeckt.
Aber nur weil es dir nicht schmeckt, ist es doch nicht böse, oder 
verwerflich.

Solange du die Wahl hast, musst du das "Konzept der Benutzer- 
definierten Casts" ja nicht verwenden.
#6979042
Lesenswert?

EAF schrieb:
> Es ist völlig ok, wenn dir das Verfahren nicht schmeckt.
> Aber nur weil es dir nicht schmeckt, ist es doch nicht böse, oder
> verwerflich.

Mit der Argumentation kann man auf jede Kritik antworten. Dir schmeckt 
goto-Spaghetti nicht? Das macht es doch nicht böse oder verwerflich. Du 
magst keine rohen Pointer? Das macht sie doch nicht böse oder 
verwerflich.
Gast #6979054
Lesenswert?

Mit dem Unterschied, dass es im Gegensatz zu Spaghetticode ein modernes 
Konzept ist. Nutzt du überhaupt modernes c++ (ab c++11)? Falls ja, bist 
du mit der Einstellung in der Minderheit.

Mombert H. schrieb:
> der Wahrheitswert eines basic_ios? Was ist der Wahrheitswert eines
> shared_ptr? Was ist der Wahrheitswert eines shared_ptr<bool*>? Was ist
> der Wahrheitswert eines std::optional?

Wenn das Verhalten der Klassen kennt, ist das offensichtlich.
#6979064
Lesenswert?

avr schrieb:
> Mit dem Unterschied, dass es im Gegensatz zu Spaghetticode ein modernes
> Konzept ist. Nutzt du überhaupt modernes c++ (ab c++11)? Falls ja, bist
> du mit der Einstellung in der Minderheit.
Ein modernes Konzept? Ich hab hier "More Effective C++" von 1996? 
rumliegen. Da gibts schon ein Kapitel mit Titel "Be wary of user defined 
conversion functions", das einige der Probleme auflistet.

> Mombert H. schrieb:
>> der Wahrheitswert eines basic_ios? Was ist der Wahrheitswert eines
>> shared_ptr? Was ist der Wahrheitswert eines shared_ptr<bool*>? Was ist
>> der Wahrheitswert eines std::optional?
>
> Wenn das Verhalten der Klassen kennt, ist das offensichtlich.
Man muss also erst nachlesen wie es definiert ist, damit es 
offensichtlich ist? Ist das das Argument? Ich habe nachgelesen, wie es 
definiert ist und halte es trotzdem für unlogisch.
Gast #6979065
Lesenswert?

Mombert H. schrieb:
> Dir schmeckt goto-Spaghetti nicht?

Mit goto habe ich keine Probleme, und löst auch keine Emotionen aus.
Spagetti, jeglicher Art, sind mir allerdings zuwider.

Damals, altes Basic, da gabs mal ein goto-Spaghetti Problem.
Lange ist her.....
War sicherlich ein Grund für mich, um davon fern zu bleiben.

Mombert H. schrieb:
> Du magst keine rohen Pointer?
Ja, stimmt.
Drum verwende ich sie auch nur in höchster Not.
In modernem C++ gibts allerdings kaum Grund, das zu tun.
Es sei denn, man muss sich mit C Altlasten rum schlagen.


Aus meiner Sicht gibt es keine Sprachmittel, welche per se böse, oder 
schlecht sind.

Es ist halt immer eine Frage des Einsatzzweckes.

Und hier if (peripheral) empfinde ich es als angemessen.
Da wird eben eine Funk- Instanz gefragt, ob sie verbunden, bzw. 
funktionsfähig ist.
#6979070
Lesenswert?

EAF schrieb:
> Und hier if (peripheral) empfinde ich es als angemessen.
> Da wird eben eine Funk- Instanz gefragt, ob sie verbunden, bzw.
> funktionsfähig ist.

Nein, da wird gefragt, ob das peripheral true oder false ist. Das ist 
etwas anderes. Und wie du feststellst ist auch schon unklar was die 
genaue bedeutung ist. Oder gibt es keinen Unterschied zwischen verbunden 
und funktionsfähig? Und wenn man sich die Implementation anschaut ist es 
weder noch, da getestet wird, ob die Adresse 0 ist.
Gast #6979072
Lesenswert?

Mombert H. schrieb:
> Da gibts schon ein Kapitel mit Titel "Be wary of user defined
> conversion functions", das einige der Probleme auflistet.

Ja, und?

Sei vorsichtig!
Das Etikett kann man sicherlich an jedes Sprachmittel dran kleben.
Denn mit jedem Sprachmittel kann man Mist bauen.

Zudem wurde ja auch der explicit Modifizierer eingeführt.
Das mildert einige der alten Problemchen

Mombert H. schrieb:
> Man muss also erst nachlesen wie es definiert ist,
Nicht die Definition/Code der Klasse ist maßgeblich, sondern das 
Interface sollte man kennen, bevor man es verwendet.
#6979078
Lesenswert?

EAF schrieb:
> Mombert H. schrieb:
>> Da gibts schon ein Kapitel mit Titel "Be wary of user defined
>> conversion functions", das einige der Probleme auflistet.
>
> Ja, und?
>
> Sei vorsichtig!
> Das Etikett kann man sicherlich an jedes Sprachmittel dran kleben.
> Denn mit jedem Sprachmittel kann man Mist bauen.

Wir können auch die Formulierung "Avoid implicit conversion operators" 
aus den C++-Core-Guidelines nehmen: 
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c164-avoid-implicit-conversion-operators

EAF schrieb:
> Zudem wurde ja auch der explicit Modifizierer eingeführt.
> Das mildert einige der alten Problemchen
Ich bezweifle, dass die Person, die den obigen operator bool geschrieben 
hat, da ein explicit dran schreiben wird. Es wäre aber schon lustig, 
wenn da plötlich ein
1
if(static_cast<bool>(peripheral))
stehen würde, statt es zu peripheral.is_valid() oder 
peripheral.has_address() zu ändern.
Gast #6979080
Lesenswert?

Mombert H. schrieb:
> Und wie du feststellst ist auch schon unklar was die
> genaue bedeutung ist.
Ist es nicht.
Es wird gefragt ob die Instanz verwendbar ist.
Diese Instanz antwortet mit ja oder nein.
So schwer ist das doch gar nicht.....

Mombert H. schrieb:
> Und wenn man sich die Implementation anschaut ist es
> weder noch, da getestet wird, ob die Adresse 0 ist.
Das ist ein Interna der Instanz.

Mit dem Arduino Serial ist es das gleiche!
if(Serial) fragt die Instanz, ob sie nutzbar ist.
Solange sie nicht mit true antwortet, macht es keinerlei Sinn da was hin 
zu schicken.
Gast #6979092
Lesenswert?

ich finde es ist sehr offensichtlich was 'if (peripheral)' bewirken 
soll. Was die Kriterien für eine Gültigkeit bzw. true sind, das sollte 
man der Doku/Code/Debugger entnehmen. Bei Arduino ist es eben usus und 
dann stellt man sich darauf ein. Wenn man es nicht toll findet, dann 
benutzt man eben keine Libs und schreibt alles selber. Aber solchen 
Inselcode finden andere nicht toll und du bleibst Einzelschicksal.
Gast #6979098
Lesenswert?

Johannes S. schrieb:
> Wenn man es nicht toll findet
Manches mal kann man zwischen verschiedenen Schreibweisen aussuchen.
So sich die schönere raus suchen.
1
  static_assert(is_integral<int>{},"Integral Type expected");
2
  static_assert(is_integral<int>(),"Integral Type expected");
3
  static_assert(is_integral<int>::value,"Integral Type expected");

Die mit ::value ist in meinen Augen nicht unbedingt die schönste.
Ich bevorzuge die erste, um (auch mir selber) klar zu machen, dass 
is_integral keine Funktion ist.
#6979148
Lesenswert?

Mombert H. schrieb:
> Und bei welchem dieser Beispiele ist die Bedeutung dieser Konvertierung
> nach bool offensichtlich?

Für mich bei so ziemlich allen. Und wenn bei einer API immer alles 
offensichtlich wäre, bräuchte man keine Doku. Das wäre zwar ein schönes, 
aber doch eher unerreichbares Ziel.
Übrigens ist auch für jedes dieser Beispiele ein Satz enthalten, der 
erklärt, was der Operator macht.

> Was der Wahrheitswert eines basic_ios?

Es sagt aus, ob der Stream benutzbar ist. Da steht ja sogar dabei, warum 
es das gibt:
"This operator makes it possible to use streams and functions that 
return references to streams as loop conditions, resulting in the 
idiomatic C++ input loops such as while(stream >> value) {...} or 
while(getline(stream, string)){...}. Such loops execute the loop's body 
only if the input operation succeeded."

> Was ist der Wahrheitswert eines shared_ptr?

Er sagt, ob er auf ein Objekt zeigt oder null ist, wie bei einem 
klassischen Zeiger auch.

> Was ist der Wahrheitswert eines shared_ptr<bool*>?

Das gleiche.

> Was ist der Wahrheitswert eines std::optional<bool>?

Es sagt, ob in dem std::optional ein Wert steht oder nicht. Das ist 
quasi die selbe Semantik wie bei einem Pointer.

> Ich kann bei keinem, der von dir gelisteten Typen, die Frage "Was ist
> der Wahrheitswehrt von X?" beantworten.

Außer für basic_ios, wo ich es schon vorher wusste, hab ich oben bei 
allen einfach mal intuitiv geschrieben, was ich erwarte. Dann habe ich 
nachgelesen, und es war auch genau das. Ich finde alle diese Operatoren 
intuitiv.

> Echt? Wie kann es in Ordnung sein, dass der Wahrheitswert eines
> Bluetooth-Devices mit seiner Adresse zu tun hat?

Ist wie bei einem Pointer. Auch dessen Wahrheitswert hängt von der 
enthaltenen Adresse ab, und zwar auf exakt die selbe Weise: Wenn sie 
null ist (und nur dann), ist der Wert false.

> Und wie kann es in Ordnung sein, dass in C++ die Adresse mit memcpy
> verglichen wird?

Das wird sie zum Glück nicht, sondern mit memcmp, was auch sehr viel 
geeigneter dafür erscheint.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren