Forum: PC-Programmierung switch(Funktion()) möglich?


von __Son´s B. (bersison)


Lesenswert?

Hallo zusammen.

Um eine switch-Anweisung zu vereinfachen, würde ich gerne eine Funktion 
statt einer Variablen einfügen.
Bsp.
1
switch (Fehlercode()) // Fehlercode() gibt 1-4 zurück
Was spricht dagegen?

: Verschoben durch Moderator
von W.A. (Gast)


Lesenswert?

__Son´s B. schrieb:
> Was spricht dagegen?

Das kommt drauf an, was das Handbuch der von dir verwendeten 
Programmiersprache dazu sagt.

von __Son´s B. (bersison)


Lesenswert?

W.A. schrieb:
> verwendeten Programmiersprache

Programmiersprache ist C.
Compiler AtmelStudio 7.

Wenn es funktioniert, wäre es besonders "unsauber" programmiert?

: Bearbeitet durch User
von good coding (Gast)


Lesenswert?

__Son´s B. schrieb:
> Wenn es funktioniert, wäre es besonders "unsauber" programmiert?

Ja, besser testbar und einfacher zu debuggen ist die Variante mit einer 
Hilfsvariablen. Ein gut optimierender Compiler erkennt die Verwendung 
und wird die Variable im 'Release' rauswerfen.

=> Varibale kostet nix und macht es einfacher.

von Peter S. (Gast)


Lesenswert?

Funktioniert prinzipiell, ist aber kein sauberer Programmier-Stil.

von Wilhelm M. (wimalopaan)


Lesenswert?

good coding schrieb:
> __Son´s B. schrieb:
>> Wenn es funktioniert, wäre es besonders "unsauber" programmiert?
>
> Ja, besser testbar und einfacher zu debuggen ist die Variante mit einer
> Hilfsvariablen.

Begründung?

von __Son´s B. (bersison)


Lesenswert?

Peter S. schrieb:
> Funktioniert prinzipiell, ist aber kein sauberer Programmier-Stil.

DANKE, für die konstruktiven Rückantworten!

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter S. schrieb:
> Funktioniert prinzipiell, ist aber kein sauberer Programmier-Stil.

Begründung?

von Einer K. (Gast)


Lesenswert?

Ich sehe da auch keinen Grund für....

Klar kann man eine unnötige Variable einführen.
Aber irgendwie schöner, wird da nix von.

von good coding (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Begründung

conditional breakpoint, Variable im Debugger verändern, ..., typesicher, 
...

von Wilhelm M. (wimalopaan)


Lesenswert?

good coding schrieb:
> Wilhelm M. schrieb:
>> Begründung
>
> conditional breakpoint, Variable im Debugger verändern, ..., typesicher,
> ...

In diesem switch-stmt? Wozu?

Typ-sicher?

von Maxx (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Peter S. schrieb:
>> Funktioniert prinzipiell, ist aber kein sauberer Programmier-Stil.
>
> Begründung?

Es erschwert das Debuggen, als dass (nicht überall) ein 
Unterbrechungspunkt nicht zwischen Auswertung des Selektors und Sprung 
zur Marke gesetzt werden kann.

Ein ordentliches
1
var_t selector = func();
2
switch (selector)
3
{

erfüllt seinen Zweck. ist kein wesentlicher Mehraufwand. Erzeugt keinen 
Overhead bei Optimierung und hat keine [versteckten] Nebeneffekte.

von Programmiersprachentheaterintendant (Gast)


Lesenswert?

Da man bei Hochsprachen sich vom Wissen was der Compiler hinter den 
Kulissen tut trennen sollte, sind mir Sprachen lieber welche sinngemäss 
auch folgendes erlauben:
1
switch(egal) {
2
3
case( function( ... )):
4
5
...
6
7
}

von Wilhelm M. (wimalopaan)


Lesenswert?

Maxx schrieb:
> Wilhelm M. schrieb:
>> Peter S. schrieb:
>>> Funktioniert prinzipiell, ist aber kein sauberer Programmier-Stil.
>>
>> Begründung?
>
> Es erschwert das Debuggen, als dass (nicht überall) ein
> Unterbrechungspunkt nicht zwischen Auswertung des Selektors und Sprung
> zur Marke gesetzt werden kann.

Das ist wohl Geschmackssache, ob ich einzelne unbedingte BPs oder einen 
bedingten BP setze.

>
> Ein ordentliches
>
1
> var_t selector = func();
2
> switch (selector)
3
> {
4
>
>
> erfüllt seinen Zweck. ist kein wesentlicher Mehraufwand.
> Erzeugt keinen
> Overhead bei Optimierung

Was für einen Overhead?

> und hat keine [versteckten] Nebeneffekte.

Wo waren die Seiteneffekte?

von Wilhelm M. (wimalopaan)


Lesenswert?

Programmiersprachentheaterintendant schrieb:
> Da man bei Hochsprachen sich vom Wissen was der Compiler hinter den
> Kulissen tut trennen sollte, sind mir Sprachen lieber welche sinngemäss
> auch folgendes erlauben:
>
1
> switch(egal) {
2
> 
3
> case( function( ... )):
4
> 
5
> ...
6
> 
7
> }
8
>

Nehme aber an, dass es sich um C handelt ... da gehts dann nicht.

von Maxx (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Maxx schrieb:
>> Wilhelm M. schrieb:
>>> Peter S. schrieb:
>>>> Funktioniert prinzipiell, ist aber kein sauberer Programmier-Stil.
>>>
>>> Begründung?
>>
>> Es erschwert das Debuggen, als dass (nicht überall) ein
>> Unterbrechungspunkt nicht zwischen Auswertung des Selektors und Sprung
>> zur Marke gesetzt werden kann.
>
> Das ist wohl Geschmackssache, ob ich einzelne unbedingte BPs oder einen
> bedingten BP setze.

Du kannst keinen einzelnen unbedingten oder bedingten BP setzen (es ist 
die gleiche Anweisung. Einige Umgebung erlauben keine multiplen BP pro 
Zeile) , es sei denn du setzt den BP innerhalb der func(). Dann aber 
feuert der auch bei anderen Aufrufen von func() ....

>
>>
>> Ein ordentliches
>>> var_t selector = func();
>> switch (selector)
>> {
>> >
>> erfüllt seinen Zweck. ist kein wesentlicher Mehraufwand.
>> Erzeugt keinen
>> Overhead bei Optimierung
>
> Was für einen Overhead?

Zuweisung einer Variablen.
Unoptimiert wird dafür Speicher reserviert, beschrieben und danach 
wieder ausgelesen.

>
>> und hat keine [versteckten] Nebeneffekte.
>
> Wo waren die Seiteneffekte?

func() macht etwas.
Du hast keine direkte Einsicht in dessen Inhalt. Wird dabei ein Zusatnd 
gewechselt?
Dies ist nicht offensichtlich.
Bei dem (ebenfalls schlechtem Stil)
1
switch (varB = varA)
2
{
fällt das wenigstens auf.

von Programmiersprachentheaterintendant (Gast)


Lesenswert?

> Ein ordentliches
>
1
> var_t selector = func();
2
> switch (selector)
3
> {
4
>

passt so schön zu
1
var_t i = heutigerAnfang(...);
2
for ( ; i = heutigerSchritt(...) ; i != heutigesEnde(...) )
3
{

O_o

von Wilhelm M. (wimalopaan)


Lesenswert?

Programmiersprachentheaterintendant schrieb:
>> Ein ordentliches
>>
1
>> var_t selector = func();
2
>> switch (selector)
3
>> {
4
>>
>
> passt so schön zu
>
1
> var_t i = heutigerAnfang(...);
2
> for ( ; i = heutigerSchritt(...) ; i != heutigesEnde(...) )
3
> {
4
>
>
> O_o

Ja ;-)

Wenn schon, dann:
1
   switch(auto x = foo()) {
2
    
3
    }

von Wilhelm M. (wimalopaan)


Lesenswert?

Maxx schrieb:
> Wilhelm M. schrieb:
>> Maxx schrieb:
>>> Wilhelm M. schrieb:
>>>> Peter S. schrieb:
>>>>> Funktioniert prinzipiell, ist aber kein sauberer Programmier-Stil.
>>>>
>>>> Begründung?
>>>
>>> Es erschwert das Debuggen, als dass (nicht überall) ein
>>> Unterbrechungspunkt nicht zwischen Auswertung des Selektors und Sprung
>>> zur Marke gesetzt werden kann.
>>
>> Das ist wohl Geschmackssache, ob ich einzelne unbedingte BPs oder einen
>> bedingten BP setze.
>
> Du kannst keinen einzelnen unbedingten oder bedingten BP setzen (es ist
> die gleiche Anweisung.

Will ich doch auch gar nicht ;-)


> func() macht etwas.
> Du hast keine direkte Einsicht in dessen Inhalt. Wird dabei ein Zusatnd
> gewechselt?

Das ist (könnte sein) auch bei der ursprünglichen Variante so.

von Maxx (Gast)


Lesenswert?

Ich denke auf Troll-Level brauchen wir nicht weiterzureden.

Man kann auch aus nem Trog fressen und wird nicht verhungern. Der Stil 
wird jedoch in den wenigsten Kulturen als gut gelten.

Ich bin raus. Ciao

von MaWin O. (Gast)


Lesenswert?

Programmiersprachentheaterintendant schrieb:
> sind mir Sprachen lieber welche sinngemäss
> auch folgendes erlauben:switch(egal) {
>
> case( function( ... )):
>
> ...
>
> }


Wann wird function aufgerufen, wenn es mehrere Cases gibt? Immer alle? 
In welcher Reihenfolge? Wird der Rest ausgelassen, sobald ein Case 
zutrifft?

Das ist sehr schwer lesbar und nachvollziehbar.
Nein danke.

von good coding (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Will ich doch auch gar nicht ;-)

Du willst deinen Code nicht testen?
Ok, dann mach mal weiter so. :-(((

von Wilhelm M. (wimalopaan)


Lesenswert?

good coding schrieb:
> Wilhelm M. schrieb:
>> Will ich doch auch gar nicht ;-)
>
> Du willst deinen Code nicht testen?
> Ok, dann mach mal weiter so. :-(((

Testen mache ich nicht mit dem Debugger, ich schreibe Test-Fälle ... 
lies Dir den Beitrag nochmal genau durch.

von A. S. (Gast)


Lesenswert?

__Son´s B. schrieb:
> Was spricht dagegen?

Da spricht nichts gegen, es ist sauber, klein, effektiv und lesbar.

Ich verstehe nicht, wieso einem Anfänger (der auch höflich fragt) das um 
die Ohren gehauen wird, nur weil Fälle denkbar sind, in denen eine 
getrennte Variable besser wäre?

Und wer sinngemäß auch "case( function( ... ))" wünscht, macht ein Fass 
auf, dass den Bereich der Imperativen Programmiersprachen verlässt. Denn 
bei denen würde das (im gegensatz zu Bereichen oder Masken) umfangreiche 
defizile Definition erfordern, wenn und in welcher Reihenfolge diese 
Funktionen denn aufgerufen würden.

von good coding (Gast)


Lesenswert?

Wilhelm M. schrieb:
> ich schreibe Test-Fälle

Das wird durch eine Hilfsvariable erleichtert!

Maxx schrieb:
> Ich bin raus. Ciao

Du hast recht, sollen die Hobby-Bastler ihre Zeit bei erschwerter 
Ferhlsuche vergeuden.

Auch raus ...

von Wilhelm M. (wimalopaan)


Lesenswert?

good coding schrieb:
> Wilhelm M. schrieb:
>> ich schreibe Test-Fälle
>
> Das wird durch eine Hilfsvariable erleichtert!

Wie schreibst Du denn Deine Testfälle?

von Wilhelm M. (wimalopaan)


Lesenswert?

Achim S. schrieb:
> __Son´s B. schrieb:
>> Was spricht dagegen?
>
> Da spricht nichts gegen, es ist sauber, klein, effektiv und lesbar.

Sehe ich genauso.

Falls es C++ sein sollte, kannst Du mit einem init-stmt im 
Bedingungsteil den Scope von ggf. benötigten Bezeichnern auf das 
switch-stmt einschränken. Was dann ein Vorteil wäre.

von Programmiersprachentheaterintendant (Gast)


Lesenswert?

> Wann wird function aufgerufen, wenn es mehrere Cases gibt? Immer alle?
> In welcher Reihenfolge? Wird der Rest ausgelassen, sobald ein Case
> zutrifft?
Das steht in der Sprachspezifikation geschrieben, wo hoffentlich nicht 
so oft wie in C et al. "behaviour is left to the implementor" vorkommt.

Die selben Fragen stellen sich bei If-Kaskaden.
Also wozu überhaupt ein so eingeschränktes "switch"? Das geht mit Arrays 
genausogut.

> Das ist sehr schwer lesbar und nachvollziehbar.
> Nein danke.
U.U. nicht Schwieriger als C et al.

von Wolfgang (Gast)


Lesenswert?

Maxx schrieb:
>> Was für einen Overhead?
>
> Zuweisung einer Variablen.
> Unoptimiert wird dafür Speicher reserviert, beschrieben und danach
> wieder ausgelesen.

Sinnvollerweise wird der Compiler soetwas in ein Register packen. Dann 
wird deswegen gar nicht im Speicher rumgemacht ...
Wer in seinem Programm das Timing über Taktzyklen macht, sollte 
allerdings tunlichst wissen, was er tut.

von Einer K. (Gast)


Lesenswert?

Achim S. schrieb:
> Und wer sinngemäß auch "case( function( ... ))" wünscht, macht ein Fass
> auf, dass den Bereich der Imperativen Programmiersprachen verlässt. Denn
> bei denen würde das (im gegensatz zu Bereichen oder Masken) umfangreiche
> defizile Definition erfordern, wenn und in welcher Reihenfolge diese
> Funktionen denn aufgerufen würden.

PHP tut das so.
1
switch(true)
2
{
3
  case $zeit===$mitternacht: tuwas(); break;
4
}
Hat seines Reiz!

von MaWin O. (Gast)


Lesenswert?

good coding schrieb:
>> ich schreibe Test-Fälle
>
> Das wird durch eine Hilfsvariable erleichtert!

Woher willst du das wissen?

von MaWin O. (Gast)


Lesenswert?

Arduino F. schrieb:
>> Und wer sinngemäß auch "case( function( ... ))" wünscht, macht ein Fass
>> auf, dass den Bereich der Imperativen Programmiersprachen verlässt. Denn
>> bei denen würde das (im gegensatz zu Bereichen oder Masken) umfangreiche
>> defizile Definition erfordern, wenn und in welcher Reihenfolge diese
>> Funktionen denn aufgerufen würden.
>
> PHP tut das so.switch(true)
> {
>   case $zeit===$mitternacht: tuwas(); break;
> }
> Hat seines Reiz!


Erstens ist "PHP tut das so" ein sehr gutes Argument die Idee direkt zu 
verwerfen.
Zweitens ist das, was du dort zeigst, nicht das, was gemeint war. Du 
hast keinen Funktionsaufruf im case-Wert.

von Einer K. (Gast)


Lesenswert?

Ma W. schrieb:
> Zweitens ist das, was du dort zeigst, nicht das, was gemeint war. Du
> hast keinen Funktionsaufruf im case-Wert.

Das ist irrelevant!
Da darf alles stehen, was einen Wert liefert.
Auch eine Funktion.

von MaWin O. (Gast)


Lesenswert?

Programmiersprachentheaterintendant schrieb:
> Das steht in der Sprachspezifikation geschrieben, wo hoffentlich nicht
> so oft wie in C et al. "behaviour is left to the implementor" vorkommt.

Ich hatte jetzt gehofft, dass du wenigstens versuchst es zu definieren.

> Die selben Fragen stellen sich bei If-Kaskaden.

Es stellen sich viel mehr Fragen.
Was machst du z.B. bei einem fall-through? Funktion aufrufen, oder 
nicht?

> Also wozu überhaupt ein so eingeschränktes "switch"? Das geht mit Arrays
> genausogut.

Ja dann mach mal vor, wie das "mit Arrays genausogut" geht.

von MaWin O. (Gast)


Lesenswert?

Arduino F. schrieb:
> Das ist irrelevant!

Nein, ist es nicht, für diese Frage.

> Da darf alles stehen, was einen Wert liefert.
> Auch eine Funktion.

Ok. Dann kannst du ja auch sicher die Regeln aufzählen, wann und wie 
Funktionen aufgerufen werden, die im case-Wert stehen.

von Einer K. (Gast)


Lesenswert?

Ma W. schrieb:
> Ok. Dann kannst du ja auch sicher die Regeln aufzählen, wann und wie
> Funktionen aufgerufen werden, die im case-Wert stehen.

Die Auswertungen im Case finden dann statt, wenn der Programmlauf an der 
Stelle ankommt.

Der Nachteil ist offensichtlich:
Damit wären, bezogen auf C oder C++, sämtliche 
Optimierungsmöglichkkeiten des Kompilers aus dem Rennen.
Also nix mehr mit vorberechneten Sprungtabellen, welche ein Kompiler nur 
erstellen kann, wenn ihm die Case Werte VORHER bekannt sind.


Immerhin erlaubt C++ mittlerweile Bereiche.

> case 4 ... 16: tuwas();break;

von MaWin O. (Gast)


Lesenswert?

Arduino F. schrieb:
> Die Auswertungen im Case finden dann statt, wenn der Programmlauf an der
> Stelle ankommt.

Und wann tut er das?
Werden erst alle cases aufgelöst, um dann zu entscheiden welcher 
angesprungen wird?
Oder werden sie in einer bestimmten Reihenfolge ausgewertet, wie 
if-else-if?
Was passiert beim fall-through? Wird die Funktion des Cases aufgerufen?

Das alles braucht ein sehr komplexes Regelwerk und ist schwer zu 
durchblicken und fehleranfällig, weil die Aufrufe nicht direkt 
offensichtlich sind.
Aber das ist bei PHP ja ein Designmerkmal.

von Einer K. (Gast)


Lesenswert?

Das ist nicht kompliziert....
Du machst es kompliziert....

Ma W. schrieb:
> Werden erst alle cases aufgelöst, um dann zu entscheiden welcher
> angesprungen wird?
Nein.

Ma W. schrieb:
> Oder werden sie in einer bestimmten Reihenfolge ausgewertet, wie
> if-else-if?
Ja.

Ma W. schrieb:
> Was passiert beim fall-through? Wird die Funktion des Cases aufgerufen?
Nein!
Wenn keine Auswertung nötig ist, wird auch keine gemacht.
Und beim "fall-through" ist keine Auswertung nötig.

Ma W. schrieb:
> Das alles braucht ein sehr komplexes Regelwerk und ist schwer zu
> durchblicken und fehleranfällig, weil die Aufrufe nicht direkt
> offensichtlich sind.
Nur wenn du von deiner starren C/C++ Sicht darauf schaust.
Ansonsten, ist das schon recht gut intuitiv erfassbar.

Ma W. schrieb:
> Aber das ist bei PHP ja ein Designmerkmal.
PHP wurde dazu erfunden um Hypertexte zu manipulieren.
Soll möglichst Plattformneutral arbeiten.
Ist halt eine ganz andere Zielsetzung, als die Hauptanwendung von C/C++.
C wurde erfunden um Betriebssystemkerne zu schreiben.

von MaWin O. (Gast)


Lesenswert?

Arduino F. schrieb:
> Nur wenn du von deiner starren C/C++ Sicht darauf schaust.

Ich habe keine starre C/C++ Sicht.
Ich habe eine Sicht für sinnvolle und unsinnig komplexe Dinge.

> Ansonsten, ist das schon recht gut intuitiv erfassbar.

Ein Umsortieren der cases kann die Semantik ändern, je nachdem ob der 
Case-Wert Nebenwirkungen hat.
Ziemlich gefährlich.
Bei if-else-if ist der Codeflow offensichtlicher.

>Und beim "fall-through" ist keine Auswertung nötig.

Und das weiß auch jeder Programmierer und der Code unterhalb des cases 
nimmt nicht an, dass die Funktion aufgerufen wurde?
Gefährlich.

>PHP wurde dazu erfunden um Hypertexte zu manipulieren.
>Soll möglichst Plattformneutral arbeiten.

Und deshalb hat es total bekloppte Regeln?
Macht der total unbrauchbare inline-if-operator es also 
plattformneutraler und besser für Hypertexte?

von Nop (Gast)


Lesenswert?

Arduino F. schrieb:

> PHP wurde dazu erfunden um Hypertexte zu manipulieren.
> Soll möglichst Plattformneutral arbeiten.

Ja, PHP ist auf jeder Plattform wüst zusammengepfuschte Sch**ße.

von Einer K. (Gast)


Lesenswert?

http://php.net/manual/de/control-structures.switch.php

Der eigentliche/häufigste Grund für die Verwendung von:
> switch(true)
> {
>   case $zeit===$mitternacht: tuwas(); break;
> }

Ist der typsichere Vergleich.

Ma W. schrieb:
> je nachdem ob der
> Case-Wert Nebenwirkungen hat.
> Ziemlich gefährlich.
Seiteneffekte sind IMMER gefährlich!
Auch an dieser Stelle.
Und genauso nützlich sind sie, wenn man weiß, was man tut.


Es ist immer schwer, Äpfel mit Birnen zu vergleichen, wenn persönliche 
Vorlieben im Spiel sind.
Darum interessiert mich ein Besser oder Schlechter, in diesem 
Zusammenhang nicht.

Ma W. schrieb:
> Und deshalb hat es total bekloppte Regeln?
> Macht der total unbrauchbare inline-if-operator es also
> plattformneutraler und besser für Hypertexte?
Bitte keine (Vor)Urteile in Fragen....
Das widerspricht allen üblichen Regeln für konstruktive Dialoge.

von MaWin O. (Gast)


Lesenswert?

Arduino F. schrieb:
> Der eigentliche/häufigste Grund für die Verwendung von:
>> switch(true)
>> {
>>   case $zeit===$mitternacht: tuwas(); break;
>> }
>
> Ist der typsichere Vergleich.

Man braucht es also, um ein anderes Sprachdefizit mit einem völlig 
verrückten Konstrukt auszugleichen.
Das passt natürlich wieder in das PHP-Bild.

von Einer K. (Gast)


Lesenswert?

Ma W. schrieb:
> Man braucht es also, um ein anderes Sprachdefizit mit einem völlig
> verrückten Konstrukt auszugleichen.

Und wieder ein Urteil.....
Offensichtlich bist du eher auf Krawall aus.
Nicht ein einem Dialog interessiert.

Man kann das laxe Typehandling von PHP als Feature, oder als 
(design)Fehler betrachten.
Das bleibt ganz dir überlassen....

Tipp:
Falls du doch mal mit PHP arbeiten musst, wird dir deine engstirnige 
Einstellung fürchterlich im Wege rum stehen.

von MaWin O. (Gast)


Lesenswert?

Arduino F. schrieb:
> Falls du doch mal mit PHP arbeiten musst

Nein danke.

von Stefan F. (Gast)


Lesenswert?

> Da man bei Hochsprachen sich vom Wissen was der Compiler hinter den
> Kulissen tut trennen sollte, sind mir Sprachen lieber welche sinngemäss
> auch folgendes erlauben ...

Der Haken ist: switch/case ist in C ein spezieller Ersatz für if/else 
Ketten, welcher gewährleistet, dass jeder Sprung gleich viel Zeit in 
Anspruch nimmt.

Dies wäre mit variablen case-Werten nicht möglich. Aus dem selben Grund 
kann man in C auch keine Strings beim case benutzen.

von Nop (Gast)


Lesenswert?

Arduino F. schrieb:

> Man kann das laxe Typehandling von PHP als Feature, oder als
> (design)Fehler betrachten.

PHP hat kein Design. Es wurde einfach rangefrickelt, was konkret gerade 
gebraucht wurde, nach Aussage von Lerdorf. Zudem war PHP ursprünglich 
auch nicht als Sprache entworfen worden. Das Ergebnis ist dann wie zu 
erwarten ein Clusterfsck.

Manches ist auch nicht mit dem Amateurstatus zu erklären, den Lerdorf 
damals hatte. Daß Funktionen mal in C-Namenskonvention bekannt sind, mal 
nach Java-Art, daß es mal "f(needle, haystack)" und mal "f(haystack, 
needle)" heißt, ist einfach Zeugnis einer durch und durch schlampigen 
Grundeinstellung.

Und wie man eine Websprache entwickeln kann, für die es keinen 
Unterschied zwischen Codestrings und Datenstrings gibt, ist mir auch ein 
Rätsel. Gerade so, als ob die buffer overflows von C auf 
von-Neumann-Rechnern kein mahnendes Beispiel gewesen wären. 90% der 
Lücken in Wordpress & Co kommen durch diese Schlamperei zustande.

von Stefan F. (Gast)


Lesenswert?

Ich sehe das auch so. Javascript ist schon ätzend, aber PHP wirklich 
schlimm. Ich bin froh, dass in meiner Firma sogar die Frontend 
Entwickler damit begonnen haben, den PHP Code Schritt für Schritt zu 
reduzieren.

von MaWin O. (Gast)


Lesenswert?

Stefanus F. schrieb:
> Der Haken ist: switch/case ist in C ein spezieller Ersatz für if/else
> Ketten, welcher gewährleistet, dass jeder Sprung gleich viel Zeit in
> Anspruch nimmt.

Wo ist das definiert?

von Nop (Gast)


Lesenswert?

Ma W. schrieb:

> Wo ist das definiert?

Gar nicht, denn der Compiler ist auch frei, ein switch als if/then/else 
zu implementieren. Das macht er insbesondere, wenn der umspannte 
Wertebereich sehr groß ist und eine Sprungtabelle daher zu groß würde.

Besonders lustig ist sowas, wenn man switch/case in einem Interrupt 
verwendet, alles gut ist und man ein case hinzufügt - und auf einmal ein 
komplett anderes Timing der ISR hat. Weil es dann unversehens zu einer 
if/then/else-Kette wurde.

von Tom (Gast)


Lesenswert?

Ob eine Zwischenspeicher-Variable lesbarer ist, würde ich anhand des 
Falls unterscheiden:

Wenn die Funktion nur eine Zugriffsfunktion auf das zu Unterscheidende 
ist, gewinnt man IMO nichts:
1
switch(get_machine_state())
2
{Ob eine Zwischenspeicher-Variable lesbarer ist, würde ich anhand des Falls unterscheiden:
3
4
Wenn die Funktion nur eine Zugriffsfunktion auf das zu Unterscheidende ist, gewinnt man IMO nichts:
5
[c]
6
switch(get_machine_state())
7
{
8
    case RUNNING: bla(); break;
9
    case OFF:     fasel(); break;
10
    case ERROR:   complain(); break;
11
}


Wen die Funktion primär irgendetwas bewirkt und zusätzlich einen Status 
zurückgibt, den man auswertet, würde ich es so besser finden:
1
result_t erfolg = try_get_herrschaft(&welt);
2
switch(erfolg)
3
{
4
    case OK:        display("Beuget die Knie!"); break;
5
    case FAILED:    display("Entschuldigung, war nicht so gemeint."); break;
6
    case NOT_FOUND: display("Wo ist die Welt hin?"); retry_herrschaft = false; break;
7
}
Hier passieren zwei verschiedene Dinge in zwei Schritten. Den 
wichtigeren Schritt würde ich hier nicht in der Klammer des switch() 
verstecken.
    case RUNNING: bla(); break;
    case OFF:     fasel(); break;
    case ERROR:   complain(); break;
}
[/c]


Wen die Funktion primär irgendetwas bewirkt und zusätzlich einen Status 
zurückgibt, den man auswertet, würde ich es so besser finden:
1
result_t erfolg = try_get_herrschaft(&welt);
2
switch(erfolg)
3
{
4
    case OK:        display("Beuget die Knie!"); break;
5
    case FAILED:    display("Entschuldigung, war nicht so gemeint."); break;
6
    case NOT_FOUND: display("Wo ist die Welt hin?"); retry_herrschaft = false; break;
7
}
Hier passieren zwei verschiedene Dinge in zwei Schritten. Das wichtigere 
würde ich nicht in der Klammer des switch() verstecken.

von Tom (Gast)


Lesenswert?

Was ist hier passiert? So sollte es:


Ob eine Zwischenspeicher-Variable lesbarer ist, würde ich anhand des 
Falls unterscheiden:

Wenn die Funktion nur eine Zugriffsfunktion auf das zu Unterscheidende 
ist, gewinnt man IMO nichts:
1
switch(get_machine_state())
2
{
3
    case RUNNING: bla(); break;
4
    case OFF:     fasel(); break;
5
    case ERROR:   complain(); break;
6
}


Wen die Funktion primär irgendetwas bewirkt und zusätzlich einen Status
zurückgibt, den man auswertet, würde ich es so besser finden:
1
result_t erfolg = try_get_herrschaft(&welt);
2
switch(erfolg)
3
{
4
    case OK:        display("Beuget die Knie!"); break;
5
    case FAILED:    display("Entschuldigung, war nicht so gemeint."); break;
6
    case NOT_FOUND: display("Wo ist die Welt hin?"); retry_herrschaft = false; break;
7
}
Hier passieren zwei verschiedene Dinge in zwei Schritten. Den
wichtigeren Schritt würde ich hier nicht in der Klammer des switch()
verstecken.

von Wilhelm M. (wimalopaan)


Lesenswert?

Tom schrieb:
>
> Wen die Funktion primär irgendetwas bewirkt und zusätzlich einen Status
> zurückgibt, den man auswertet, würde ich es so besser finden:
>
1
> result_t erfolg = try_get_herrschaft(&welt);
2
> switch(erfolg)
3
> {
4
>     case OK:        display("Beuget die Knie!"); break;
5
>     case FAILED:    display("Entschuldigung, war nicht so gemeint."); 
6
> break;
7
>     case NOT_FOUND: display("Wo ist die Welt hin?"); retry_herrschaft = 
8
> false; break;
9
> }
10
>

Der Nachteil an so einer Vorgehensweise ist, dass man in den umgebenden 
Block einen Bezeichner einführt, den man u.U. nur im Block des 
switch-stmt haben möchte.

In C++ kann man folgendes schreiben:
1
switch(auto result = foo()) {
2
}


oder auch
1
switch(auto [result, reason] = foo(); result) {
2
}

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

>> Der Haken ist: switch/case ist in C ein spezieller Ersatz für if/else
>> Ketten, welcher gewährleistet, dass jeder Sprung gleich viel Zeit in
>> Anspruch nimmt.

> Wo ist das definiert?

Keine Ahnung, woher ich das habe. Es gilt jedenfalls nicht nur für den 
gcc, soviel ist sicher. Infos dazu:

http://lazarenko.me/switch/
https://www.codeproject.com/Articles/100473/Something-You-May-Not-Know-About-the-Switch-Statem
https://blogs.msdn.microsoft.com/abhinaba/2006/12/18/switches-and-jump-tables/

von MaWin O. (Gast)


Lesenswert?

Stefanus F. schrieb:
> Keine Ahnung, woher ich das habe.

Soso.

> Es gilt jedenfalls nicht nur für den
> gcc, soviel ist sicher.

Überhaupt nichts ist sicher.
Das ist kein garantiertes Verhalten.
Auch der gcc generiert if-else-if für switch, wenn er sich dazu 
entscheidet.

von (prx) A. K. (prx)


Lesenswert?

Stefanus F. schrieb:
> Der Haken ist: switch/case ist in C ein spezieller Ersatz für if/else
> Ketten, welcher gewährleistet, dass jeder Sprung gleich viel Zeit in
> Anspruch nimmt.

Nope, das ist nirgends so definiert und das wäre allein schon aufgrund 
der Eigenschaften der Hardware auch praktisch unmöglich zu 
gewährleisten.

Compiler kennen diverse verschiedene Methoden, switch() Statements 
umzusetzen, die sie je nach Anzahl und Verteilung der Werte auswählen. 
Keine davon produziert jedoch auf PC-Prozessoren der letzten 20 Jahre 
eine wiederholbare und vom Wert unabhängige Laufzeit.

Auch ein Table-Switch kann aufgrund des branch target predictors von 
einer Ausführung zur nächsten unterschiedlich lange dauern, und kann 
eine vom Wert oder der Folge von Werten abhängige Laufzeit haben.

Zudem kann ein einziges geändertes oder hinzugefügtes Case-Label die 
verwendete Methode völlig umkrempeln.

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Der Nachteil an so einer Vorgehensweise ist, dass man in den umgebenden
> Block einen Bezeichner einführt, den man u.U. nur im Block des
> switch-stmt haben möchte.

Kein Problem in C:
1
{
2
    result_t erfolg = try_get_herrschaft(&welt);
3
    switch(erfolg)
4
    {
5
        case OK:        display("Beuget die Knie!"); break;
6
        case FAILED:    display("Entschuldigung, war nicht so gemeint."); break;
7
        case NOT_FOUND: display("Wo ist die Welt hin?"); retry_herrschaft = false; break;
8
    }
9
}

von Programmiersprachentheaterintendant (Gast)


Lesenswert?

Ma W. schrieb:
> Programmiersprachentheaterintendant schrieb:
>> Also wozu überhaupt ein so eingeschränktes "switch"? Das geht mit Arrays
>> genausogut.
>
> Ja dann mach mal vor, wie das "mit Arrays genausogut" geht.

Du hast danach gefragt, beschwere Dich anschliessend nicht:
1
functab[selector](param1, param2);

von Wilhelm M. (wimalopaan)


Lesenswert?

Nop schrieb:
> Wilhelm M. schrieb:
>
>> Der Nachteil an so einer Vorgehensweise ist, dass man in den umgebenden
>> Block einen Bezeichner einführt, den man u.U. nur im Block des
>> switch-stmt haben möchte.
>
> Kein Problem in C:
>
>
1
> {
2
>     result_t erfolg = try_get_herrschaft(&welt);
3
>     switch(erfolg)
4
>     {
5
> ...
6
>     }
7
> }


Nö, der Bezeichner erfolg ist nach wie vor außerhalb des switch-stmt 
sichtbar, etwa direkt nach der } des switch-stmt.

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Nö, der Bezeichner erfolg ist nach wie vor außerhalb des switch-stmt
> sichtbar, etwa direkt nach der } des switch-stmt.

Spielt keine Rolle, weil man deswegen den Block vor der Deklaration 
einführen kann.

Im Übrigen geht es in diesem Thread ausweislich des Postings #3 um C, 
nicht C++, und es wäre nett, wenn Du aufhören würdest, C-Threads mit C++ 
zuzuspammen.

von Nop (Gast)


Lesenswert?

Programmiersprachentheaterintendant schrieb:

> Du hast danach gefragt, beschwere Dich anschliessend nicht:
> functab[selector](param1, param2);

Oder mit computed goto, also goto via Array-Index auf ein Array, in dem 
Labeladressen stehen. Das ist allerdings nicht Standard-C, sondern GCC 
und Clang können es.

von Wilhelm M. (wimalopaan)


Lesenswert?

Nop schrieb:
> Wilhelm M. schrieb:
>
>> Nö, der Bezeichner erfolg ist nach wie vor außerhalb des switch-stmt
>> sichtbar, etwa direkt nach der } des switch-stmt.
>
> Spielt keine Rolle, weil man deswegen den Block vor der Deklaration
> einführen kann.

Das könnte man ja auch in C++, aber ich habe es aus gutem Grund nicht 
genannt, denn es löst das Problem nicht.

> Im Übrigen geht es in diesem Thread ausweislich des Postings #3 um C,
> nicht C++, und es wäre nett, wenn Du aufhören würdest, C-Threads mit C++
> zuzuspammen.

Den Ausflug in PHP hattest Du wohl übersehen?

Nicht alles, was einen Blick über den Tellerrand bietet, ist Spam.

von MaWin O. (Gast)


Lesenswert?

Nop schrieb:
>> Du hast danach gefragt, beschwere Dich anschliessend nicht:
>> functab[selector](param1, param2);
>
> Oder mit computed goto, also goto via Array-Index auf ein Array, in dem
> Labeladressen stehen. Das ist allerdings nicht Standard-C, sondern GCC
> und Clang können es.

Man könnte auch inline-assembly nehmen.

von Nop (Gast)


Lesenswert?

Ma W. schrieb:

> Man könnte auch inline-assembly nehmen.

Das wäre dann aber nicht mehr portabel. Computed goto funktioniert auf 
ARM genauso wie auf x86, wenn der Compiler es unterstützt.

Da man bekanntlich nicht verfrüht optimieren soll, hat man zunächst 
einmal ja ohnehin eine Codeversion, die ohne solche Scherze auskommt. 
Sofern es was bringt, kann man dann die Version mit computed goto 
hinzufügen - und die andere per ifdef drinlassen, falls man das mal mit 
einem Compiler durchziehen will, der das nicht unterstützt.

von Wilhelm M. (wimalopaan)


Lesenswert?

Nop schrieb:
> Ma W. schrieb:
>
>> Man könnte auch inline-assembly nehmen.
>
> Das wäre dann aber nicht mehr portabel. Computed goto funktioniert auf
> ARM genauso wie auf x86, wenn der Compiler es unterstützt.
>
> Da man bekanntlich nicht verfrüht optimieren soll, hat man zunächst
> einmal ja ohnehin eine Codeversion, die ohne solche Scherze auskommt.
> Sofern es was bringt, kann man dann die Version mit computed goto
> hinzufügen - und die andere per ifdef drinlassen, falls man das mal mit
> einem Compiler durchziehen will, der das nicht unterstützt.

Jo, genau. Das kannst Du per ifdef doch auch protabel machen. Oh man ...

Aus dem Konstrukt switch / if das beste heraus zu holen, ist Sache des 
Compiler.

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Aus dem Konstrukt switch / if das beste heraus zu holen, ist Sache des
> Compiler.

Schafft er aber halt nicht immer. Falls man es auf Geschwindigkeit 
anlegt, wird's halt mitunter etwas dreckig. Ob es das wert ist, muß der 
Entwickler anhand des vorher/nachher-Profilings entscheiden.

von (prx) A. K. (prx)


Lesenswert?

Wilhelm M. schrieb:
> Aus dem Konstrukt switch / if das beste heraus zu holen, ist Sache des
> Compiler.

Von Hand per if/else einen balancierten binary tree search zu 
implementieren ist äusserst spassbefreit, ganz besonders wenn sich 
gelegentlich was dran ändert. Und das ist jene Variante, die bei einer 
grösseren Anzahl von Werten und arg löchriger Werteverteilung oft die 
effektivste sein wird.

von __Son´s B. (bersison)


Lesenswert?

Da habe ich etwas los getrete, wovon ich nur wenig nachvollziehen kann.
Daher vielleicht ein konkretes Bsp das nicht korrekt (vollkommen 
unkontrolliert) läuft;

Es handelt sich um 2 Selbsthaltungen, die erst nach bestimmten 
Startbedingungen/-routinen ablaufen sollen. Daher der default-Block.
1
default:                
2
switch(Entprell_PB2())  //Selbshaltung, 1-Impuls=ON, 2-Impuls=gelöscht=OFF
3
{
4
  case 1:  PB2_high=1;  break;    // Taster/Schalter=ON
5
  case 2:  PB2_high=0;  break;    // Taster/Schalter=OFF
6
}
7
if(PB2_high)  LED_HELL_ON;
8
else    LED_ALLE_OFF;
9
                
10
switch(Entprell_PB1())  // Selbshaltung, 1-Impuls=ON, 2-Impuls=gelöscht=OFF
11
{
12
  case 1:  PB1_high=1;  break;    // Taster/Schalter=ON
13
  case 2:  PB1_high=0;  break;    // Taster/Schalter=OFF
14
}      
15
if(PB1_high)  LED_DUNKEL_ON;
16
else    LED_ALLE_OFF;
17
18
break;

Mir ist bewusst, dass der eine oder Andere diese switch-Schreibweise 
"unsauber" findet.
Aber wo steckt der konkrete Fehler?

von Nop (Gast)


Lesenswert?

__Son´s B. schrieb:

> Daher vielleicht ein konkretes Bsp das nicht korrekt (vollkommen
> unkontrolliert) läuft;

Zu einer Fehlerfrage gehört:

1) Welches Verhalten wurde bei welchen Eingabedaten erwartet?
2) Welches Verhalten wurde stattdessen bei welchen Eingabedaten 
beobachtet?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

__Son´s B. schrieb:
> Aber wo steckt der konkrete Fehler?

Ein konkreter Fehler ist da nicht zu sehen, nur potentielle Fehler. 
Was geschieht, wenn die Funktionen "Entprell_XX" andere Werte als 1 oder 
2 zurückliefern? Welchen Wert haben die Variablen PB1_high/PB2_high?

Warum rufst Du die Macros "LED_HELL_ON" etc. nicht direkt in Deinen 
beiden switch-Statements auf, und verwendest die zusätzlichen Variablen 
PB1_high etc.? Werden die noch irgendwo anders verwendet?

von Peter D. (peda)


Lesenswert?

Warum die unnötige Indirektion?
Besser:
1
switch(Entprell_PB1())  // Selbshaltung, 1-Impuls=ON, 2-Impuls=gelöscht=OFF
2
{
3
  case 1:  LED_DUNKEL_ON;  break;    // Taster/Schalter=ON
4
  case 2:  LED_ALLE_OFF;   break;    // Taster/Schalter=OFF
5
  default:                 break;
6
}

von __Son´s B. (bersison)


Lesenswert?

Rufus Τ. F. schrieb:

> Ein konkreter Fehler ist da nicht zu sehen, nur potentielle Fehler.
> Was geschieht, wenn die Funktionen "Entprell_XX" andere Werte als 1 oder
> 2 zurückliefern? Welchen Wert haben die Variablen PB1_high/PB2_high?

Entprell_XX liefert bei Flankenanstieg einen "1"-Impuls, bei 
Flankenabfall einen "2"-Impuls. Sonst "0", hier soll der letzte Zustand 
erhalten bleiben: ON oder OFF.
Daher weise PB1_high bei Flankenanstieg "1" und bei Flankenabfall eine 
"2" zu. Dies scheint mir die einfachste und flexibelste Lösung zu 
sein...

Rufus Τ. F. schrieb:
> Warum rufst Du die Macros "LED_HELL_ON" etc. nicht direkt in Deinen
> beiden switch-Statements auf, und verwendest die zusätzlichen Variablen
> PB1_high etc.? Werden die noch irgendwo anders verwendet?

Ich wollte eine universelle Taster-Überwachung schaffen, die immer und 
überall (in späteren c-Prog) einsetzbar sind.
In deisem konkreten Prog wird die Var XXX_high nicht weiter verwendet.

von __Son´s B. (bersison)


Lesenswert?

Nop schrieb:
> Zu einer Fehlerfrage gehört:
> 1) Welches Verhalten wurde bei welchen Eingabedaten erwartet?
> 2) Welches Verhalten wurde stattdessen bei welchen Eingabedaten
> beobachtet?

Rufus Τ. F. schrieb:
> Ein konkreter Fehler ist da nicht zu sehen, nur potentielle Fehler.

FEHLER GEFUNDEN!
Es war ein Hardwarefehler auf der Versuchs-LP.
Daher noch einmal DANKE für eure Anmerkungen!

von A. S. (Gast)


Lesenswert?

Mir ist (nach deinen Bezeichnern zu urteilen) nicht klar, warum LED_HELL 
nur an sein darf, wenn auch LED_DUNKEL an ist.

Wenn Entprell_PBx zylisch aufgerufen werden muss (also mit jedem Aufruf 
getaktet ist), dann ist es m.E. nicht glücklich, dies im darauf 
reagierenden Code zu verteilen. Je nach Architektur ist meist ein 
lokaler (oder globaler) Tastatentatus sinnvoller, also z.B. 
KeyPB1={An=1/Make=3/Break=2/Aus=0} oder entsprechende boolsche 
Tastaturabbilder (je 0/1): (Key.PB1, Key.PB2, ..., KeyMake.PB1, .... , 
KeyBreak.PB1, ...}

Wenn Entprell notfalls auch mehrfach oder garnicht aufgerufen werden 
kann, ... dann ist es nicht die Entprellung sondern fragt den Zustand 
der Tasten ab. Das sollte dann entsprechend billig sein, dass ggf. auch 
direkter code mit mehrfachen ifs möglich ist.

von __Son´s B. (bersison)


Lesenswert?

Achim S. schrieb:
> Mir ist (nach deinen Bezeichnern zu urteilen) nicht klar, warum LED_HELL
> nur an sein darf, wenn auch LED_DUNKEL an ist.

Beide Tasten und somit deren Ausgänge, sind vollkommen unabhängig 
voneiander.
Ich möchte einen Tastenabfrage-Block, Tasten/Schalterabfrage 1-4, die in 
ihre Var XXX_high schreiben. Im Anschluss kommt erst ein Handlungsblock 
bei dem ich die Var XXX_high auswerte.

Mein oben aufgeführter Code ist ein Versuchsblock um zu sehen, was nach 
Tastenentpr. und Selbshaltung raus kommt.

von NichtWichtig (Gast)


Lesenswert?

Wer mal im Team an Großprojekten programmieren möchte sollte sich einen 
brauchbaren Programmierstil angewöhnen.

Derartiges Zeugs (switch(fun()) ... gehört nicht dazu.

Jeden Abend läuft Axivion über den Sourcecode und liefert Warnungen und 
Verstöße gegen die coding guide lines.

Das kommt nicht gut bei der Führung an wenn bestimmte Mitarbeiter immer 
wieder durch solche Bullshit auffallen.

Zudem wollen >50 andere Entwickler auch die ein oder andere Klasse 
verbauen  und da spielt Lesbarkeit eine Rolle.

von __Son´s B. (bersison)


Lesenswert?

Das ist ja verrückt;
wird zuerst PB2 und danach PB1 abgefragt, funktioniert PB1/Dunkel 
einwandfrei, aber keine Reaktion wenn PB2/Hell betätigt wird.
1
default:
2
switch(Entprell_PB2())
3
{
4
  case 1:  PB2_high=1;  break;
5
  case 2:  PB2_high=0;  break;  
6
}
7
if(PB2_high)  LED_HELL_ON;
8
else    LED_ALLE_OFF;
9
        
10
switch(Entprell_PB1())  
11
{
12
  case 1:  PB1_high=1;  break;  
13
  case 2:  PB1_high=0;  break;  
14
}
15
if(PB1_high)  LED_DUNKEL_ON;
16
else    LED_ALLE_OFF;
17
18
break;

Werden die beiden Blöcke umgetauscht, zuerst PB1-Abfrage+Auswertung und 
danach PB2, funktionieren beide Blöcke einwandfrei!
1
default:
2
switch(Entprell_PB1())  
3
{
4
  case 1:  PB1_high=1;  break;  
5
  case 2:  PB1_high=0;  break;  
6
}
7
if(PB1_high)  LED_DUNKEL_ON;
8
else    LED_ALLE_OFF;
9
10
switch(Entprell_PB2())
11
{
12
  case 1:  PB2_high=1;  break;
13
  case 2:  PB2_high=0;  break;  
14
}
15
if(PB2_high)  LED_HELL_ON;
16
else    LED_ALLE_OFF;
17
18
break;

Wo ist denn hier die Logik?

von Wilhelm M. (wimalopaan)


Lesenswert?

NichtWichtig schrieb:
> Wer mal im Team an Großprojekten programmieren möchte sollte sich einen
> brauchbaren Programmierstil angewöhnen.
>
> Derartiges Zeugs (switch(fun()) ... gehört nicht dazu.

Kannst Du das auch begründen?

>
> Jeden Abend läuft Axivion über den Sourcecode und liefert Warnungen und
> Verstöße gegen die coding guide lines.

Das ist kein Grund bzw. vielmehr ein Grund ggf. das Regelwerk des Tools 
anzupassen.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Kannst Du das auch begründen?

Muss man das? Man legt halt in einem Team einmal die Regeln fest. Dann 
sollte man sich daran halten - der Einheitlichkeit des Codes wegen.

> Das ist kein Grund bzw. vielmehr ein Grund ggf. das Regelwerk des Tools
> anzupassen.

Wenn es eine Mehrheitsentscheidung gibt, ja. Aber wegen des Geschmacks 
eines einzelnen: nein.

von Wilhelm M. (wimalopaan)


Lesenswert?

Frank M. schrieb:
> Wilhelm M. schrieb:
>> Kannst Du das auch begründen?
>
> Muss man das? Man legt halt in einem Team einmal die Regeln fest. Dann
> sollte man sich daran halten - der Einheitlichkeit des Codes wegen.

Ja, entweder gibt es objektive technische Gründe, oder es ist eben nur 
eine Teamfestlegung für den Geschmack, die hier natürlich keine Relevanz 
hat.

>> Das ist kein Grund bzw. vielmehr ein Grund ggf. das Regelwerk des Tools
>> anzupassen.
>
> Wenn es eine Mehrheitsentscheidung gibt, ja. Aber wegen des Geschmacks
> eines einzelnen: nein.

Genau! Mit scheint es eine Frage des Geschmacks zu sein. Und Geschmack 
kann man / muss man nicht begründen. Dann sollte man aber seinen eigenen 
oder den Geschmack ds Teams nicht anderen aufdrücken wollen.

von NichtWichtig (Gast)


Lesenswert?

Wartbarkeit des codes
debuggen
Verständnis derer die den code benutzen aber nicht geschrieben haben.
Forderungen durch Normen

Wir können und wollen es uns nicht leisten miesen code weltweit bei 
Kunden einzusetzen.

keep it simple!


Was bei diesen tollen Optimierungsversuchen passiert käönnt ihr dort
Autor: __Son´s Bersi__ (bersison)
Datum: 15.05.2018 12:29

nachlesen

 hihi

von A. S. (Gast)


Lesenswert?

__Son´s B. schrieb:
> aber keine Reaktion wenn PB2/Hell betätigt wird.

> Wo ist denn hier die Logik?

Achim S. schrieb:
> nicht klar, warum LED_HELL nur an sein darf, wenn auch LED_DUNKEL an ist.

Nochmal:

Du machst zweimal alle LEDs aus, mit "else LED_ALLE_OFF;"



Wenn es funktioniren soll, solltest Du im else-Zweig nur die eine LED 
ausmachen. Oder am Anfang alle aus und dann nur setzen (spielt bei LEDs 
meist keine Rolle).

Wenn es noch weitere Fehler gibt, werden die wohl in den 
Entprell-Routinen zu suchen sein. Poste deren Code ruhig mit.

von Wilhelm M. (wimalopaan)


Lesenswert?

NichtWichtig schrieb:
> Wartbarkeit des codes

Wo liegt bei
1
switch(foo()) {
2
...
3
}

die Nichtwartbarkeit? Was wäre denn D.E. warum besser?

> debuggen

s.o. Was wäre denn D.E. warum besser?

> Verständnis derer die den code benutzen aber nicht geschrieben haben.

s.o. Was wäre denn D.E. warum besser?

> Forderungen durch Normen

Das gilt für Euch, aber nicht allgemein.

> keep it simple!

Wie wäre es denn einfacher?

von __Son´s B. (bersison)


Lesenswert?

Achim S. schrieb:
>Wenn es funktioniren soll, solltest Du im else-Zweig nur die eine LED
>ausmachen. Oder am Anfang alle aus und dann nur setzen (spielt bei LEDs
> meist keine Rolle).

Tatsächlich war das der Fehler!
Trotzdem verstehe ich nicht, warum beim Reihenfolgewechsel, erst PB1 
danach PB2, die Funktion iO war? Habs mehrfach ausprobiert - Zufall?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Dann sollte man aber seinen eigenen oder den Geschmack ds Teams nicht
> anderen aufdrücken wollen.

Darauf wollte ich hinaus. Unter Geschmack zähle ich übrigens nicht nur 
die Wahl des Coding-Styles, sondern auch die individuelle Wahl der 
Programmiersprache. Das sollte man durchaus auch mal respektieren und 
sich die Gebetsmühle "In C++ geht das alles viel schöner als in C" ab 
und zu verkneifen. ;-)

: Bearbeitet durch Moderator
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Wo liegt bei switch(foo()) {
> ...
> }
>
> die Nichtwartbarkeit?

1. Man kann sich u.U. den Rückgabewert von foo() nicht per printf() 
testweise ausgeben lassen.

Bei
1
  int rtc = foo ();
2
  switch (rtc)
3
  ...

ist das einfach:
1
  int rtc = foo ();
2
3
#ifdef DEBUG
4
  printf ("rtc = %d\n", rtc);
5
#endif
6
7
  switch (rtc)
8
  ...

2. Wird später mal die Semantik in foo() geändert, so dass zum Beispiel 
für einen ganzen Zahlenbereich der switch() gar nicht durchgeführt 
werden soll, muss man mühsam den Funktionsaufruf und den switch() wieder 
auseinanderpopeln.

Beispiel: Es wird später eine Fehlerbehandlung in foo() eingebaut. 
Fehler sollen als negative Return-Werte zurückgegeben werden und zu 
einem Abbruch der Verarbeitung führen.

Bei
1
  int rtc = foo ();
2
  switch (rtc)
3
  ...

ist das einfach:
1
  int rtc = foo ();
2
3
  if (rtc < 0)
4
  {
5
    myerror (rtc);
6
    return (rtc);
7
  }
8
9
  switch (rtc)
10
  ...

3. Breakpoint im Debugger hinter den Aufruf von foo() setzen ist beinahe 
unmöglich.

Dein Argument, dass Du keinen Debugger nutzt, ist kein Argument. Es gibt 
durchaus Gründe, einen Debugger für bestimmte Szenarien zu nutzen statt 
ein spezielles Test-Case dafür zu verwenden.

Am wenigsten geeignet sind übrigens Test-Cases, die von demselben 
Programmierer stammen, der auch den zu testenden Code geschrieben hat. 
Solche Test-Cases berücksichtigen dann nur das, was man auch schon im 
Code verzapft hat. Daher sind sie im allgemeinen ungeeignet.

Ein Debugger jedoch kann da schon mal für den einen oder anderen 
Überraschungseffekt sorgen: "Stimmt, soooo habe ich das gar nicht 
bedacht!"

: Bearbeitet durch Moderator
von Wilhelm M. (wimalopaan)


Lesenswert?

Frank M. schrieb:
> Wilhelm M. schrieb:
>> Wo liegt bei switch(foo()) {
>> ...
>> }
>>
>> die Nichtwartbarkeit?
>
> 1. Man kann sich u.U. den Rückgabewert von foo() nicht per printf()
> testweise ausgeben lassen.

Ich kann z.B. im gdb einen bedingten BP setzen im switch(): foo()==42

> 2. Wird später mal die Semantik in foo() geändert, so dass zum Beispiel
> für einen ganzen Zahlenbereich der switch() gar nicht durchgeführt
> werden soll, muss man mühsam den Funktionsaufruf und den switch() wieder
> auseinanderpopeln.
>
> Beispiel: Es wird später eine Fehlerbehandlung in foo() eingebaut.
> Fehler sollen als negative Return-Werte zurückgegeben werden und zu
> einem Abbruch der Verarbeitung führen.
>
> Bei
>
1
>   int rtc = foo ();
2
>   switch (rtc)
3
>   ...
4
>
>
> ist das einfach:
>
>
1
>   int rtc = foo ();
2
> 
3
>   if (rtc < 0)
4
>   {
5
>     myerror (rtc);
6
>     return (rtc);
7
>   }
8
> 
9
>   switch (rtc)
10
>   ...
11
>

Das kann ich doch alles im switch erledigen. Wozu das if-stmt? Das ist 
für meinen Geschmack (s.o.) nun wieder das Gegenteil von KISS und 
Lokalität.


> 3. Breakpoint im Debugger hinter den Aufruf von foo() setzen ist beinahe
> unmöglich.

In der von mir genannten Art erzwingt die Schreibweise, dass der gesamte 
Wertebereich von foo() im switch abgedeckt ist / sein sollte. Dann kann 
ich in jedes case/default ein BP setzen.

Und wie gesagt: würde man hier nur dieses eine, kleine, neue feature von 
C++ nutzen (s.u.), nämlich in if-stmts und switch-stmts auch init-stmts 
einbauen zu können, so hätte man sehr viel gewonnen: hier genau 
Einfachheit und Lokalität.

> Dein Argument, dass Du keinen Debugger nutzt, ist kein Argument.

Das wollte ich nicht damit behaupten. Allerdings ist es extrem selten 
geworden ;-) C++ und strong-types lassen grüßen.

> Es gibt
> durchaus Gründe, einen Debugger für bestimmte Szenarien zu nutzen statt
> ein spezielles Test-Case dafür zu verwenden.
>
> Am wenigsten geeignet sind übrigens Test-Cases, die von demselben
> Programmierer stammen, der auch den zu testenden Code geschrieben hat.
> Solche Test-Cases berücksichtigen dann nur das, was man auch schon im
> Code verzapft hat. Daher sind sie im allgemeinen ungeeignet.

Das brauchst Du mir nicht zu erklären. Im übrigen habe ich das auch 
nicht gesagt.

> Ein Debugger jedoch kann da schon mal für den einen oder anderen
> Überraschungseffekt sorgen: "Stimmt, soooo habe ich das gar nicht
> bedacht!"

Wie schon des öfteren gesagt: statt ein Laufzeit-Debugging 
durchzuführen, sollte man seine Energie darauf verwenden, Code so zu 
gestalten, dass er nur schwer falsch und leicht richtig benutzt werden 
kann. Und gerade in dieser Hinsicht hat C nun so seine Schwächen und C++ 
seine Stärken. Zudem sehe ich bei sehr vielen C-Problemen, die hier 
immer und immer wieder besprochen werden, dass man sie in C++ nicht 
hätte. Und dass man die meisten dieser Programme einfach als C++ Code 
übersetzen könnte und dabei schrittweise die besseren Möglichkeiten 
dieser Sprache nutzen könnte. Deswegen weise ich darauf hin.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Dann sollte man aber seinen eigenen oder den Geschmack ds Teams nicht
> anderen aufdrücken wollen.

Passt nicht zur Gebetsmühle:

Wilhelm M. schrieb:
> Und gerade in dieser Hinsicht hat C nun so seine Schwächen und C++ seine
> Stärken. Zudem sehe ich bei sehr vielen C-Problemen, die hier immer und
> immer wieder besprochen werden, dass man sie in C++ nicht hätte. Und
> dass man die meisten dieser Programme einfach als C++ Code übersetzen
> könnte und dabei schrittweise die besseren Möglichkeiten dieser Sprache
> nutzen könnte. Deswegen weise ich darauf hin.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:

> Ich kann z.B. im gdb einen bedingten BP setzen im switch(): foo()==42

Im gdb mag das ja gehen. Es gibt aber durchaus noch andere Debugger, 
welche zeilenoriientiert arbeiten und es nicht hinbekommen, den 
return-Wert von foo() anzuzeigen, wenn dieses innerhalb von switch() 
steht.

> Das kann ich doch alles im switch erledigen. Wozu das if-stmt? Das ist
> für meinen Geschmack (s.o.) nun wieder das Gegenteil von KISS und
> Lokalität.

Ich kann und will das nicht im switch erledigen, denn:

foo() wird noch an 100 anderen Orten aufgerufen. Soll ich dann die 
Fehlerbehandlung an 100 Stellen machen? Wenn es 200 verschiedene 
negative Return-Werte gibt, soll ich die dann alle einzeln in genau 
diesem Switch (und an hundert anderen Orten) abfackeln? Und komm mir 
bitte nicht mit gcc-Erweiterungen, dass man hier ganze Zahlenbereiche im 
switch angeben kann. Das ist kein Standard.

Ein vorzeitiges:
1
if (rtc < 0)
2
{
3
    // evtl. myerror (rtc);
4
    return rtc;
5
}

ist genau die Lösung für das geschilderte Szenario. Gerade dann, wenn 
der 201te negative Wert später hinzukommt. Dann brauche ich nämlich an 
dieser Stelle gar nichts zu ändern!

> In der von mir genannten Art erzwingt die Schreibweise, dass der gesamte
> Wertebereich von foo() im switch abgedeckt ist / sein sollte.

Das ist nur sinnvoll, wenn Du foo() nur an dieser einen Stelle im 
Programm aufrufst, siehe oben. Das ist aber praxisfremd. Im Allgemeinen 
nutzt man Funktionen, damit man wiederkehrenden Code nur einmal 
schreiben und mehrfach von verschiedenen Stellen aus aufrufen kann.

: Bearbeitet durch Moderator
von Wilhelm M. (wimalopaan)


Lesenswert?

Frank M. schrieb:
> Wilhelm M. schrieb:
>
>> Ich kann z.B. im gdb einen bedingten BP setzen im switch(): foo()==42
>
> Im gdb mag das ja gehen. Es gibt aber durchaus noch andere Debugger,
> welche zeilenoriientiert arbeiten und es nicht hinbekommen, den
> return-Wert von foo() anzuzeigen, wenn dieses innerhalb von switch()
> steht.

Ja, die gibt es leider.
Und das ist der Grund, warum auch hier eben C++ besser ist:
1
if (auto result = foo(); result < 0) {
2
    handl_error(result);
3
}
4
else {
5
    switch(result) {
6
...
7
    }
8
}

Auch wenn Du es nicht magst, werde ich mir die Freiheit nehmen, darauf 
immer wieder hinzuweisen.

>> Das kann ich doch alles im switch erledigen. Wozu das if-stmt? Das ist
>> für meinen Geschmack (s.o.) nun wieder das Gegenteil von KISS und
>> Lokalität.
>
> Ich kann und will das nicht im switch erledigen:

die bessere Lösung steht oben

> foo() wird noch an 100 anderen Orten aufgerufen. Soll ich dann die
> Fehlerbehandlung an 100 Stellen machen?

Genau das wirst Du wohl machen müssen: die Fehlerbehandlung s.o.

> Wenn es 200 verschiedene
> negative Return-Werte gibt, soll ich die dann alle einzeln in genau
> diesem Switch (und an hundert anderen Orten) abfackeln?

Dazu hast Du hoffentlich eine eigene Funktion oder generischen 
Mechanismus, um Replication zu vermeiden ... alles andere wäre auch 
praxisfern.

> Und komm mir
> bitte nicht mit gcc-Erweiterungen, dass man hier ganze Zahlenbereiche im
> switch angeben kann. Das ist kein Standard.

Schon mal was von falltrough gehört? Naja, ist auch mal wider 
Standard-C++, sorry.

> Ein vorzeitiges:
>
1
> if (rtc < 0)
2
> {
3
>     // evtl. myerror (rtc);
4
>     return rtc;
5
> }
6
>
>
> ist genau die Lösung für das geschilderte Szenario. Gerade dann, wenn
> der 201te negative Wert später hinzukommt. Dann brauche ich nämlich an
> dieser Stelle gar nichts zu ändern!

Wenn Du es richtig machst, brauchst Du im switch auch nichts zu ändern. 
Und Du weißt sicher, dass man das so in C++ auch nicht machen müsste / 
würde (und die Lösung heisst jetzt nicht exception).

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Und das ist der Grund, warum auch hier eben C++ besser ist:

Irrelevant.

von Wilhelm M. (wimalopaan)


Lesenswert?

Frank M. schrieb:

> Bei
>
1
>   int rtc = foo ();
2
>   switch (rtc)
3
>   ...
4
>
>
> ist das einfach:
>
>
1
>   int rtc = foo ();
2
> 
3
> #ifdef DEBUG
4
>   printf ("rtc = %d\n", rtc);
5
> #endif
6
> 
7
>   switch (rtc)
8
>   ...
9
>

Und Du empfiehlst wahrscheinlich auch
1
bool rtc = is_even(x);
2
if (rtc) {
3
...
4
}

zu schreiben statt
1
if (is_even(x)) {
2
...
3
}

nehme nun ich an.

von Wilhelm M. (wimalopaan)


Lesenswert?

Nop schrieb:
> Wilhelm M. schrieb:
>
>> Und das ist der Grund, warum auch hier eben C++ besser ist:
>
> Irrelevant.

Der nächste C-Standard soll C2x (etwa 2022) sein. Und ich denke, dass 
viel von den "kleinen C++-Features" (s.o.) bis C++20 auch dort 
einfließen wird. Das ist natürlich noch lange hin und schade für die, 
die nicht willens sind, über den Tellerrand hinaus zu blicken.

von Carl D. (jcw2)


Lesenswert?

Wilhelm M. schrieb:
> Nop schrieb:
>> Wilhelm M. schrieb:
>>
>>> Und das ist der Grund, warum auch hier eben C++ besser ist:
>>
>> Irrelevant.
>
> Der nächste C-Standard soll C2x (etwa 2022) sein. Und ich denke, dass
> viel von den "kleinen C++-Features" (s.o.) bis C++20 auch dort
> einfließen wird. Das ist natürlich noch lange hin und schade für die,
> die nicht willens sind, über den Tellerrand hinaus zu blicken.

Verschwende einfach nicht deine Zeit mit denen im Suppenteller.
Es gibt eine Vielzahl von Nicks, bei denen man einfach sein 
Antwortbedürfnis unterdrücken muß.
Aber immer klappt das nicht, wie ich selber zugeben muß ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Carl D. schrieb:

>
> Verschwende einfach nicht deine Zeit mit denen im Suppenteller.
> Es gibt eine Vielzahl von Nicks, bei denen man einfach sein
> Antwortbedürfnis unterdrücken muß.

Ja, das ist wohl einerseits richtig. Andererseits gibt ja auch hier noch 
den TO und auch weitere stille Mitleser, die einfach auch mal etwas 
Neues erfahren wollen, statt ständig nur zu hören: das geht nur so, 
basta!

von Carl D. (jcw2)


Lesenswert?

Wilhelm M. schrieb:
> Carl D. schrieb:
>
>>
>> Verschwende einfach nicht deine Zeit mit denen im Suppenteller.
>> Es gibt eine Vielzahl von Nicks, bei denen man einfach sein
>> Antwortbedürfnis unterdrücken muß.
>
> Ja, das ist wohl einerseits richtig. Andererseits gibt ja auch hier noch
> den TO und auch weitere stille Mitleser, die einfach auch mal etwas
> Neues erfahren wollen, statt ständig nur zu hören: das geht nur so,
> basta!

Deshalb:
>> Aber immer klappt das nicht, wie ich selber zugeben muß ;-)

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Das ist natürlich noch lange hin und schade für die,
> die nicht willens sind, über den Tellerrand hinaus zu blicken.

C++ hat schon vor langer Zeit aufgehört, nützlich zu sein, so what.

von Einer K. (Gast)


Lesenswert?

Nop schrieb:
> C++ hat schon vor langer Zeit aufgehört, nützlich zu sein, so what.

Viele Werkzeuge sind nutzlos, in den Händen von Leuten, die damit nicht 
umgehen können oder wollen.
Auch ist z.B. ein Spaten nicht unbedingt hilfreich, beim Teppichboden 
verlegen, selbst wenn man sich mit Spaten gut auskennt.

von A. S. (Gast)


Lesenswert?

Arduino F. schrieb:
> Viele Werkzeuge sind nutzlos, in den Händen von Leuten, die damit nicht
> umgehen können oder wollen.

Das stimmt. Und ich freue mich ja auch über jedes Beispiel, wo irgendwas 
eleganter wird. Aber wieso wird hier wieder C++ als ultimativ 
hingestellt, ohne einen nachvollziehbaren Mehrwert zu zeigen. Was an

Wilhelm M. schrieb:
> Ja, die gibt es leider.
> Und das ist der Grund, warum auch hier eben C++ besser ist:
> if (auto result = foo(); result < 0) {
>     handl_error(result);
> }

soviel besser sein soll als
1
int result;
2
   
3
   if(result =foo(), result < 0){
4
       handle_error(result);
5
   }
Auch in C kann ich if-Klammern überfrachten. Ist es "auto"? Ist es der 
Scope von result? Die Deklarationszeile? Ja, C++ wird schneller 
verändert, hat mehr Freiheiten, erlaubt filigranere Verbiegungen, ... 
aber das hier tangiert die Eigenschaften der Unterschiede nicht 
wirklich.

von MaWin O. (Gast)


Lesenswert?

Wer bei
1
switch (func())
ein Problem sieht, hat die Kontrolle über sein Leben verloren.

von Wilhelm M. (wimalopaan)


Lesenswert?

Achim S. schrieb:
> Arduino F. schrieb:
>> Viele Werkzeuge sind nutzlos, in den Händen von Leuten, die damit nicht
>> umgehen können oder wollen.
>
> Das stimmt. Und ich freue mich ja auch über jedes Beispiel, wo irgendwas
> eleganter wird. Aber wieso wird hier wieder C++ als ultimativ
> hingestellt, ohne einen nachvollziehbaren Mehrwert zu zeigen.

Wie Du unten selbst zitierst, habe ich geschrieben, dass es besser geht, 
aber nicht als die nicht mehr zu verbessernde Lösung.

> Was an
>
> Wilhelm M. schrieb:
>> Ja, die gibt es leider.
>> Und das ist der Grund, warum auch hier eben C++ besser ist:
>> if (auto result = foo(); result < 0) {
>>     handl_error(result);
>> }
>
> soviel besser sein soll als
>
1
> int result;
2
> 
3
>    if(result =foo(), result < 0){
4
>        handle_error(result);
5
>    }
6
>

Der entscheidende, wenn auch bei diesem kleinen Beispiel eben zunächst 
klein ausfallende Unterschied ist die effektive Begrenzung des 
Gültigkeitsbereichs auf den Scope des if-stmts. Und das ist schon mal 
ein großer Vorteil, verhindert es doch effektiv das so oft vorkommende 
Recycling von Variablen aus Faulheit. Debuggen kann man es dann auch 
(s.o.), falls man einen schwachen Debugger hat.

> Auch in C kann ich if-Klammern überfrachten. Ist es "auto"?

Das "auto" kommt natürlich erst in generischem Code zum Tragen.

> Ist es der
> Scope von result? Die Deklarationszeile?

s.o.

von Carl D. (jcw2)


Lesenswert?

Achim S. schrieb:
> Arduino F. schrieb:
>> Viele Werkzeuge sind nutzlos, in den Händen von Leuten, die damit nicht
>> umgehen können oder wollen.
>
> Das stimmt. Und ich freue mich ja auch über jedes Beispiel, wo irgendwas
> eleganter wird. Aber wieso wird hier wieder C++ als ultimativ
> hingestellt, ohne einen nachvollziehbaren Mehrwert zu zeigen. Was an
>
> Wilhelm M. schrieb:
>> Ja, die gibt es leider.
>> Und das ist der Grund, warum auch hier eben C++ besser ist:
>> if (auto result = foo(); result < 0) {
>>     handl_error(result);
>> }
>
> soviel besser sein soll als
>
1
> int result;
2
> 
3
>    if(result =foo(), result < 0){
4
>        handle_error(result);
5
>    }
6
>
.
> Auch in C kann ich if-Klammern überfrachten. Ist es "auto"? Ist es der
> Scope von result? Die Deklarationszeile? Ja, C++ wird schneller
> verändert, hat mehr Freiheiten, erlaubt filigranere Verbiegungen, ...
> aber das hier tangiert die Eigenschaften der Unterschiede nicht
> wirklich.

Wenn result kein int ist, sondern etwas, das man wieder abräumen muß, 
dann lernt man "scope" schätzen. Und natürlich das bisschen mehr 
gegenüber C, weil der Compiler aufräumt.
auto übernimmt den Typ vom Ergebnis von foo(). Wenn man z.B. 
Raketen-Flugbahnen mal in INT und mal in double berechnet. Da kann auto 
die Lebensdauer des Fluggeräts deutlich erhöhen.
Man braucht das natürlich nicht, so wie man keinen Akkuschrauber braucht 
und keine CNC-Bearbeitungsstation. Letzteres ist auch nicht so einfach 
zu bedienen, wie eine Feile und viel teuerer, wird aber trotzdem 
gekauft.

von S. R. (svenska)


Lesenswert?

Nop schrieb:
>> Man könnte auch inline-assembly nehmen.
>
> Das wäre dann aber nicht mehr portabel. Computed goto funktioniert auf
> ARM genauso wie auf x86, wenn der Compiler es unterstützt.

Ich weise mal leise darauf hin, dass computed goto auf AVR nicht 
unterstützt wird.

von Nop (Gast)


Lesenswert?

S. R. schrieb:

> Ich weise mal leise darauf hin, dass computed goto auf AVR nicht
> unterstützt wird.

Interessant - wegen Harvard?

von Carl D. (jcw2)


Lesenswert?

S. R. schrieb:
> Nop schrieb:
>>> Man könnte auch inline-assembly nehmen.
>>
>> Das wäre dann aber nicht mehr portabel. Computed goto funktioniert auf
>> ARM genauso wie auf x86, wenn der Compiler es unterstützt.
>
> Ich weise mal leise darauf hin, dass computed goto auf AVR nicht
> unterstützt wird.

Komisch, benutze ich bei einer C++ Protothreads Implementierung mit g++ 
auf AVR.
Vielleicht weil ich mir den Compiler immer aus aktuellen Sourcen selbst 
baue und nicht einen WinAvr2010 benutze?

Edit: gerade gelesen: AVR-GCC kann keine Offsets zweier Labeladressn 
berechnen. Brauchte ich aber nicht.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Carl D. schrieb:
> S. R. schrieb:
>> Nop schrieb:
>>>> Man könnte auch inline-assembly nehmen.
>>>
>>> Das wäre dann aber nicht mehr portabel. Computed goto funktioniert auf
>>> ARM genauso wie auf x86, wenn der Compiler es unterstützt.
>>
>> Ich weise mal leise darauf hin, dass computed goto auf AVR nicht
>> unterstützt wird.
>
> Komisch, benutze ich bei einer C++ Protothreads Implementierung mit g++
> auf AVR.
> Vielleicht weil ich mir den Compiler immer aus aktuellen Sourcen selbst
> baue und nicht einen WinAvr2010 benutze?

Nein, denke genaues Lesen ist angesagt:

https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html

von A. S. (Gast)


Lesenswert?

> Gültigkeitsbereichs auf den Scope des if-stmts. Und das ist schon mal
> ein großer Vorteil, verhindert es doch effektiv das so oft vorkommende
> Recycling von Variablen aus Faulheit. Debuggen kann man es dann auch
> (s.o.), falls man einen schwachen Debugger hat.

Carl D. schrieb:
> Wenn result kein int ist

Das meine ich. Ich brauche unbedingt C++ (mit seinem ungleich größeren 
Sprachumfang), um für ein mögliches Debug-Szenario mit einem 
beschränkten Debugger den Scope einer general-Purpose int-Variable zu 
begrenzen. Das ist absurd!

Ja, in anderen Beispielen ist der Scope interessant und Destruktoren 
oder auto. Aber nicht hier! Es geht um switch, und da ist int immer 
richtig und gut! Egal was foo() zurückliefert.

von Carl D. (jcw2)


Lesenswert?

Wilhelm M. schrieb:
> Nein, denke genaues Lesen ist angesagt:
>
> https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html

Differenzen zwischen Label-Adressen gehen nicht, aber die braucht man 
weder für Protithreads, nicht für "Label-Arrays".
Auf alle Fälle ist "geht nicht" eine starke Verbiegung der Wahrheit.

von Wilhelm M. (wimalopaan)


Lesenswert?

Achim S. schrieb:
>> Gültigkeitsbereichs auf den Scope des if-stmts. Und das ist schon mal
>> ein großer Vorteil, verhindert es doch effektiv das so oft vorkommende
>> Recycling von Variablen aus Faulheit. Debuggen kann man es dann auch
>> (s.o.), falls man einen schwachen Debugger hat.
>
> Carl D. schrieb:
>> Wenn result kein int ist
>
> Das meine ich. Ich brauche unbedingt C++ (mit seinem ungleich größeren
> Sprachumfang), um für ein mögliches Debug-Szenario mit einem
> beschränkten Debugger den Scope einer general-Purpose int-Variable zu
> begrenzen. Das ist absurd!

Genau! Ich brauche C++, weil es C nicht kann! Das C das nicht kann ist 
absurd.

Was ist eine general-purpose int-Variable? Eine, die Du immer wieder 
recyclest?

> Ja, in anderen Beispielen ist der Scope interessant und Destruktoren
> oder auto. Aber nicht hier! Es geht um switch, und da ist int immer
> richtig und gut!

Ja, die alte Strategie: alles ist ein int oder ein C-String ... back to 
the 70s

> Egal was foo() zurückliefert.

Du willst also echt jeden Typ, den foo() liefert, auf ein int implizit 
wandeln. Oh, das nenne ich mal krass!

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Carl D. schrieb:
> Wilhelm M. schrieb:
>> Nein, denke genaues Lesen ist angesagt:
>>
>> https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html
>
> Differenzen zwischen Label-Adressen gehen nicht, aber die braucht man
> weder für Protithreads, nicht für "Label-Arrays".
> Auf alle Fälle ist "geht nicht" eine starke Verbiegung der Wahrheit.

Was ich damit sagen wollte: da hat wohl einer eben nicht so richtig 
gelesen ...

Beitrag #5423672 wurde von einem Moderator gelöscht.
von A. S. (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Du willst also echt jeden Typ, den foo() liefert, auf ein int implizit
> wandeln. Oh, das nenne ich mal krass!

Ich vermute, Du hast Dich ein bisschen verrannt oder meinen Beitrag nur 
überflogen. Ich möchte gar nichts, switch macht.

von Nop (Gast)


Lesenswert?

Carl D. schrieb:
> Wenn man z.B.
> Raketen-Flugbahnen mal in INT und mal in double berechnet.

Das ist dann ein Designbug, der geradezu nach Fehlern schreit. Genauso, 
wenn man den Datentyp beibehält, aber mal in m/s und mal in Knoten 
rechnet. Grund: "principle of the least surprise" verletzt.

Richtig macht man das so, daß man in einem einzigen Datenformat und 
einer einzigen Einheit rechnet, und zwar konsistent und überall. Bei 
Bedarf kann bei der Ausgabe in andere Datentypen oder Einheiten 
konvertiert werden.

von Wilhelm M. (wimalopaan)


Lesenswert?

Achim S. schrieb:
> Wilhelm M. schrieb:
>> Du willst also echt jeden Typ, den foo() liefert, auf ein int implizit
>> wandeln. Oh, das nenne ich mal krass!
>
> Ich vermute, Du hast Dich ein bisschen verrannt oder meinen Beitrag nur
> überflogen. Ich möchte gar nichts, switch macht.

Ich habe schon genau genau gelesen: Du möchtest gerne
1
double foo();
2
3
void test() {
4
   switch(foo()) {
5
   ...
6
   }
7
}

was aber selbst in C nicht geht.

von Wilhelm M. (wimalopaan)


Lesenswert?

Nop schrieb:
> Carl D. schrieb:
>> Wenn man z.B.
>> Raketen-Flugbahnen mal in INT und mal in double berechnet.
>

> Richtig macht man das so, daß man in einem einzigen Datenformat und
> einer einzigen Einheit rechnet, und zwar konsistent und überall.

Und wie stellst Du das sicher, wenn alles ein double ist?

Richtig ist, sich ein Typ-System zu schaffen, bei dem richtig gerechnet 
wird, also etwa:
1
auto distance = 13_km;
2
auto time = 1_sec;
3
4
auto speed = distance / time;

"Unsinnige" Operationen werden nicht realisiert. Dann kann fast nichts 
mehr schief gehen.

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

Ach ja, und ein switch ist für diskrete Zustände gedacht. Das für eine 
eigentlich kontinuierliche Größe wie eine Geschwindigkeit zu verwenden, 
wäre an sich schon ein Bug. Range-Checks macht man nicht mit Switch.

von Wilhelm M. (wimalopaan)


Lesenswert?

Nop schrieb:
> Ach ja, und ein switch ist für diskrete Zustände gedacht. Das für eine
> eigentlich kontinuierliche Größe wie eine Geschwindigkeit zu verwenden,
> wäre an sich schon ein Bug. Range-Checks macht man nicht mit Switch.

Lies nochmal genau: er bezogt sich auf mein Beispiel mit dem if-stmt.

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Und wie stellst Du das sicher, wenn alles ein double ist?

Die Magie des SI-Systems gewährleistet das schon von selber. Sicher kann 
man auch aus Versehen Zeit durch Weg teilen, wenn man Geschwindigkeit 
will, aber erstens ist das kein relevantes Szenario, und zweitens fällt 
sowas im Test mit Sicherheit auf - wenn nicht, hat man noch ganz andere 
Probleme.

> Richtig ist, sich ein Typ-System zu schaffen

Ja, Leute mit starkem Hang zu DSLs würden das tun. Es löst allerdings 
kein relevantes Problem, denn z.B. der Verlustder Mars-Sonde wäre schon 
verhindert worden, wenn alle überall mit demselben System gerechnet 
hätten.

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Lies nochmal genau: er bezogt sich auf mein Beispiel mit dem if-stmt.

Da wäre das aus einem noch ganz anderen Grund ein Bug gewesen: negative 
physikalische Werte als Fehler zu interpretieren wird in dem Moment zur 
Überraschung, wo aus irgendwelchen Gründen das System auf einmal einen 
Rückwärtsgang bekommt. Siehe das Beispiel der F-16, die auf der 
Südhalbkugel einfach mal in Rückenlage gedreht hat wegen negativer 
Breitengrade. Bzw. hätte, das wurde ja im Simulator noch rechtzeitig 
gefunden.

Richtig macht man das so, daß man erstmal die Eingabewerte auf gültige 
Bereiche validiert und dann einfach rechnen kann.

von Wilhelm M. (wimalopaan)


Lesenswert?

Nop schrieb:
> Wilhelm M. schrieb:
>
>> Und wie stellst Du das sicher, wenn alles ein double ist?
>
> Die Magie des SI-Systems gewährleistet das schon von selber.

C kann aber kein SI-System.

Du kannst es nur per Konvention bestimmen, dass Entfernung in Angström 
gemessen wird. Ob Deine Programmierer die richtigen Umrechnungsfaktoren 
verwenden, kannst Du nicht sicher sein.

> Sicher kann
> man auch aus Versehen Zeit durch Weg teilen, wenn man Geschwindigkeit
> will, aber erstens ist das kein relevantes Szenario, und zweitens fällt
> sowas im Test mit Sicherheit auf - wenn nicht, hat man noch ganz andere
> Probleme.

Doch, genau so etwas ist ein Problem. Und Du kannst es verhindern, weil 
es einfach nicht kompiliert.

>> Richtig ist, sich ein Typ-System zu schaffen
>
> Ja, Leute mit starkem Hang zu DSLs würden das tun. Es löst allerdings
> kein relevantes Problem, denn z.B. der Verlustder Mars-Sonde wäre schon
> verhindert worden, wenn alle überall mit demselben System gerechnet
> hätten.

Haben sie aber nicht, weil der Compiler sie dazu nicht gezwungen hat. 
Due drehst Dich im Kreis mit Deinen Aussagen.

von Carl D. (jcw2)


Lesenswert?

@Wilhelm M.:
Um dies zu verstehen, muß man sich weiter in die Sprache reinlehnen, als 
die meisten hier bereit sind. Da kann ich dann sogar verstehen, daß man 
die Vorzüge nicht wahrnimmt. Vielleicht kann man schlicht nicht Glauben, 
was da möglich ist. Oder es ist halt doch zu kompliziert für NOP's.

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Du kannst es nur per Konvention bestimmen, dass Entfernung in Angström
> gemessen wird.

Angström ist keine SI-Einheit.

> Ob Deine Programmierer die richtigen Umrechnungsfaktoren
> verwenden, kannst Du nicht sicher sein.

Du kannst nie sicher sein, daß Deine Programmierer irgendwas richtig 
machen. Nichtmal in ADA.

> Doch, genau so etwas ist ein Problem.

Ein Spielzeugproblem. Das reale Beispiel, was ich nannte, wäre schon 
durch Konsistenz im Projekt verhindert worden.


Carl D. schrieb:
>  Oder es ist halt doch zu kompliziert für NOP's.

Damit, daß die Abstraktion von C++ eher Obfuscation ist, stehe ich nicht 
alleine. Wenn Du schon auf der persönlichen Schiene argumentieren willst 
- da ist z.B. jemand, der den Kernel für ein recht prominentes OS 
entwickelt hat. Aber der ist im Vergleich zu Dir natürlich auch nur ein 
Armleuchter, schon klar.

von Wilhelm M. (wimalopaan)


Lesenswert?

Carl D. schrieb:
> @Wilhelm M.:
> Um dies zu verstehen, muß man sich weiter in die Sprache reinlehnen, als
> die meisten hier bereit sind. Da kann ich dann sogar verstehen, daß man
> die Vorzüge nicht wahrnimmt. Vielleicht kann man schlicht nicht Glauben,
> was da möglich ist. Oder es ist halt doch zu kompliziert für NOP's.

Wahrscheinlich hast Du recht :-(

Daher der Spruch: "stop teaching C", den ja auch viele falsch verstehen. 
Die Kernelemente in der Programmierung sind ADTs, aber C "lehrt" eine 
Denkweise von der technischen Repräsentation her und bürdet dem 
Programmierer die Last der Interpretation auf. Leider verliert man 
dadurch eben vieles von dem, was man eigentlich die Maschine machen 
lassen kann / will.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Nop schrieb:

> Ein Spielzeugproblem. Das reale Beispiel, was ich nannte, wäre schon
> durch Konsistenz im Projekt verhindert worden.

Und wie? Durch große Poster an der Wand? Durch eine Project-Charta?

Mach doch bitte einmal die Mühe, und denke darüber nach, was ich oben 
gesagt habe.

von Carl D. (jcw2)


Lesenswert?

Nop schrieb:.
>
> Damit, daß die Abstraktion von C++ eher Obfuscation ist, stehe ich nicht
> alleine. Wenn Du schon auf der persönlichen Schiene argumentieren willst
> - da ist z.B. jemand, der den Kernel für ein recht prominentes OS
> entwickelt hat. Aber der ist im Vergleich zu Dir natürlich auch nur ein
> Armleuchter, schon klar.

Der hat dafür gute Gründe, die ich nachvollziehen kann. Die sind aber 
nicht "C++ ist zu kompliziert", sondern technischer Art im Umfeld 
Kernel-Entwicklung.
Das ist die gleiche Argumentsweise, 5worte aus 5Seiten Text picken, wie 
weiter oben stand "geht nicht bei AVR", ohne daß der Schreiber die GCC 
Doku nur ansatzweise verstanden hatte.
Er, der große Unbenannte bezieht sich auf einen Teilbereich, Exceptions, 
den man nicht nutzen muß. Auf AVR's gibt es keine Exceptions, aber 
vieles ander, was das Leben erleichtern kann.
Aber, und da muß ich sogar Moby recht geben, es geht auch in Assembler. 
Wenn man's braucht.

von A. S. (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Du möchtest gerne

Vielleicht solltest Du ne Nacht drüber schlafen, und morgen nach einem 
Kaffee nochmal lesen...

Wilhelm M. schrieb:
> distance = 13_km;

Die ganze Diskussion zum rechnen mit Einheiten (und entsprechende 
Programmiersprachen) ist übrigens älter als C.

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Und wie? Durch große Poster an der Wand?

Wenn es das ist, was Du unter einer Projekt-Spezifikation verstehst, 
dann schon.


Carl D. schrieb:

> Der hat dafür gute Gründe, die ich nachvollziehen kann. Die sind aber
> nicht "C++ ist zu kompliziert", sondern technischer Art im Umfeld
> Kernel-Entwicklung.

Beileibe nicht nur - und zudem ist das Argument mit den Exceptions auch 
Unsinn, weil man die nicht benutzen MUSS. Es wird aber indirekt doch ein 
Schuh draus, denn sobald man C++ benutzt, wird es ein ständiger 
Abwehrkampf, weil irgendwelche Freaks immer eine Ausrede finden, wieso 
sie ihr Lieblingsfeature einbauen müssen. Das Ergebnis, wenn man nicht 
konsequent Subsetting betreibt und das auch durchsetzt, ist eine 
unwartbare Codebasis.

Du unterschlägst außerdem auch seine Kommentare zu STL und Boost, die in 
genau diese Richtung gehen. Und natürlich, daß er ganz bewußt keine 
Leute zur systemnahen Programmierung, und auch nicht für Git, haben 
wollte, sondern Leute, die reale Computer auf Bytelevel programmieren 
können. Er drückte es so aus, selbst wenn man durch Verwendung von C 
lediglich C++-Programmierer fernhalte, sei allein das schon den Einsatz 
von C wert.

Wenn ich mir hier so manches durchlese, hat Torvalds das zwar auf seine 
liebenswerte Weise verpackt, aber es steckt ein wahrer Kern dahinter.

von Carl D. (jcw2)


Lesenswert?

Wilhelm M. schrieb:
> Nop schrieb:
>
>> Ein Spielzeugproblem. Das reale Beispiel, was ich nannte, wäre schon
>> durch Konsistenz im Projekt verhindert worden.
>
> Und wie? Durch große Poster an der Wand? Durch eine Project-Charta?
>
> Mach doch bitte einmal die Mühe, und denke darüber nach, was ich oben
> gesagt habe.

Wer noch nie gesehen hat, daß der Compiler sagt "Apfel durch Birnen ist 
keine erlaubte Operation", der wird das nie für möglich halten.
Aber mit den Worten des Herrn Schubert: "ich kann mich nicht um alles 
kümmern!"

von Carl D. (jcw2)


Lesenswert?

Nop schrieb:
> Wilhelm M. schrieb:
>
>> Und wie? Durch große Poster an der Wand?
>
> Wenn es das ist, was Du unter einer Projekt-Spezifikation verstehst,
> dann schon.
>
>
> Carl D. schrieb:
>
>> Der hat dafür gute Gründe, die ich nachvollziehen kann. Die sind aber
>> nicht "C++ ist zu kompliziert", sondern technischer Art im Umfeld
>> Kernel-Entwicklung.
>
> Beileibe nicht nur - und zudem ist das Argument mit den Exceptions auch
> Unsinn, weil man die nicht benutzen MUSS. Es wird aber indirekt doch ein
> Schuh draus, denn sobald man C++ benutzt, wird es ein ständiger
> Abwehrkampf, weil irgendwelche Freaks immer eine Ausrede finden, wieso
> sie ihr Lieblingsfeature einbauen müssen. Das Ergebnis, wenn man nicht
> konsequent Subsetting betreibt und das auch durchsetzt, ist eine
> unwartbare Codebasis.
.
> Du unterschlägst außerdem auch seine Kommentare zu STL und Boost, die in
> genau diese Richtung gehen.
Man findet auch C-Bibliotheken, die man missbrauchen.

> Und natürlich, daß er ganz bewußt keine
> Leute zur systemnahen Programmierung, und auch nicht für Git, haben
> wollte, sondern Leute, die reale Computer auf Bytelevel programmieren
> können. Er drückte es so aus, selbst wenn man durch Verwendung von C
> lediglich C++-Programmierer fernhalte, sei allein das schon den Einsatz
> von C wert.
Das klingt eher nach Stuhl festhalten. Angst ist ein schlechter 
Ratgeber.

> Wenn ich mir hier so manches durchlese, hat Torvalds das zwar auf seine
> liebenswerte Weise verpackt, aber es steckt ein wahrer Kern dahinter.

Hoffentlich sagt ihm keiner, daß sein C-Code von C++-Programmen 
übersetzt wird. Wahlweise GCC oder clang. Es scheint also möglich große 
Software-Pakete in C++ zu schreiben. Nur offenbar nicht überall und mit 
jedem Projektleiter. Muß man ja auch nicht. Darf man aber unbemecktert 
können dürfen.

von Heiko L. (zer0)


Lesenswert?

Carl D. schrieb:
> Hoffentlich sagt ihm keiner, daß sein C-Code von C++-Programmen
> übersetzt wird.

Autsch...

----
https://madnight.github.io/githut/#/pull_requests/2018/1
Was will man noch diskutieren? Die Sprache der Spachen ist....
JavaScript

: Bearbeitet durch User
von Carl D. (jcw2)


Lesenswert?

Heiko L. schrieb:
> Carl D. schrieb:
>> Hoffentlich sagt ihm keiner, daß sein C-Code von C++-Programmen
>> übersetzt wird.
>
> Autsch...

Falls das ein kritisches ätsch war:
 clang immer schon,
 GCC seit Version 6 (WIMRE)

Beim letzten hab ich dem erst heut wieder zugeschaut, wie er sich selber 
übersetzt. Unter dem Name g++ gestartet.

von Heiko L. (zer0)


Lesenswert?

Carl D. schrieb:
> Beim letzten hab ich dem erst heut wieder zugeschaut, wie er sich selber
> übersetzt. Unter dem Name g++ gestartet.

Ja, und? Ist doch alles das selbe Backend. Und das besteht aus 
verdächtig vielen Dateien mit .c-Endung.

Die Rationale ist da auch ganz einfach: Zum Bootstrappen braucht man 
einen Compiler. Und ein C-Compiler ist in Assembler um einiges leichter 
zu schreiben....

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

Mal was anderes - das Forum braucht abartig lange, um diesen Thread zu 
laden, und das ist auch bei anderen langen Threads so. Von "klick" bis 
"Forum springt zum ersten neuen Beitrag" vergehen mehrere Sekunden.

Nur bei mir, oder geht Euch das auch so?

von Heiko L. (zer0)


Lesenswert?

> Nur bei mir, oder geht Euch das auch so?
Bei mir ist alles normal.

Beitrag #5423789 wurde vom Autor gelöscht.
von Nop (Gast)


Lesenswert?

Heiko L. schrieb:

> Die Rationale ist da auch ganz einfach: Zum Bootstrappen braucht man
> einen Compiler.

Da kann man heute aber auch einfach einen Crosscompiler von irgendeiner 
anderen Plattform nehmen.

von Carl D. (jcw2)


Lesenswert?

Heiko L. schrieb:
> Carl D. schrieb:
>> Beim letzten hab ich dem erst heut wieder zugeschaut, wie er sich selber
>> übersetzt. Unter dem Name g++ gestartet.
>
> Ja, und? Ist doch alles das selbe Backend. Und das besteht aus
> verdächtig vielen Dateien mit .c-Endung.

Es gibt noch "etwas" Code zwischen Parser und Backend. Der macht die 
ganze Optimierung und ist C++. Aber muß ich nicht weiter diskutieren.

> Die Rationale ist da auch ganz einfach: Zum Bootstrappen braucht man
> einen Compiler. Und ein C-Compiler ist in Assembler um einiges leichter
> zu schreiben....
Hä?? Was hat das mit dem Thema zu tun? Ich brauche kein Bootrtapp, meine 
Kiste hat einen lauffähigen GCC, der sich selbst übersetzen kann. Und 
das von mir benötigte Bachend ist auch schon in der Standard-Source 
drin. Oder ist das Füllmaterial?

von Heiko L. (zer0)


Lesenswert?

Nop schrieb:
> Heiko L. schrieb:
>
>> Die Rationale ist da auch ganz einfach: Zum Bootstrappen braucht man
>> einen Compiler.
>
> Da kann man heute aber auch einfach einen Crosscompiler von irgendeiner
> anderen Plattform nehmen.

Richtig. Dann muss die Wahl der Sprache wohl noch einen anderen Grund 
haben.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Und Du empfiehlst wahrscheinlich auchbool rtc = is_even(x);
> if (rtc) {
> ...
> }
>
> zu schreiben statt if (is_even(x)) {
> ...
> }
>
> nehme nun ich an.

Nein. Das ist doch nur ein simples if und kein switch. Der return-Wert 
kann ja nur 2 Werte annehmen. Wieso schiebst Du mir so etwas unter?

Ich persönlich rufe auch triviale Funktionen innerhalb von 
C-Kontrollstrukturen wie if und switch auf. Zum Beispiel ist das simple 
"Kopierprogramm" von K&R, welches im Buch bereits kurz nach das "Hello, 
World" aufgeführt wird, einfach genial:
1
    while ((c = getchar()) != EOF)
2
    {
3
        putchar (c);
4
    }

Die Zuweisung (inkl. Funktionsaufruf!) innerhalb der Bedingung löst 
genau das Problem, das hier andere Programmiersprachen mit 
while-Schleifen haben. Wäre so eine Zuweisung nicht möglich, müsste man 
umständlich schreiben:
1
    c = getchar();
2
3
    while (c != EOF)
4
    {
5
        putchar (c);
6
        c = getchar ();
7
    }

Hier wäre der getchar()-Aufruf doppelt.

oder als do-while-Schleife:
1
    do
2
    {
3
        c = getchar ();
4
5
        if (c != EOF)
6
        {
7
            putchar (c);
8
        }
9
    } while (c != EOF);

Hier wäre die Abfrage doppelt.

Zurück zum Thema:

Du fragtest oben nach Vorteilen, die Funktion nicht in den switch() zu 
schreiben. Ich hatte Dir lediglich einige genannt, die unter Umständen 
vorteilhaft sein können.

Deine Schlussfolgerung, dass ich persönlich grundsätzlich keine 
Funktionsaufrufe innerhalb von if, while, switch mache, ist jedoch 
ziemlich weit hergeholt und auch unzutreffend.

Ich kann Dir versichern: Wenn die Funktion bzgl. Aufruf und Semantik 
hinreichend trivial ist, rufe ich sie auch innerhalb switch() auf. ;-)

Also: Ich mache keine Religion daraus.

Deine sportliche Anstrengung, den Scope von Variablen möglichst klein zu 
halten, um Wiederverwendung derselben auszuschließen, in allen Ehren. 
Aber das ist eher eine Frage der Disziplin des Programmierers, nicht der 
Programmiersprache.

P.S.
Zu meiner Person: Ich programmiere seit 1984 in C unter UNIX und habe in 
den 90ern auch viele Jahre in C++ programmiert. Den Stroustrup habe ich 
damals dafür rauf und runtergeackert. Ich mag beide Sprachen. Aber ich 
würde niemals wegen einer so simplen Frage, wie sie der TO gestellt hat, 
auf die Idee kommen, ihm den Wechsel von C auf C++ anzuraten.

Das ist nicht nur kontraproduktiv, sondern auch abschreckend. Du 
erreichst mit Deiner "Missionarsarbeit" durchaus Ablehnung, weil Du 
ziemlich hartnäckig sein kannst. Die Bezeichnung "SPAM", die hier 
gefallen ist, ist da nicht ganz unzutreffend. Wer will auch alle paar 
Stunden einen Zeugen Jehowas an der Türe klopfen hören, nur weil er 
gesehen hat, dass ich täglich an einer Kirche vorbeigehe?

Mit Tellerrändern, die hier auch aufgeführt wurden, hat das ebenso 
überhaupt nichts zu tun. Man muss aber nicht immer direkt ein Flugzeug 
chartern, wenn man beim Aldi einkaufen gehen will.

: Bearbeitet durch Moderator
von batman (Gast)


Lesenswert?

Frank M. schrieb:
> Ich kann Dir versichern: Wenn die Funktion bzgl. Aufruf und Semantik
> hinreichend trivial ist, rufe ich sie auch innerhalb switch() auf. ;-)

Aha. Es soll ja auch Leute geben, die schachteln gleich 3 oder sogar 4 
(vier) Funktionen ineinander. Boooaaaaaaa! :)

von Nop (Gast)


Lesenswert?

batman schrieb:

> Aha. Es soll ja auch Leute geben, die schachteln gleich 3 oder sogar 4
> (vier) Funktionen ineinander. Boooaaaaaaa! :)

Nennt sich "functional programming". ;-)

von Einer K. (Gast)


Lesenswert?

batman schrieb:
> Aha. Es soll ja auch Leute geben, die schachteln gleich 3 oder sogar 4
> (vier) Funktionen ineinander. Boooaaaaaaa! :)

Habe letztens folgenden Ausdruck verfasst:
1
void loop() 
2
{
3
  led = blinkomat = flankenerkennung = entprellen = taster;
4
}
Gab auch witzige Kommentare....

von Nop (Gast)


Lesenswert?

Arduino F. schrieb:

> Gab auch witzige Kommentare....

Zurecht.

von S. R. (svenska)


Lesenswert?

Carl D. schrieb:
> Das ist die gleiche Argumentsweise, 5worte aus 5Seiten Text picken, wie
> weiter oben stand "geht nicht bei AVR", ohne daß der Schreiber die GCC
> Doku nur ansatzweise verstanden hatte.

Gut, dann korrigiere ich:
Computed Goto wird auf AVR-GCC nur eingeschränkt unterstützt.

Und nun? Wer den vollen Umfang nutzen will, rennt gegen eine Wand. Wer 
ein Subset nutzen will, muss vorher untersuchen, ob das korrekt 
implementiert ist - UND DAS IST ARCHITEKTUR-SPEZIFISCH.

Klar?

von Wilhelm M. (wimalopaan)


Lesenswert?

Frank M. schrieb:

> Ich persönlich rufe auch triviale Funktionen innerhalb von
> C-Kontrollstrukturen wie if und switch auf. Zum Beispiel ist das simple
> "Kopierprogramm" von K&R, welches im Buch bereits kurz nach das "Hello,
> World" aufgeführt wird, einfach genial:
>
1
>     while ((c = getchar()) != EOF)
2
>     {
3
>         putchar (c);
4
>     }
5
>

Ich habe das Snippet, was Du als geniales Stück Code bezeichnest, einmal 
zu einem Miniprogramm ergänzt, wie Du es evtl. auch gemacht hättest:
1
#include <stdio.h>
2
3
int main(){
4
    int c = EOF;
5
    while((c = getchar()) != EOF) {
6
        if (putchar(c) == EOF) {
7
     return 1;
8
  }
9
    }
10
    return 0;
11
}

Natürlich ist Dir und vielen anderen hier als sehr erfahrende C-Gurus 
sofort klar, was hier abläuft. Schaut man aber mal genau hin, so stellt 
man fest, dass es doch schlampig umgesetzt ist. Und deswegen halte ich 
dieses Programm als einführendes Beispiel für ziemlich ungeeignet. Und 
zwar aus den folgenden Gründen. Ich bin jetzt etwas sehr pingelig - 
zugegeben -, aber ich will das eigentliche Problem deutlich machen. Und 
ja, dies ist kein Spezifikum von C / libc, sondern wir finden es auch in 
C++ / libstdc++ wieder, vor allem in pre-C++11-Code. Aber man kann es 
eben besser machen. Und das sollte der Anspruch sein, wenn man solche 
Diskussionen wie in diesem Thread hat. Es geht hier nicht um 
Missionierung, sondern darum, was den Kern der Programmierung ausmacht.


1) Es wird in main() im äußeren Block die Variable c des Typs int 
definiert und mit EOF initialisiert. Gut, das bedeutet, dass der Inhalt 
von c als ganzzahliger, vorzeichenbehafteter numerischer und 
semantikloser Typ deklariert wird, der den lokalen Zustand des in main() 
realisierten Algorithmus repräsentiert. Ich erwarte also einen 
arithmetischen Umgang mit c. Die Initialisierung mit EOF ist aber schon 
recht zweifelhaft. "EOF: integer constant of type int and negative 
value" steht in der Doku der libc. Bedeutung von EOF: end-of-file. Was 
hat dieser Sentinel-Wert mit ganzzahliger Arithmetisch zu tun? Was würde 
etwa (EOF + 42) bedeuten?


2) Der Gültigkeitsbereich der Variable c ist der Funktionsblock main(). 
Auf dieser Ebene wird c aber gar nicht mehr verwendet. Hat also der 
Ersteller des Codes hier etwas vergessen? Ist das Programm gar 
fehlerhaft? Das ist auf den schnellen Blick nicht zu erkennen. Das ist 
verwirrend.


3) Es wird "int getchar()" aufgerufen. Die Doku sagt, es wird ein 
Zeichen von stdin gelesen. Soweit so gut: aber, warum ist der Typ der 
Funktion dann int und nicht etwa char, unsigned char, oder sonst was? 
Ich möchte doch ein Zeichen lesen. Es gibt die Möglichkeit, dass der 
Name getchar() einfach nicht zur Semantik passt und besser getNumber() 
heissen sollte. Das könnte den Typ int erklären und ich könnte evtl. 
Zahlen im Wertebereich eines ints von stdin parsen. Oder es wird ein 
UTF-16 o.ä. Zeichen gelesen?


4) Genauere Betrachtungen ergeben, das der Typ von getchar() nur 
deswegen int ist, damit der negative Wert des Sentinels EOF 
repräsentiert werden kann. Auch das ist sehr undurchsichtig. Ich habe 
den Typ int für ein Zeichen + Sentinel. Das könnte bedeuten, dass von 
den mindestens 2^16 Werten des int nur (2^7 + 1) bzw. (2^8 + 1) zulässig 
sind. Alles andere sind eher trap-representations. Warum sollte ich mit 
dem Wert von getchar() Berechnungen wie (3 * c + 300) / 42 anstellen 
können. Unklar.


5) getchar() sollte aber die Semantik besitzen: liefere ein Zeichen, 
wenn es da ist, oder etwas ungültiges bzw. nichts, wenn ein Fehler 
auftrat. Ich brauche also einen Datentyp, mit dem ich jedes gültige 
Zeichen und den ungültigen Zustand repräsentieren kann. Arithm. 
Operationen für Zeichen sind Unsinn. Operationen zur Konvertierung in 
korrespondierende Klein-/Großbuchstaben sinnvoll.


6) Bei "int putchar(int)" ergibt sich dasselbe Bild: warum ist der 
Argumenttyp ein int? Ich möchte doch ein Zeichen ausgeben. Oder gibt 
putchar(int) den int-Wert als ganze vorzeichenbehaftete Zahl zur Basis 
10 aus? Der Name putchar und der Argumenttyp widersprechen sich auch 
hier.


7) Der Wert von putchar() wird eigentlich nur gebraucht, um Miss-/Erfolg 
anzuzeigen. Zwei Zustände, dafür gibt es bool. Man fragt sich sofort, 
welche Werte können denn noch auftauchen? Und welche Bedeutung haben 
sie? Warum sollte ich auch hier mit dem Wert von putchar() Arithmetik 
betreiben können? Die Doku sagt aus, das der Argumentwert intern auf 
unsigned char gewandelt wird und dieser Wert dann zusammen mit ggf. dem 
Sentinel auftauchen kann. Mindestens verwirrend.

Man kann daraus im wesentlichen zwei Sachen lernen.

I) Datentypen sind dazu da, die notwendigen Entitäten im Code möglichst 
exakt abzubilden. Entsprechen die Wertebereiche und/oder möglichen 
Operationen nicht den Erwartungen, ist das mindestens sehr verwirrend.

II) Namen sind nicht unwesentlich.

Ein Code ist nur dann klar, wenn I und II konsistent zueinander passen. 
Bei "int getchar()" und "int putchar(int)" ist das nicht gegeben. Zudem 
ist int als unspezifischer Typ zur Darstellung eines Zeichens in einem 
7- oder 8-bit-Code unsinnig.

Die für manche nun unangenehme Erkenntnis ist, dass wir I in C nicht 
zufriedenstellend erreichen können, und damit die wichtige Kombination 
aus I und II in C scheitert.
Besserer Code kann nur gelingen, wenn

a) Datentypen entsprechend der Problemdomäne geschaffen werden können 
(UDT).

b) Diese Datentypen (UDT) durch sinnvolle Wahl der möglichen Operationen 
(Erzeugung, Kopier- und Konvertierungsverhalten, intrinsische und 
zusätzliche Operationen, Zerstörung, etc.) und Namen eine Semantik 
bekommen.

c) Die UDT möglichst nahtlos in die restliche Sprache eingebettet sind.

d) Die Sprache so gestaltet ist, das die Intention des Autors optimal 
wiedergegeben werden kann.

Die Sprache C++ macht das mittlerweile zu einem großen Maße ganz gut 
(das war aber vor C++11 nicht so):

i) Man kann eigene Typen erstellen und sie fast vollständig in die 
Struktur der Sprache einbetten, d.h die instrinsichen Operationen der 
Sprache und zusätzliche ermöglichen oder nicht, etc. Dabei drängt man 
die Bedeutung der intrinsichen Typen soweit wie möglich zurück, denn 
intrinsiche Typen entfalten kein Semantik (Beispiel: double als Meter 
oder Kilometer).

ii) Dabei kann man die Mächtigkeit der Typen vollkommen steuern: was ist 
wie erzeugbar, was kann man kopieren oder nur verschieben, was passiert 
beim Zerstören, welcher Wertebereiche / Zustände sind zulässig, welche 
Konvertierung sollen ausführbar sein, etc.

iii) Durch Meta-Programmierung können Datentypen ausgehend von den 
Eigenschaften anderer Datentypen ermittelt werden, etwa: was ist der 
kleinste aber ausreichende Typ für die Indizierung aller Elemente in 
einem Container (hat in diesem o.g. Beispiel allerdings keine 
Bedeutung).

iv) ...

Als C++-Programm könnte das obige Beispiel etwa so aussehen, z.B. für 
7-Bit Ascii-Zeichen oder unspezifische Bytes:
1
int main(){
2
    while(auto c = get<AsciiChar>()) {
3
        if (!put(*c)) {
4
            return 1;
5
        }
6
  // c *= 3; // nicht sinnvoll
7
  // c <<= 2; // nicht sinvoll
8
    }
9
    // hier kein c mehr existent
10
    
11
    while(auto c = get<std::byte>()) {
12
        if (!put(*c)) {
13
     return 1;
14
        }
15
  // c *= 3; // nicht sinnvoll
16
  // c <<= 2; // sinnvoll und machbar
17
    }
18
    return 0;
19
}

Zum nachvollziehen ist dazu folgendes noch notwendig:
1
struct AsciiChar {
2
    explicit AsciiChar(unsigned char v) : value(v) {
3
        assert(value < 128);
4
    }
5
    unsigned char value{};
6
};
7
8
template<typename T> std::optional<T> get();
9
10
template<>
11
std::optional<AsciiChar> get<AsciiChar>() {
12
    if (int c = getchar(); c != EOF) {
13
        return AsciiChar{static_cast<unsigned char>(c)};
14
    }    
15
    return {};
16
}
17
template<>
18
std::optional<std::byte> get<std::byte>() {
19
    if (int c = getchar(); c != EOF) {
20
        return std::byte(c);
21
    }    
22
    return {};
23
}
24
25
bool put(AsciiChar c) {
26
    return putchar(c.value) != EOF;
27
}
28
bool put(std::byte c) {
29
    return putchar(static_cast<int>(c)) != EOF;
30
}

Wobei die Verwendung des std::optional<> zur Externalisierung des 
invaliden Zustandes bei AsciiChar natürlich nicht notwendig ist (man 
hätte auch einen Typumwandlungsoperator nach bool schreiben können), 
aber bei std::byte praktischer (falls man den DT std::byte aus der 
libstdc++ verwendet). Der generische DT std::optional<> ist sinnvoll in 
die Sprache eingebettet, folgt er doch dem Zeiger-Idiom (Zeigertypen 
sind die einzigen Typen, die einen ungütigen Wert besitzen, und dienen 
deswegen als Orientierung mit der impl. Wandlung nach bool).

Wie man sieht, kommen hier nur wenige Komponenten der 
objekt-orientierten Programmierung im engeren Sinn zur Anwendung. Wohl 
aber

1) werden UDT definiert, die möglichst genau dem Einsatzzweck 
entsprechen (mögliche Werte, ungültiger Zustand),

2) die möglichen Operationen der Typen wird genau festgelegt 
(Elementfunktionen, freie Funktionen),

3) die Expressivität auf der Anwendungsseite wird durch
3.1) Einschränkung des Gültigkeitsbereiches
3.2) Funktionsüberladung
3.3) generischen Code und Typinferenz gesteigert.

4) die Sicherheit wird erhöht, weil unsinnige Operationen nicht möglich 
sind.

Natürlich ist das obige noch nicht optimal, aber m.E. schon ein Schritt 
in die richtige Richtung. Summa summarum ist also der Schritt von 
gewöhnlichem C zur dieser Art von prozeduralem, expressivem Code in C++ 
nicht besonders groß.

(Ups, Ende, ICE hält gleich ...)

Beitrag #5424457 wurde von einem Moderator gelöscht.
Beitrag #5424593 wurde von einem Moderator gelöscht.
von S. R. (svenska)


Lesenswert?

Wilhelm M. schrieb:
> Aber man kann es eben besser machen. Und das sollte der Anspruch
> sein, wenn man solche Diskussionen wie in diesem Thread hat.

In erster Linie sollte man es richtig machen. Und das heißt, dass man 
die zu verwendende API auch korrekt verwendet - oder eine bessere API 
entwickelt.

> 1) Es wird in main() im äußeren Block die Variable c des Typs int
> definiert und mit EOF initialisiert.

Sinnvoll.

> Ich erwarte also einen arithmetischen Umgang mit c. [...]
> Bedeutung von EOF: end-of-file. Was hat dieser Sentinel-Wert
> mit ganzzahliger Arithmetisch zu tun?

Nichts.

> Was würde etwa (EOF + 42) bedeuten?

Ist eine Zahl. Kann positiv oder negativ sein.
Ist implementation-defined und entsprechend dokumentiert.

> 2) Der Gültigkeitsbereich der Variable c ist der Funktionsblock main().
> Auf dieser Ebene wird c aber gar nicht mehr verwendet.

Das tut mir aber Leid. Mach einen Block drum, wenn's dich stört.

> Hat also der Ersteller des Codes hier etwas vergessen?
> Ist das Programm gar fehlerhaft?

Die Frage gilt für jeden Code.

> Das ist auf den schnellen Blick nicht zu erkennen. Das ist
> verwirrend.

Wenn du von so einem Trivialprogramm verwirrt bist, dann sind 
domänenspezifische Programmiersprachensubsets vielleicht eher was für 
dich?

> 3) Es wird "int getchar()" aufgerufen. Die Doku sagt, es wird ein
> Zeichen von stdin gelesen. Soweit so gut: aber, warum ist der Typ der
> Funktion dann int und nicht etwa char, unsigned char, oder sonst was?

Weil du die Doku nicht ordentlich gelesen hast.

> Ich möchte doch ein Zeichen lesen. Es gibt die Möglichkeit, dass der
> Name getchar() einfach nicht zur Semantik passt und besser getNumber()
> heissen sollte.

Der Name passt zur Semantik, du definierst dir nur gerade irgendwas 
zurecht.

> Das könnte den Typ int erklären und ich könnte evtl.
> Zahlen im Wertebereich eines ints von stdin parsen.

Zahlen und Zeichen sind verschiedene Dinge.

> Oder es wird ein UTF-16 o.ä. Zeichen gelesen?

Die Doku redet nicht von UTF-16, also wird es höchstwahrscheinlich auch 
keins sein.

> 4) Genauere Betrachtungen ergeben, das der Typ von getchar() nur
> deswegen int ist, damit der negative Wert des Sentinels EOF
> repräsentiert werden kann. Auch das ist sehr undurchsichtig.

Das ist nicht undurchsichtig, sondern schlicht so definiert.
Da kannst du auf und niedrig hüpfen oder deine eigene API bauen, aber du 
wirst dieses Problem auch irgendwie lösen müssen (zweiter Rückgabewert, 
Exceptions o.ä.). Sämtliche deiner Lösungen werden auf Maschinen mit 
mehr als 8 Bit weniger effizient sein.

> Ich habe den Typ int für ein Zeichen + Sentinel.
> Das könnte bedeuten, dass von den mindestens 2^16 Werten
> des int nur (2^7 + 1) bzw. (2^8 + 1) zulässig sind.

Korrekt.

> Alles andere sind eher trap-representations. Warum sollte ich mit
> dem Wert von getchar() Berechnungen wie (3 * c + 300) / 42 anstellen
> können. Unklar.

Du kannst solche Berechnungen genau dann erledigen, wenn getchar() != 
EOF ist. Und wozu du sie anstellen wolltest, liegt an deinem Problem, 
nicht an deiner Wunschlösung.

> 5) getchar() sollte aber die Semantik besitzen: liefere ein Zeichen,
> wenn es da ist, oder etwas ungültiges bzw. nichts, wenn ein Fehler
> auftrat.

Aha.

> Ich brauche also einen Datentyp, mit dem ich jedes gültige
> Zeichen und den ungültigen Zustand repräsentieren kann.

Also mindestens 8 Bit plus Flag oder mindestens 9 Bit.

> Arithm. Operationen für Zeichen sind Unsinn.

Warum? Sie sind zumindest für Kryptographie sinnvoll, auch wenn du nicht 
auf ASCII stehst.

> Operationen zur Konvertierung in
> korrespondierende Klein-/Großbuchstaben sinnvoll.

Was ist mit Zeichen, die keine oder keine eindeutige Repräsentation 
haben? Wie sieht es mit locale-Unterstützung aus (I <-> i, oder doch 
lieber I <-> ı und İ <-> i wie im Türkischen)?

Und willst du Unicode so tief in die Sprache einbauen, dass es auch für 
Aramäisch funktioniert? Auf 8 Bit-Systemen mit 4 KB Flash?

> 6) Bei "int putchar(int)" ergibt sich dasselbe Bild: warum ist der
> Argumenttyp ein int?

Orthogonalität, auch bekannt als Konsistenz.

> 7) Der Wert von putchar() wird eigentlich nur gebraucht, um Miss-/Erfolg
> anzuzeigen. Zwei Zustände, dafür gibt es bool. Man fragt sich sofort,
> welche Werte können denn noch auftauchen?

Jemand, der die Programmiersprache verstanden hat, weiß sofort, dass die 
Wahrheitswerte auf "gleich null" und "ungleich null" herauslaufen.

Du willst die Sprache für jemanden verständlich machen, der eine andere 
Sprache versteht, aber nicht bereit ist, sich mit dieser Sprache zu 
befassen. Gratuliere. Aber dann nimm halt die andere Sprache und gut 
ist?

> I) Datentypen sind dazu da, die notwendigen Entitäten im Code möglichst
> exakt abzubilden. Entsprechen die Wertebereiche und/oder möglichen
> Operationen nicht den Erwartungen, ist das mindestens sehr verwirrend.

Also ich erwarte in aller Regel das, was in der Doku steht...

> II) Namen sind nicht unwesentlich.

getchar() gibt ein Zeichen zurück,
putchar() sendet ein Zeichen,
finde ich eindeutig.

Dass ein Zeichen hier als "int" definiert ist, weil es alle Zeichen 
und ein EOF beinhalten kann, ist eine konsistent umgesetze Eigenheit 
der API.

Da finde ich den PHP-Ansatz, alles zueinander inkonsistent zu halten, 
wesentlich nerviger.

> Zudem ist int als unspezifischer Typ zur Darstellung eines Zeichens
> in einem 7- oder 8-bit-Code unsinnig.

Warum? ASCII ist nunmal ein 7- oder 8-bit-Code...

> Die für manche nun unangenehme Erkenntnis ist, dass wir I in C nicht
> zufriedenstellend erreichen können, und damit die wichtige Kombination
> aus I und II in C scheitert.

Das ist eine für dich unangenehme Erkenntnis, weil dein Horizont 
ungeeignet dafür ist. :-)

Andere können damit gut leben. Und ja, mir ist auch klar, dass man es 
besser machen kann - aber dann muss man die Vorteile von C aufgeben. Und 
dazu bin ich in bestimmten Bereichen schlicht nicht bereit. Eine Kröte 
muss ich schlucken.

> Natürlich ist das obige noch nicht optimal, aber m.E. schon ein Schritt
> in die richtige Richtung. Summa summarum ist also der Schritt von
> gewöhnlichem C zur dieser Art von prozeduralem, expressivem Code in C++
> nicht besonders groß.

Dein geposter Code ist wesentlich länger als das, was die Definition von 
"ein Zeichen im Sinne eines Streams ist ein Zeichen oder ein EOF" 
erlaubt. Dass der Maschinencode im Endeffekt besser sein kann (nicht 
sein muss), ist eine andere Frage...

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> [Unendliche Ausführung über int als Rückgabetyp von getchar()]

Du regst Dich auf über die Unzulänglichkeiten, aber wendest diese selbst 
an:

> template<>
> std::optional<std::byte> get<std::byte>() {
>     if (int c = getchar(); c != EOF) {
>         return std::byte(c);

Das ist jetzt nicht wahr, oder? Ich lese da:

    int c = getchar();
und
    c != EOF;

Du legst also lediglich um die vermeintlichen Unzulänglichkeitenen nur 
ein rosa-rotes Mäntelchen drum und verkaufst das dann als ultimative 
"Verbesserung". Klasse!

Sorry, damit machst Du Dich komplett unglaubwürdig. Dein "verbesserter 
Code" ist für einen potentiellen C- nach C++-Umsteiger:

- länger
- komplizierter
- umständlicher
- nicht schneller
- und vor allen Dingen: ABSCHRECKEND.

So sind Deine Missionierungsversuche von vornherein zum Scheitern 
verurteilt.

Und damit EOD für mich. Du solltest in den Vertrieb gehen. Als Verkäufer 
machst Du Dich ganz gut.

: Bearbeitet durch Moderator
von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Als C++-Programm könnte das obige Beispiel etwa so aussehen, z.B. für
> 7-Bit Ascii-Zeichen oder unspezifische Bytes:

Und genau das ist der Grund, wieso ein Torvalds keine C++-Programmierer 
an Bord haben will. Weil die aus ganz einfachen Routinen einen totalen 
Clusterfsck veranstalten, nur weil sie können.

von Nop (Gast)


Lesenswert?

Übrigens, ein ganzes Projekt in so einem Stil geschrieben, mit dann 
hunderten solcher sinnloser Abstraktionen, das ist schlichtweg 
unwartbarer write-only-Code. Niemand außer dem ursprünglichen Autoren 
wird durch so einen Wust noch durchsteigen, und wenn der aus dem Projekt 
draußen ist, kann man es bloß noch wegwerfen.

Mit Glück hat man eine Projektführung, die solche Tendenzen rechtzeitig 
erkennt und das stoppt, im Extremfall auch durch Entfernen solcher "code 
astronauts" aus dem Projekt.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

getchar.c:
1
#include <stdio.h>
2
3
int main ()
4
{
5
    int c;
6
7
    while ((c = getchar ()) != EOF)
8
    {
9
        putchar (c);
10
    }
11
12
    return 0;
13
}

Compilieren:
1
$ cc -O -Wall getchar.c -o getchar-c

Ausführen: Kopieren von 1000 MB von /dev/zero nach /dev/null:
1
$ time dd if=/dev/zero bs=1M count=1000 | ./getchar-c >/dev/null
2
1000+0 records in
3
1000+0 records out
4
1048576000 bytes (1.0 GB, 1000 MiB) copied, 3.69862 s, 284 MB/s
5
6
real    0m3.702s
7
user    0m3.388s
8
sys     0m0.808s

Jetzt das Gleiche mit Wilhelms Lösung:
getchar.cc:
1
#include <stdio.h>
2
#include <cstddef>
3
#include <optional>
4
5
template<typename T> std::optional<T> get();
6
7
template<>
8
std::optional<std::byte> get<std::byte>() {
9
    if (int c = getchar(); c != EOF) {
10
        return std::byte(c);
11
    }
12
    return {};
13
}
14
15
bool put(std::byte c) {
16
    return putchar(static_cast<int>(c)) != EOF;
17
}
18
19
int main()
20
{
21
    while(auto c = get<std::byte>()) {
22
        if (!put(*c)) {
23
     return 1;
24
        }
25
    }
26
    return 0;
27
}

Compilieren:
1
$ g++ -O -Wall -std=c++17 getchar.cc -o getchar-cc

Ausführen: Kopieren von 1000 MB:
1
$ time dd if=/dev/zero bs=1M count=1000 | ./getchar-cc >/dev/null
2
1000+0 records in
3
1000+0 records out
4
1048576000 bytes (1.0 GB, 1000 MiB) copied, 8.04939 s, 130 MB/s
5
6
real    0m8.054s
7
user    0m7.728s
8
sys     0m0.856s

Fazit: Das simple Kopieren von Zeichen von links nach rechts dauert nun 
mehr als doppelt(!) so lang. Aber Hauptsache, das Programm sieht geil 
aus.

: Bearbeitet durch Moderator
von mh (Gast)


Lesenswert?

Frank M. schrieb:
> Fazit: Das simple Kopieren von Zeichen von links nach rechts dauert nun
> mehr als doppelt(!) so lang. Aber Hauptsache, das Programm sieht geil
> aus.

Lass denn Compiler etwas arbeiten (-O2) und schon ist das Ergebnis 
identisch.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

mh schrieb:
> Lass denn Compiler etwas arbeiten (-O2) und schon ist das Ergebnis
> identisch.
1
# time dd if=/dev/zero bs=1M count=1000 | ./getchar-cc-o2 >/dev/null
2
1000+0 records in
3
1000+0 records out
4
1048576000 bytes (1.0 GB, 1000 MiB) copied, 3.99222 s, 263 MB/s
5
6
real    0m3.995s
7
user    0m3.560s
8
sys     0m0.932s

Stimmt, bis auf ca. 5% ist er dran. Okay.

von mh (Gast)


Lesenswert?

Frank M. schrieb:
> Stimmt, bis auf ca. 5% ist er dran. Okay.

Bei meinem Test ist die C++ Variante 3% schneller... Mikrobenchmarks mit 
"time" sind halt nicht sehr verlässlich.

Man sollte auch bedenken, dass deine C Variante weniger Arbeit erledigen 
muss als die C++ Variante, da der Rückgabewert von putchar ignoriert 
wird. Korrigiert man das (siehe 2. Code im Post won W.M.), dann liefert 
der Compiler für C und C++ ein identisches Ergebnis.

von Wilhelm M. (wimalopaan)


Lesenswert?

mh schrieb:
> Frank M. schrieb:
>> Stimmt, bis auf ca. 5% ist er dran. Okay.
>
> Bei meinem Test ist die C++ Variante 3% schneller... Mikrobenchmarks mit
> "time" sind halt nicht sehr verlässlich.
>
> Man sollte auch bedenken, dass deine C Variante weniger Arbeit erledigen
> muss als die C++ Variante, da der Rückgabewert von putchar ignoriert
> wird. Korrigiert man das (siehe 2. Code im Post won W.M.), dann liefert
> der Compiler für C und C++ ein identisches Ergebnis.

Der dazu idiomatische C++-Code
1
int main(){
2
    Console<std::byte> console;
3
    std::copy(std::begin(console), std::end(console), console.obegin());
4
}

ist übrigens genauso schnell wie die C-Variante (oder die andere 
C++-Variante).

von mh (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Der dazu idiomatische C++-Code
> int main(){
>     Console<std::byte> console;
>     std::copy(std::begin(console), std::end(console), console.obegin());
> }
>
> ist übrigens genauso schnell wie die C-Variante (oder die andere
> C++-Variante).

Aber dafür muss man ja nen template verstehen und selbst programmieren. 
;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

mh schrieb:
> Wilhelm M. schrieb:
>> Der dazu idiomatische C++-Code
>> int main(){
>>     Console<std::byte> console;
>>     std::copy(std::begin(console), std::end(console), console.obegin());
>> }
>>
>> ist übrigens genauso schnell wie die C-Variante (oder die andere
>> C++-Variante).
>
> Aber dafür muss man ja nen template verstehen und selbst programmieren.
> ;-)

Oh ja, das böse T-Wort ...
Deswegen habe ich paar Zeilen für "Console" grad mal weggelassen. Der 
geneigte Leser mag es grad selbst schreiben.

von Carl D. (jcw2)


Lesenswert?

mh schrieb:
> Wilhelm M. schrieb:
>> Der dazu idiomatische C++-Code
>> int main(){
>>     Console<std::byte> console;
>>     std::copy(std::begin(console), std::end(console), console.obegin());
>> }
>>
>> ist übrigens genauso schnell wie die C-Variante (oder die andere
>> C++-Variante).
>
> Aber dafür muss man ja nen template verstehen und selbst programmieren.
> ;-)

Man kann aber statt Console<> auch jeden anderen Container nehmen, ohne 
am Algorithmus etwas ändern zu müssen. Oder umgekehrt jeden Algorithmus 
aus der STL verwenden kann, der einen Forward-Iterator und einen 
"Ende-Zeiger", der den "!="-Operator kennt.
Aber sicher zuviel Abstraktion, oder?      Nicht für jeden.

von Wilhelm M. (wimalopaan)


Lesenswert?

S. R. schrieb:
>> Ich erwarte also einen arithmetischen Umgang mit c. [...]
>> Bedeutung von EOF: end-of-file. Was hat dieser Sentinel-Wert
>> mit ganzzahliger Arithmetisch zu tun?
>
> Nichts.

Richtig.

>> Was würde etwa (EOF + 42) bedeuten?
>
> Ist eine Zahl. Kann positiv oder negativ sein.
> Ist implementation-defined und entsprechend dokumentiert.

Und deswegen sollte diese Operation auch nicht möglich sein
(s.a. Antwort auf die vorige Frage).

>> 2) Der Gültigkeitsbereich der Variable c ist der Funktionsblock main().
>> Auf dieser Ebene wird c aber gar nicht mehr verwendet.
>
> Das tut mir aber Leid. Mach einen Block drum, wenn's dich stört.

Man fügt einen weiteren Block ein, nur damit eine Variable in diesem 
Block
sichtbar ist, obwohl sie eigentlich nur in einem inneren Block dazu 
sichtbar
sein sollte? Das ist von hinten durch die Brust ins Auge und ändern
am Problem nichts.

>> Hat also der Ersteller des Codes hier etwas vergessen?
>> Ist das Programm gar fehlerhaft?
>
> Die Frage gilt für jeden Code.

Bei einem Codeabschnitt, der in seiner Struktur schon fragwürdig ist,
ist die Wahrscheinlichkeit von Fehlern einfach größer.

>> Das ist auf den schnellen Blick nicht zu erkennen. Das ist
>> verwirrend.
>
> Wenn du von so einem Trivialprogramm verwirrt bist, dann sind
> domänenspezifische Programmiersprachensubsets vielleicht eher was für
> dich?

Ein Beispiel ist ein Beispiel und damit per se einfach.
Du hast den Punkt nicht erkannt.

>> 3) Es wird "int getchar()" aufgerufen. Die Doku sagt, es wird ein
>> Zeichen von stdin gelesen. Soweit so gut: aber, warum ist der Typ der
>> Funktion dann int und nicht etwa char, unsigned char, oder sonst was?
>
> Weil du die Doku nicht ordentlich gelesen hast.

Und die Doku beschreibt einfach nur einen Mangel: den Widerspruch 
zwischen
dem Typ der Funktion und der Aufgabe der Funktion. In der Doku steht:

The obtained character on success or EOF on failure.

Die Frage bleibt: warum ein int als Typ?

Die Posix-Doku ist da etwas deutlicher. Die man-page von getchar() 
verweist
auf die von getc(), die wiederum verweist auf die von fgetc() und dort
steht dann:

the fgetc() function shall obtain the next byte as an unsigned char 
converted to an int

Ja, und leider kann der Anfänger auch leicht den Fehler
1
unsigned char c = getchar();

Auch hier gilt das doch pars pro toto. Denk doch einfach mal einen 
Schritt weiter. Stell Dir
irgendeine beliebige andere Funktion vor:
1
uint16_t convert(const Data*);

Die Bedeutung einer Funktion sollte weitestgehend auch ohne Doku klar 
aus dem Typ
und der Signatur hervorgehen: hier lese ich heraus, dass ein Objekt des 
Typs Data
in den Typ uint16_t konvertiert wird. Was ich nicht erfahre ist, was 
passiert in dem
Fall, das die Konversion nicht möglich ist (falsche Format, 
NULL-Pointer)?
Irgendwo steht dann im Kleingedruckten, dass die Konversion aber in ein 
uint8_t
erfolgt und der Typ nur deswegen uint16_t ist, damit ich einen 
Misserfolg anzeigen
kann.

Das Pendant dazu
1
std::optional<uint8_t> convert(const Data&);

ist viel klarer: eine Referenz ist nicht nullable, um den Fall muss ich 
mir also keine
Gedanken machen. Das eigentliche Ergenbis hat den Typ uint8_t, und per 
std::optional<>
kann ich auch als Ergebnis die leere Menge erhalten (im Fehlerfall).

>> Ich möchte doch ein Zeichen lesen. Es gibt die Möglichkeit, dass der
>> Name getchar() einfach nicht zur Semantik passt und besser getNumber()
>> heissen sollte.
>
> Der Name passt zur Semantik, du definierst dir nur gerade irgendwas
> zurecht.

Das erfahre ich aber erst dann, wenn ich etwas mühsam die Doku gelesen 
habe (s.o.).
Der Punkt ist doch, dass ich den Code so aussagekräftig wie möglich 
gestalten
will.

>> Das könnte den Typ int erklären und ich könnte evtl.
>> Zahlen im Wertebereich eines ints von stdin parsen.
>
> Zahlen und Zeichen sind verschiedene Dinge.

Ja, das versuche ich doch die ganze Zeit klar zu machen.

>> Oder es wird ein UTF-16 o.ä. Zeichen gelesen?
>
> Die Doku redet nicht von UTF-16, also wird es höchstwahrscheinlich auch
> keins sein.

Höchstwahrscheinlich ... besser wäre, man müsste keine Vermutungen 
anstellen.

>> 4) Genauere Betrachtungen ergeben, das der Typ von getchar() nur
>> deswegen int ist, damit der negative Wert des Sentinels EOF
>> repräsentiert werden kann. Auch das ist sehr undurchsichtig.
>
> Das ist nicht undurchsichtig, sondern schlicht so definiert.

Es ist schlicht schlecht designed.

> Da kannst du auf und niedrig hüpfen oder deine eigene API bauen, aber du
> wirst dieses Problem auch irgendwie lösen müssen (zweiter Rückgabewert,
> Exceptions o.ä.).

Genau, habe ich ja auch gelöst.

> Sämtliche deiner Lösungen werden auf Maschinen mit
> mehr als 8 Bit weniger effizient sein.

Nein, es kommt sogar derselbe Code dabei heraus.

>> Alles andere sind eher trap-representations. Warum sollte ich mit
>> dem Wert von getchar() Berechnungen wie (3 * c + 300) / 42 anstellen
>> können. Unklar.
>
> Du kannst solche Berechnungen genau dann erledigen, wenn getchar() !=
> EOF ist.

Nein. Ich kann es leider immer: der Compiler verhindert es hier nicht.

>> Ich brauche also einen Datentyp, mit dem ich jedes gültige
>> Zeichen und den ungültigen Zustand repräsentieren kann.
>
> Also mindestens 8 Bit plus Flag oder mindestens 9 Bit.

Genau. Oder eben 16-Bit. Aber diese 16-Bit will ich doch keinesfalls
als vorzeichenbehaftete Zahl interpretieren. Ich möchte den 8-Bit
Zeichencode haben und ggf. einen Fehlerzustand.

>> Arithm. Operationen für Zeichen sind Unsinn.
>
> Warum? Sie sind zumindest für Kryptographie sinnvoll, auch wenn du nicht
> auf ASCII stehst.

Was soll denn das Ergebnis der Multiplikation des Buchstabens A mit dem
Buchstaben Z sein? Du meinst eher: Multiplizieren den ASCII-Zeichencode 
von A
mit dem ASCII-Zeichencode von B:
1
AsciiChar c1, c2;
2
auto m = c1.asciiCode() * c2.asciiCode();

>> Operationen zur Konvertierung in
>> korrespondierende Klein-/Großbuchstaben sinnvoll.
>
> Was ist mit Zeichen, die keine oder keine eindeutige Repräsentation
> haben?

Ja, genau (s.a. int tolower(int c)). Hier wieder dasselbe Problem ...

>> 6) Bei "int putchar(int)" ergibt sich dasselbe Bild: warum ist der
>> Argumenttyp ein int?
>
> Orthogonalität, auch bekannt als Konsistenz.

Mmh, Orthogonalität in der Schnittstelle eines Typs ist etwas anderes 
als
Konsistenz in der Doku.

Formal habe ich den Datentyp int für vorzeichenbehaftete Ganzzahlen mit
mindestens 16-Bit, der die Operationen +,-,*,/, ..., <, <=, ... und auch 
putchar(),
tolower(), ... hat. Wäre es nicht der DT int, sondern etwa 
ComplexNumber,
dann wäre es jedem Anfänger klar, dass etwa <, <=, ... und auch 
tolower() nicht
zur Schnittstelle des DT gehören.

Hier aber wird der DT int für etwas hijacked, für das er nicht steht. 
Nur bei int
ist man historisch bedingt bereit eine Ausnahme zu machen, weil C es 
nicht besser
kann.

>> 7) Der Wert von putchar() wird eigentlich nur gebraucht, um Miss-/Erfolg
>> anzuzeigen. Zwei Zustände, dafür gibt es bool. Man fragt sich sofort,
>> welche Werte können denn noch auftauchen?
>
> Jemand, der die Programmiersprache verstanden hat, weiß sofort, dass die
> Wahrheitswerte auf "gleich null" und "ungleich null" herauslaufen.

Mmh, ich lese das: jemand, der die Unzulänglichkeiten der 
Programmiersprache
verstanden hat, ...

> Du willst die Sprache für jemanden verständlich machen, der eine andere
> Sprache versteht, aber nicht bereit ist, sich mit dieser Sprache zu
> befassen. Gratuliere.

Nein. Ich möchte darauf aufmerksam machen, dass die Sprache C eklatante
Mängel hat, die andere Sprachen nicht haben. Zudem existiert eben mit 
C++
eine Sprache, die einen sehr leichten, schrittweisen Aufstieg 
ermöglicht, weil
ich cherry-picking machen kann: ich bleibe bspw. beim prozeduralen
Paradigma wie in C, nehme aber viele Vorteile mit.

> Aber dann nimm halt die andere Sprache und gut
> ist?

Das mache ich ja. Reines C ist für mich keine Option (mehr), weil es 
einfach nur
Nachteile hat.

>> I) Datentypen sind dazu da, die notwendigen Entitäten im Code möglichst
>> exakt abzubilden. Entsprechen die Wertebereiche und/oder möglichen
>> Operationen nicht den Erwartungen, ist das mindestens sehr verwirrend.
>
> Also ich erwarte in aller Regel das, was in der Doku steht...

Das Problem daran ist aber, dass der Compiler die Doku nicht überprüft. 
Und bekanntlich
altert Doku anders als der Code, das ist wie bei Kommentaren. Die 
Konsequenz
daraus ist, dass ich nur dann die Fehlerwahrscheinblichkeit im Code 
reduziere, wenn
der Compiler möglichst viel überprüfen kann. Und das mache ich mit einem
starken Typsystem, was aus den notwendigerweise unspezifischen 
primitiven DT
der Sprache domänenspezifische DT mit klarer Semantik erstelle.

>> II) Namen sind nicht unwesentlich.
>
> getchar() gibt ein Zeichen zurück,
> putchar() sendet ein Zeichen,
> finde ich eindeutig.

Du hast hier I) vergessen: der Name passt nicht zur Schnittstelle.

> Dass ein Zeichen hier als "int" definiert ist, weil es alle Zeichen
> und ein EOF beinhalten kann, ist eine konsistent umgesetze Eigenheit
> der API.

Kann man so sehen: wenn man den DT int bei getchar() schon missbraucht, 
dann
sollte man es auch bei putchar(int) tun ;-)

>> Zudem ist int als unspezifischer Typ zur Darstellung eines Zeichens
>> in einem 7- oder 8-bit-Code unsinnig.
>
> Warum? ASCII ist nunmal ein 7- oder 8-bit-Code...

Da widersprichst Du Dir selbst: warum sollte ich auf einer kleinen 8-Bit 
Plattform
ein Zeichen als ein int darstellen.

> Andere können damit gut leben. Und ja, mir ist auch klar, dass man es
> besser machen kann -

Das versuche ich die ganze Zeit zu sagen: man kann es besser machen.

> aber dann muss man die Vorteile von C aufgeben.

Die wären jetzt in diesem Beispiel welche: ich sehe keinen Einzigen.

> Und
> dazu bin ich in bestimmten Bereichen schlicht nicht bereit. Eine Kröte
> muss ich schlucken.

Ich will keine Kröte schlucken, schmeckt mir einfach nicht. Ich esse 
lieber etwas,
was optisch ansprechend und in sich gesund ist, um im Bild zu bleiben.

> Dein geposter Code ist wesentlich länger als das, was die Definition von
> "ein Zeichen im Sinne eines Streams ist ein Zeichen oder ein EOF"
> erlaubt.

Das stimmt so nicht, denn Du muss ja berücksichtigen, dass die 
Definition des Typs
AsciiChar und auch die der anderen freien Funktionen Bestandteil einer 
(besseren)
Bibliothek sind, und damit hier nicht mitzählen. Oder möchtest Du die
realisierung von getchar() / putchar() aus der libc auch dazu zählen?

> Dass der Maschinencode im Endeffekt besser sein kann (nicht
> sein muss), ist eine andere Frage...

Ich kann nicht für alle Compiler / Plattformen sprechen: er wird aber in 
vielen
Fällen (etwa auch AVR8) identisch sein.

von batman (Gast)


Lesenswert?

Wilhelm M. schrieb:
> The obtained character on success or EOF on failure.
>
> Die Frage bleibt: warum ein int als Typ?

Na vielleicht wollte sich der Autor damals nicht auf 7-Bit ASCII 
festlegen. Damit kommt ja heute auch nicht mehr weit. Es ist ja auch 
wirklich kein Problem, so wie ich es oft mache, noch einen kleinen 
typsicheren Wrapper für eine OO-Sprache drumrumzulegen. In C++ hat man 
sowieso überall dicke Verkapselungszwiebeln.

von Wilhelm M. (wimalopaan)


Lesenswert?

batman schrieb:
> Wilhelm M. schrieb:
>> The obtained character on success or EOF on failure.
>>
>> Die Frage bleibt: warum ein int als Typ?
>
> Na vielleicht wollte sich der Autor damals nicht auf 7-Bit ASCII
> festlegen. Damit kommt ja heute auch nicht mehr weit.

Es ist ja auch nicht 7-Bit Ascii, es ist ein Byte-Interface!

Das war ja auch nicht wirklich als Frage meinerseits zu verstehen: woher 
es kommt, ist mir schon klar. Es ist doch auch nur ein Beispiel für ein 
schlecht gestaltetes Interface, eben weil C keine andere Möglichkeiten 
hat.

> Es ist ja auch
> wirklich kein Problem, so wie ich es oft mache, noch einen kleinen
> typsicheren Wrapper für eine OO-Sprache drumrumzulegen. In C++ hat man
> sowieso überall dicke Verkapselungszwiebeln.

Genau das habe ich ja gemacht, weil ich natürlich nicht getchar() neu 
erfinden wollte, sondern nur das Interface klarer gestalten. Aber das 
sollte doch auch aus dem ganzen Thread deutlich werden.

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Genau das habe ich ja gemacht, weil ich natürlich nicht getchar() neu
> erfinden wollte, sondern nur das Interface klarer gestalten.

Klarer gestalten.. ja nee, ist klar. Tatsächlich hast Du aus einer 
einfachen Funktion einen unleserlichen Clusterfsck gemacht. Ich will gar 
nicht erst wissen, wie Deine Funktionen aussehen, die nichttriviale 
Dinge tun.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Aber das sollte doch auch aus dem ganzen Thread deutlich werden.

Der Thread ging ursprünglich um die Frage, ob man in C (nicht C++) 
innerhalb des switch() eine Funktion aufrufen könne.

Der Thread wurde erfolgreich gekapert. Gratuliere.

von batman (Gast)


Lesenswert?

Wilhelm M. schrieb:
>> Na vielleicht wollte sich der Autor damals nicht auf 7-Bit ASCII
>> festlegen. Damit kommt ja heute auch nicht mehr weit.
>
> Es ist ja auch nicht 7-Bit Ascii, es ist ein Byte-Interface!

Wo steht das?
Der Rückegabewert ist quasi eine UNION aus Fehlercode und numerischem 
Zeichencode.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

batman schrieb:
> Der Rückegabewert ist quasi eine UNION aus Fehlercode und numerischem
> Zeichencode.

Eben. Wie ich oben schon schrieb, hat er lediglich ein rosa-rotes 
Mäntelchen um getchar() und EOF gewickelt.

Da bleibe ich lieber bei getchar(). Da ist in wenigen Minuten ein 
funktionsfähiges Kopierprogramm, mit CRLF->LF oder LF->CRLF Filter 
(unix2dos or dos2unix) geschrieben, während Wilhelm noch am Interface 
tüftelt - ohne irgendeinen funktionalen Vorteil zu bieten.

Für jedes Problem das richtige Werkzeug. Es ist unbestritten, dass C++ 
für bestimmte Aufgaben die bessere Programmiersprache ist. Aber jemanden 
zu drängen, auf C++ umzusteigen, damit er Funktionen innerhalb switch() 
aufrufen kann (was in C genauso geht), ist nicht zielführend.

Der TO hat sich auch genau an dieser Stelle aus dem Thread ausgeklinkt.

: Bearbeitet durch Moderator
von Wilhelm M. (wimalopaan)


Lesenswert?

batman schrieb:
> Wilhelm M. schrieb:
>>> Na vielleicht wollte sich der Autor damals nicht auf 7-Bit ASCII
>>> festlegen. Damit kommt ja heute auch nicht mehr weit.
>>
>> Es ist ja auch nicht 7-Bit Ascii, es ist ein Byte-Interface!
>
> Wo steht das?

Nicht explizit: es hängt vom Mode des streams ab.

> Der Rückegabewert ist quasi eine UNION aus Fehlercode und numerischem
> Zeichencode.

Kann man so sehen. Allerdings eben mit dem ganz großen Nachteil, dass es 
das nicht nicht wirklich als eigener Typ ist, sondern hier ein in 
missbraucht wird. Daraus resultieren alle weiteren Probleme.
Es gibt ja nicht ohne Grund die Fragen zum Thema, was hieran wohl falsch 
sei:
1
char c = getchar();

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> was hieran wohl falsch sei:char c = getchar();

Willst Du so C-Programmierer vor ihrer eigenen Dummheit schützen oder 
was soll das? Schon im K&R steht drin, dass c vom Typ int sein muss.

man getchar

Da steht:

int getchar(void);

Einfacher gehts nicht. Konstruiere doch nicht irgendeinen Programmierer, 
der ein Blödmann ist. Die gibts überall - und die programmieren nicht 
alle in C.

Wenn Du ein Betätigungsfeld für Deine Mission suchst: Schnapp Dir die 
zahllosen Arduino-Threads. Die Armen "programmieren" alle in C++, ohne 
es überhaupt zu wissen. Viele von ihnen haben echt Hilfe nötig.

: Bearbeitet durch Moderator
von Wilhelm M. (wimalopaan)


Lesenswert?

Frank M. schrieb:
> batman schrieb:
>> Der Rückegabewert ist quasi eine UNION aus Fehlercode und numerischem
>> Zeichencode.
>
> Eben. Wie ich oben schon schrieb, hat er lediglich ein rosa-rotes
> Mäntelchen um getchar() und EOF gewickelt.
>
> Da bleibe ich lieber bei getchar(). Da ist in wenigen Minuten ein
> funktionsfähiges Kopierprogramm, mit CRLF->LF oder LF->CRLF Filter
> (unix2dos or dos2unix) geschrieben, während Wilhelm noch am Interface
> tüftelt - ohne irgendeinen funktionalen Vorteil zu bieten.

Ein funktionaler Vorteil sollte doch auch gar nicht dabei heraus kommen. 
Ein unix2dos in C++ sollte genauso verwendet werden können, wie eins in 
C oder ...

>
> Für jedes Problem das richtige Werkzeug. Es ist unbestritten, dass C++
> für bestimmte Aufgaben die bessere Programmiersprache ist. Aber jemanden
> zu drängen, auf C++ umzusteigen, damit er Funktionen innerhalb switch()
> aufrufen kann (was in C genauso geht), ist nicht zielführend.

Der Einstieg in die Diskussion war aber wie Du weißt anderes: es ging 
nicht um die Möglichkeit, sondern um eine Stilfrage und anschließend, ob 
es das Debugging behindere: an der Stelle habe ich weitere andere 
Gesichstpunkte gebracht.

von Wilhelm M. (wimalopaan)


Lesenswert?

Frank M. schrieb:
> Wilhelm M. schrieb:
>> was hieran wohl falsch sei:char c = getchar();
>
> Willst Du so C-Programmierer vor ihrer eigenen Dummheit schützen

Das wäre oft sehr gut ;-)

> oder
> was soll das? Schon im K&R steht drin, dass c vom Typ int sein muss.
>
> man getchar
>
> Da steht:
>
> int getchar(void);
>
> Einfacher gehts nicht. Konstruiere doch nicht irgendeinenen
> Programmierer, der ein Blödmann ist.

Ich will aber, dass der Compiler genau diesen Fehler erkennt und 
ablehnt. Das erreiche ich aber nicht, wenn ich irgendwelche DT hijacke.

Mir ist klar, dass das historisch bedingt ist und einfach so ist, wie es 
ist. Trotzdem sollte jeder, der sich ernsthaft mit dem Programmieren 
beschäftigt mindestens einmal diese Schwachstelle erkennen. Und aus 
dieser Erkenntnis heraus ergibt sich vielleicht für den ein oder anderen 
der Wunsch, es besser zu machen.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Ich will aber, dass der Compiler genau diesen Fehler erkennt und
> ablehnt.

Ach?
Ein Ausschnitt aus Deinem Code:
1
template<>
2
std::optional<std::byte> get<std::byte>() {
3
    if (int c = getchar(); c != EOF) {
4
        return std::byte(c);
5
    }
6
    return {};
7
}

Wie verhinderst Du, dass ich hier "char c = getchar()" hinschreibe?

Du hast dieses von Dir konstruierte Problem nur in eine andere Ecke 
geschoben, mehr nicht. Das meinte ich mit rosa Mäntelchen.

: Bearbeitet durch Moderator
von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Und aus
> dieser Erkenntnis heraus ergibt sich vielleicht für den ein oder anderen
> der Wunsch, es besser zu machen.

Das tust Du aber nicht. Du machst es unleserlicher. Das mag im 
Hobbybereich OK sein, wenn das Programm sowieso nur vom Autoren genutzt 
und gepflegt wird. Bei professionellem Arbeiten ist das Entscheidende 
die leichte Lesbarkeit für spätere Programmierer. Hier machst Du es 
nicht besser, sondern Dimensionen schlechter. Du schreibst 
Wegwerf-Software, und das ist nichts, was zur Nachahmung anregt.

von Wilhelm M. (wimalopaan)


Lesenswert?

Frank M. schrieb:
> Wilhelm M. schrieb:
>> Ich will aber, dass der Compiler genau diesen Fehler erkennt und
>> ablehnt.
>
> Ach?
> Ein Ausschnitt aus Deinem Code:
>
1
> template<>
2
> std::optional<std::byte> get<std::byte>() {
3
>     if (int c = getchar(); c != EOF) {
4
>         return std::byte(c);
5
>     }
6
>     return {};
7
> }
8
>
>
> Wie verginderst Du, dass ich hier "char c = getchar()" hinschreibe?
>
> Du hast dieses Problem nur in eine andere Ecke geschoben, mehr nicht.

Also nocheinmal: das was ich da geschrieben habe schuldet der Tatsache 
Rechnung, das ich das Beispiel auf einem Posix-System erstellt habe und 
ich grad keine Lust hatte, getchar() von scratch neu zu schreiben. Auch 
steht weiter oben schon, dass Du dies ruhig als Bestandteil einer 
alternativen Bibliothek vorstellen darfst. Insofern ist Dein Argument 
hinfällig.

In der bare-metal Variante wird dann auch kein getchar() verwendet.

von Wilhelm M. (wimalopaan)


Lesenswert?

Nop schrieb:
> Wilhelm M. schrieb:
>> Und aus
>> dieser Erkenntnis heraus ergibt sich vielleicht für den ein oder anderen
>> der Wunsch, es besser zu machen.
>
> Das tust Du aber nicht. Du machst es unleserlicher.

Was ist denn unleserlicher daran?

> Das mag im
> Hobbybereich OK sein, wenn das Programm sowieso nur vom Autoren genutzt
> und gepflegt wird.

Mir scheint es eher daran zu liegen, dass die meisten sehr stark in 
einer starren C-Brille verhaftet sind.

> Bei professionellem Arbeiten ist das Entscheidende
> die leichte Lesbarkeit für spätere Programmierer. Hier machst Du es
> nicht besser, sondern Dimensionen schlechter. Du schreibst
> Wegwerf-Software, und das ist nichts, was zur Nachahmung anregt.

Das letzte Beispiel von mir, was etwa in der Art war:
1
    Console<std::byte>::Input input;
2
    Console<std::byte>::Output output;
3
    std::copy(std::begin(input), std::end(input), std::begin(output));
ist idiomatisches C++, insofern für Profis klar lesbar. Und zwar aus 
recht simplen Gründen:

1) es wird ein fertiger Algorithmus statt einer (hier Schleife) 
Eigenkonstruktion verwendet
2) es benutzt die wohlbekannte Iterator-Schnittstelle.

Für reine "nur" C-Programmierer mag das ungewohnt sein.

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Was ist denn unleserlicher daran?

Vergleich mal in Beitrag "Re: switch(Funktion()) möglich?" 
die Original-Variante mit dem Unfall, den Du daraus gebaut hast.

Wenn Dir dabei tatsächlich nichts auffällt, dann dürfte es daran liegen, 
daß C++ an sich zu aufgeblasenem, unleserlichen Zeugs verführt. Eben 
genau der Grund, wieso Torvalds keine C++-Programmierer an Bord haben 
will. Du führst es exemplarisch vor.

Und je mehr Du mit diesem Zeug hausieren gehst und in C-Threads 
herumtrollst, desto abschreckender wird es. Man kann das, was Du hier 
machst, echt nicht mehr anders als Trollen/Spammen bezeichnen.

ES NERVT. Genauso wie die Zeugen Jehovas, die einen rausklingeln, obwohl 
man die nicht haben will.

von Wilhelm M. (wimalopaan)


Lesenswert?

Nop schrieb:
> Wilhelm M. schrieb:
>
>> Was ist denn unleserlicher daran?
>
> Vergleich mal in Beitrag "Re: switch(Funktion()) möglich?"
> die Original-Variante mit dem Unfall, den Du daraus gebaut hast.

Aus dem Original
1
switch(foo()) {
2
...
3
}
was von einigen anderen als unsauber, schwer zu debuggen, etc. teilweise 
ohne Begründung abgelehnt wurde, und teilweise zu
1
{
2
  int v = foo();
3
  switch(v) {
4
  ...
5
  }
6
}

mutierte, habe ich ein
1
switch(auto v = foo()) {
2
...
3
}

gemacht. Also, was ist daran unleserlich?

Die weitere Diskussion entstand dann um das Beispiel mit 
getchar()/putchar() von Frank. Und dort habe ich mir erlaubt, auf die 
Unzulänglichkeiten dieser historischen Schnittstelle hinzuweisen und 
auch Verbesserungen aufgezeigt.

> Wenn Dir dabei tatsächlich nichts auffällt, dann dürfte es daran liegen,
> daß C++ an sich zu aufgeblasenem, unleserlichen Zeugs verführt. Eben
> genau der Grund, wieso Torvalds keine C++-Programmierer an Bord haben
> will.

Und nocheinmal die Frage: was ist an +
1
    while(const auto c = get<AsciiChar>()) {
2
        if (!put(*c)) {
3
            return 1;
4
        }
5
    }

aufgeblasen.

> Und je mehr Du mit diesem Zeug hausieren gehst und in C-Threads
> herumtrollst, desto abschreckender wird es. Man kann das, was Du hier
> machst, echt nicht mehr anders als Trollen/Spammen bezeichnen.
>
> ES NERVT. Genauso wie die Zeugen Jehovas, die einen rausklingeln, obwohl
> man die nicht haben will.

Der nach seinem EOD immer noch mitdiskutierende Moderator kann ja diesen 
Thread einfach schließen. Oder Du könntest Die Klingel hier abstellen. 
Warum machst Du das nicht?

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Also, was ist daran unleserlich?

Der Rattenschwanz, den das danach noch nach sich zieht - im Verhältnis 
zu dem, was vorher dastand. Eine massive Verschlechterung, und das schon 
bei einer Trivialfunktion.

> Der nach seinem EOD immer noch mitdiskutierende Moderator kann ja diesen
> Thread einfach schließen. Oder Du könntest Die Klingel hier abstellen.
> Warum machst Du das nicht?

Ziemlich unverschämt. Du spammst wiederholt völlig offtopic rum und 
fragst dann auch noch, ob nicht irgendwer Dir den Stecker ziehen kann, 
indem der Thread dichtgemacht wird?!

von Carl D. (jcw2)


Lesenswert?

@Wilheim:
Lasses, will doch eh keiner was von wissen.
Schau dir lieber Boost.SML an. Das geht richtig gut auf AVR und sperrt 
Abstraktionsgenger wirkungsvoll aus. Ist besser investierte Zeit.

Edit: seh gerade, ist eh schon auf deinem Radar.

: Bearbeitet durch User
von S. R. (svenska)


Lesenswert?

Wilhelm M. schrieb:
>>> Ich erwarte also einen arithmetischen Umgang mit c. [...]
>>> Bedeutung von EOF: end-of-file. Was hat dieser Sentinel-Wert
>>> mit ganzzahliger Arithmetisch zu tun?
>> Nichts.
> Richtig.

Und was hat das mit der verwendeten Programmiersprache zu tun?
Nichts.

>>> Was würde etwa (EOF + 42) bedeuten?
>>
>> Ist eine Zahl. Kann positiv oder negativ sein.
>> Ist implementation-defined und entsprechend dokumentiert.
>
> Und deswegen sollte diese Operation auch nicht möglich sein
> (s.a. Antwort auf die vorige Frage).

Warum? Weil du der Meinung bist, das gehört so?

Auf den Rest gehe ich nicht mehr ein, denn du hast den Unterschied 
zwischen "API" und "Programmiersprache" nicht verstanden.

von Wilhelm M. (wimalopaan)


Lesenswert?

Carl D. schrieb:
> @Wilheim:
> Lasses, will doch eh keiner was von wissen.

Danke für Dein Mitgefühl ;-) Ja, das Lager "alles ist ein int, 
andernfalls ist es ein C-String" ist schon eine harte Nuss. Habe gerade 
schon überlegt, ob ich den Wäschetrockner anschalten soll und dabei aus 
dem Haus gehen ...

> Schau dir lieber Boost.SML an. Das geht richtig gut auf AVR und sperrt
> Abstraktionsgenger wirkungsvoll aus. Ist besser investierte Zeit.
>
> Edit: seh gerade, ist eh schon auf deinem Radar.

Ja, stimmt. Bin aber noch nicht so weit gekommen, und bin aber auch noch 
nicht so ganz überzeugt ... kann aber noch werden ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

S. R. schrieb:
> Wilhelm M. schrieb:
>>>> Ich erwarte also einen arithmetischen Umgang mit c. [...]
>>>> Bedeutung von EOF: end-of-file. Was hat dieser Sentinel-Wert
>>>> mit ganzzahliger Arithmetisch zu tun?
>>> Nichts.
>> Richtig.
>
> Und was hat das mit der verwendeten Programmiersprache zu tun?
> Nichts.

Dann bist du wahrscheinlich auch der Meinung, dass
1
double distance1 = 42.0; // km?
2
double distance2 =  1.0; // mm? cm? who knows ... 
3
double l = distance1 + distance2;
einen Wert in l von etwa 43.0 ergeben sollte?

>>>> Was würde etwa (EOF + 42) bedeuten?
>>>
>>> Ist eine Zahl. Kann positiv oder negativ sein.
>>> Ist implementation-defined und entsprechend dokumentiert.
>>
>> Und deswegen sollte diese Operation auch nicht möglich sein
>> (s.a. Antwort auf die vorige Frage).
>
> Warum? Weil du der Meinung bist, das gehört so?

Genau!

Vielleicht hilft ja folgendes Beispiel zum Verständnis:
1
    int   a = 0;
2
    void* p = &a + NULL;
Die Addition zwischen int* (dem Typ von &a) und void* (dem Typ von NULL) 
bzw. allgemein zwischen Zeigertypen ist selbst in C nicht definiert.

> Auf den Rest gehe ich nicht mehr ein, denn du hast den Unterschied
> zwischen "API" und "Programmiersprache" nicht verstanden.

Magst Du ihn mir erklären?

von S. R. (svenska)


Lesenswert?

Wilhelm M. schrieb:
> Dann bist du wahrscheinlich auch der Meinung, dass
> double distance1 =  42.0; // km?
> double distance2 =  1.0; // mm? cm? who knows ...
> double l = distance1 + distance2;
> einen Wert in l von etwa 43.0 ergeben sollte?

Ich bin der festen Überzeugung, dass 42.0 + 1.0 ziemlich genau gleich 
43.0 ist, ja. Das gilt für Äpfel, Kilometer, Birnen und Meilen ebenso.

Und wer Äpfel von Birnen nicht unterscheiden kann, hat sowieso ein 
anderes Problem.

Wilhelm M. schrieb:
>> Warum? Weil du der Meinung bist, das gehört so?
> Genau!

Gut, dann wäre das geklärt.

Wilhelm M. schrieb:
>> Auf den Rest gehe ich nicht mehr ein, denn du hast den Unterschied
>> zwischen "API" und "Programmiersprache" nicht verstanden.
> Magst Du ihn mir erklären?

Nö.

von batman (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Was würde etwa (EOF + 42) bedeuten?

EOF-42 wäre schlicht als der 43. Fehlercode zu interpretieren, wenn man 
weiß, daß EOF der erste ist, ob nun gerade definiert oder nicht. Das ist 
im Grunde eine brauchbare Darstellung. Ich habe das schon zu einem 
mygetchar() mit zusätzlichen Fehlercodes gewrapped verwendet. Ich 
verstehe nicht, was daran überhaupt so stört, daß hier eine 
Integer-Arithmetik nicht ausgeschlossen wird.

Ok, im Fehlerfall, wenn das Result also als Fehlercode zu interpretieren 
ist, könnte man hier auf einen Aufzählungstyp (als member von union) 
eingrenzen oder wenn nur, wie gerade mal hier, nur ein einziger 
Fehlercode möglich ist, sogar auf Boolean. Wäre zumindest nur wenig 
nachteilig.

Sehr dämlich wäre es allerdings für den Normalfall, wenn etwa ein 
ASCII-Code zurückgegeben wird. Was soll man damit ohne 
Integer-Arithmetik in der Praxis anfangen? Wie soll der Anwender 
ermitteln, ob es ein Steuerzeichen, Groß- oder Kleinbuchstabe ist? Wie 
macht er dann aus einem Klein- einen Großbuchstaben? Wie bekommt er den 
ASCII zu x, einer Zahl von 0-9?

Selbst bei Pointern ist meist Arithmetik definiert, ob man sie nun 
braucht oder nicht. Ob ++p nun auf ein Objekt zeigt oder ins Nirwana, 
kann dir die Sprache auch nicht retten.

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Die Addition zwischen int* (dem Typ von &a) und void* (dem Typ von NULL)
> bzw. allgemein zwischen Zeigertypen ist selbst in C nicht definiert.

Der wesentliche Unterschied: hierbei hat man nicht einen Haufen 
sinnlosen Overengineerings betrieben, eigens um die Addition unmöglich 
zu machen. Sondern da diese Addition sowieso nichts Sinnvolles täte, hat 
man sie halt nicht implementiert. KISS.

von Wilhelm M. (wimalopaan)


Lesenswert?

batman schrieb:
> Wilhelm M. schrieb:
>> Was würde etwa (EOF + 42) bedeuten?
>
> EOF-42 wäre schlicht als der 43. Fehlercode zu interpretieren, wenn man
> weiß, daß EOF der erste ist, ob nun gerade definiert oder nicht. Das ist
> im Grunde eine brauchbare Darstellung.

Abgesehen davon, dass oben (EOF + 42) stand (was ja auch positiv sein 
kann und damit ein Ascii-Code darstellen kann): was soll denn wohl der 
43. Fehlercode (EOF - 42) bedeuten im Kontext von getchar()? Da der Wert 
von EOF implementierungsabhängig ist ...

> Ich habe das schon zu einem
> mygetchar() mit zusätzlichen Fehlercodes gewrapped verwendet. Ich
> verstehe nicht, was daran überhaupt so stört, daß hier eine
> Integer-Arithmetik nicht ausgeschlossen wird.

Was soll denn (FehlerCode1 * FehlerCode2) bedeuten? Das ist schlicht 
sinnlos.

Genauso sinnlos wie zwei Zeigerwerte zu multiplizieren. Und dies erlaubt 
die Sprache C auch sinnvollerweise nicht (s.u.).

Da man in C aber keine Datentypen im engeren Sinn definieren kann, 
bleibt einem hier als eine Möglichkeit, eine primitiven Datentyp zu 
zweckentfremden. Und das schafft massiv Nachteile.

> Sehr dämlich wäre es allerdings für den Normalfall, wenn etwa ein
> ASCII-Code zurückgegeben wird. Was soll man damit ohne
> Integer-Arithmetik in der Praxis anfangen?

Zu Zeichenketten zusammenfügen, vergleichen, in Großbuchstaben 
umwandeln, anordnen, ...

> Wie soll der Anwender
> ermitteln, ob es ein Steuerzeichen, Groß- oder Kleinbuchstabe ist?

bool isLower(AsciiChar);

> Wie
> macht er dann aus einem Klein- einen Großbuchstaben?

AsciiChar toUpper(AsciiChar);

> Wie bekommt er den
> ASCII zu x, einer Zahl von 0-9?

AsciiChar x = ...;
static_cast<uint8_t>(x);
uint8_t v = x.toInt();
uint8_t w = toInt(x);

> Selbst bei Pointern ist meist Arithmetik definiert, ob man sie nun
> braucht oder nicht.

Aber nur Addition und Subtraktion, keine Multiplikation, Division...

von Wilhelm M. (wimalopaan)


Lesenswert?

Nop schrieb:
> Wilhelm M. schrieb:
>
>> Die Addition zwischen int* (dem Typ von &a) und void* (dem Typ von NULL)
>> bzw. allgemein zwischen Zeigertypen ist selbst in C nicht definiert.
>
> Der wesentliche Unterschied: hierbei hat man nicht einen Haufen
> sinnlosen Overengineerings betrieben, eigens um die Addition unmöglich
> zu machen. Sondern da diese Addition sowieso nichts Sinnvolles täte, hat
> man sie halt nicht implementiert. KISS.

Sehr interessant!
Und da die Multiplikation zweier Fehlercodes auch sinnlos ist, definiert 
man sie eben nicht. Sehr gut erkannt ;-)

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Und da die Multiplikation zweier Fehlercodes auch sinnlos ist, definiert
> man sie eben nicht.

Muß man auch nicht, sie funktioniert trotzdem. Auch wenn sie dann nichts 
Sinnvolles tut. Was Du willst, ist eben nicht, sie nicht zu definieren, 
sondern Umwege zu gehen, damit sie undefiniert WIRD.

Und das ist überkompliziertes Denken von Leuten, die im Grunde 
unterbeschäftigt sind und leer laufen. Daher diese krampfigen Versuche, 
absurde, realitätsferne Fehlerszenarien zu "verhindern" - und dafür auch 
gerne unlesbaren Wegwerfcode zu produzieren.

Sieh Dir mal den Code von Stockfish an, der wahrscheinlich weltbesten 
Schach-Engine. Auch C++, aber gut lesbar, weil nicht auf Krampf 
möglichst viele C++-Features reingeklatscht wurden. Und nur 5% langsamer 
als der C-Port.

von Wilhelm M. (wimalopaan)


Lesenswert?

Nop schrieb:
> Wilhelm M. schrieb:
>
>> Und da die Multiplikation zweier Fehlercodes auch sinnlos ist, definiert
>> man sie eben nicht.
>
> Muß man auch nicht, sie funktioniert trotzdem. Auch wenn sie dann nichts
> Sinnvolles tut.

Naja, wie Du jetzt richtig erkannt hast, tut diese Operation nichts 
sinnvolles. Und dann sollte sie auch nicht möglich sein. Deswegen 
definiere ich sie nicht. Ist doch eigentlich ganz einfach.

Wenn Du in Deinem Code gerne Operationen drin hast, die nichts 
Sinnvolles tun, dann ist das Deine Entscheidung. Für mich ist das aber 
wiederum nicht sinnvoll.

von batman (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Was soll denn (FehlerCode1 * FehlerCode2) bedeuten? Das ist schlicht
> sinnlos.

Genauso sinnlos, wie zwei Geldbeträge zu multiplizieren, zwei 
Temperaturen, Spannungen...

Wer will denn für alles und jedes eine eigene extra (beschränkte) 
Arithemtik definieren oder einbinden. Die Fehler macht man dann eh 
woanders, weil die Zeit knapp wird.

Wilhelm M. schrieb:
>> Sehr dämlich wäre es allerdings für den Normalfall, wenn etwa ein
>> ASCII-Code zurückgegeben wird. Was soll man damit ohne
>> Integer-Arithmetik in der Praxis anfangen?
>
> Zu Zeichenketten zusammenfügen, vergleichen, in Großbuchstaben
> umwandeln, anordnen, ...

Und das macht eine Integer-Arithmetik - auch wenn du sie hinter 
zusätzlichen Wrappern versteckst.

von Vincent H. (vinci)


Lesenswert?

batman schrieb:
> Wilhelm M. schrieb:
>> Was soll denn (FehlerCode1 * FehlerCode2) bedeuten? Das ist schlicht
>> sinnlos.
>
> Genauso sinnlos, wie zwei Geldbeträge zu multiplizieren, zwei
> Temperaturen, Spannungen...
>
> Wer will denn für alles und jedes eine eigene extra (beschränkte)
> Arithemtik definieren oder einbinden. Die Fehler macht man dann eh
> woanders, weil die Zeit knapp wird.

https://en.wikipedia.org/wiki/Mars_Climate_Orbiter#Cause_of_failure

125 fucking million reasons

von Wilhelm M. (wimalopaan)


Lesenswert?

Vincent H. schrieb:
> batman schrieb:
>> Wilhelm M. schrieb:
>>> Was soll denn (FehlerCode1 * FehlerCode2) bedeuten? Das ist schlicht
>>> sinnlos.
>>
>> Genauso sinnlos, wie zwei Geldbeträge zu multiplizieren, zwei
>> Temperaturen, Spannungen...
>>
>> Wer will denn für alles und jedes eine eigene extra (beschränkte)
>> Arithemtik definieren oder einbinden. Die Fehler macht man dann eh
>> woanders, weil die Zeit knapp wird.
>
> https://en.wikipedia.org/wiki/Mars_Climate_Orbiter#Cause_of_failure
>
> 125 fucking million reasons

Danke Vincent. Den Hinweis hatte ich mir die ganze Zeit verkniffen, denn 
weiter oben wurde ja schon behauptet, dass man solche Fehler durch 
Sprüche an der Wand o.d.gl., Projektmanagement, etc. verhindert kann.

von batman (Gast)


Lesenswert?

Vincent H. schrieb:
> https://en.wikipedia.org/wiki/Mars_Climate_Orbiter#Cause_of_failure
>
> 125 fucking million reasons

Nö, da gehts um die falsche Einheit fürs Gewicht, der Wert in Pfund 
statt Kilo. Die Arithmetik wäre dieselbe und durch blindes Vertrauen in 
die Entwicklungsumgebung und mangelndes Mitdenken entstehen genau solche 
Fehler. Da steht jetzt nicht, ob das Programm in C oder C++ geschrieben 
war, ist auch Wurscht.

von Wilhelm M. (wimalopaan)


Lesenswert?

batman schrieb:
> Vincent H. schrieb:
>> https://en.wikipedia.org/wiki/Mars_Climate_Orbiter#Cause_of_failure
>>
>> 125 fucking million reasons
>
> Nö, da gehts um die falsche Einheit fürs Gewicht, der Wert in Pfund
> statt Kilo.

Weiter oben hatte ich das Beispiel schon mal gebracht mit Entfernungen. 
Hast Du schon mal in einer Sprache programmiert, in der man sich DT 
selbst definieren kann?

> Die Arithmetik wäre dieselbe und durch blindes Vertrauen in
> die Entwicklungsumgebung und mangelndes Mitdenken entstehen genau solche
> Fehler.

Mit Ausnahme der Skalierungs- bzw. Umrechnungsfaktoren ;-)

Und weil solche Fehler entstehen und tatsächlich gemacht werden, ist es 
wichtig, die Schnittstelle so wasserdicht wie möglich zu machen:

Eine Schnittstelle sollte leicht richtig und schwer falsch zu benutzen 
sein.

Wenn aber alles ein double ist ohne Semantik, dann kommen eben solche 
Fehler vor.

: Bearbeitet durch User
von Heiko L. (zer0)


Lesenswert?

>The discrepancy between calculated and measured position, resulting in the 
discrepancy between desired and actual orbit insertion altitude, had been noticed 
earlier by at least two navigators, whose concerns were dismissed.

Wie aus einem Comic.
"Die Rechnung kommt nicht hin..."
"Ach... Das passt schon!"

von Wilhelm M. (wimalopaan)


Lesenswert?

Heiko L. schrieb:
>>The discrepancy between calculated and measured position, resulting in the
> discrepancy between desired and actual orbit insertion altitude, had been 
noticed
> earlier by at least two navigators, whose concerns were dismissed.
>
> Wie aus einem Comic.
> "Die Rechnung kommt nicht hin..."
> "Ach... Das passt schon!"

Ja, so wie in diesem Thread gerade ... ;-)

von Heiko L. (zer0)


Lesenswert?

Wilhelm M. schrieb:
> Mit Ausnahme der Skalierungs- bzw. Umrechnungsfaktoren ;-)
>
> Und weil solche Fehler entstehen und tatsächlich gemacht werden, ist es
> wichtig, die Schnittstelle so wasserdicht wie möglich zu machen:
>
> Eine Schnittstelle sollte leicht richtig und schwer falsch zu benutzen
> sein.
>
> Wenn aber alles ein double ist ohne Semantik, dann kommen eben solche
> Fehler vor.

Naja, eigentlich war es ja abgesprochen, dass dort alles in SI units 
übergeben wird. Wenn man da eine Funktion hat, deren Spec sagt "returns 
[value] in meters", es kommen aber feet raus oder sowas, dann ist da 
etwas falsch gelaufen. Da macht es dann auch keinen Unterschied, ob das 
ein falsch typisierter Wert ist oder nicht...

von batman (Gast)


Lesenswert?

Wilhelm M. schrieb:
>> Die Arithmetik wäre dieselbe und durch blindes Vertrauen in
>> die Entwicklungsumgebung und mangelndes Mitdenken entstehen genau solche
>> Fehler.
>
> Mit Ausnahme der Skalierungs- bzw. Umrechnungsfaktoren ;-)

Die nun immerhin in deiner Fantasie existieren, weil du im Nachhinein 
den Fehler erkannt hast. Das hilft nur leider bei der Entwicklung nix, 
wie man sieht.

von Wilhelm M. (wimalopaan)


Lesenswert?

Heiko L. schrieb:

> Naja, eigentlich war es ja abgesprochen, dass dort alles in SI units
> übergeben wird. Wenn man da eine Funktion hat, deren Spec sagt "returns
> [value] in meters", es kommen aber feet raus oder sowas, dann ist da
> etwas falsch gelaufen. Da macht es dann auch keinen Unterschied, ob das
> ein falsch typisierter Wert ist oder nicht...

Das schöne ist ja: wenn dort der DT Feet herauskommt, kann ich den ja 
auch ggf. korrekt(!) durch Kilogramm dividieren. Es ist eigentlich 
gaaanz einfach.

von Heiko L. (zer0)


Lesenswert?

Wilhelm M. schrieb:
> Das schöne ist ja: wenn dort der DT Feet herauskommt, kann ich den ja
> auch ggf. korrekt(!) durch Kilogramm dividieren. Es ist eigentlich
> gaaanz einfach.

Nicht, wenn sein Betrag Meter sind.

von Wilhelm M. (wimalopaan)


Lesenswert?

Heiko L. schrieb:
> Wilhelm M. schrieb:
>> Das schöne ist ja: wenn dort der DT Feet herauskommt, kann ich den ja
>> auch ggf. korrekt(!) durch Kilogramm dividieren. Es ist eigentlich
>> gaaanz einfach.
>
> Nicht, wenn sein Betrag Meter sind.

Nochmal:
1
auto d = distance();
2
auto time = 1_s;
3
Velocity v = d / time;

Jetzt ist es egal, ob der DT von d nun Meter oder Feet ist. Es gibt dann 
zwei Möglichkeiten:

a) die Division zwischen Feet und Second ist nicht definirt. Das 
Programm kompiliert nicht.

b) die Division ist definiert und berücksichtigt gleich den 
Umrechnungsfaktor.

Das Ergebnis ist in jedem Fall Velocity in der Einheit [m/s].

von Heiko L. (zer0)


Lesenswert?

Du kennst den Unterschied zwischen Einheit und Betrag doch.
Wenn man statt 5m 5ft schreibt, ist es falsch. Das geht auch mit 
Length<feet>(5) oder ähnlichem.

von Wilhelm M. (wimalopaan)


Lesenswert?

Heiko L. schrieb:
> Du kennst den Unterschied zwischen Einheit und Betrag doch.
> Wenn man statt 5m 5ft schreibt, ist es falsch. Das geht auch mit
> Length<feet>(5) oder ähnlichem.

Selbstverständlich kann ich eine Entfernung falsch abgeben.

Ich kann auch einfach die falsche Formel für den schiefen Wurf ansetzen. 
Ich kann N Fehler machen. Aber wenn ich durch ganz einfache Maßnahmen 
schon einige dieser Fehler ausschließen kann, bin ich schon ein Stück 
weiter.

Hinzu kommt noch, das
1
Meter d = 1_m;
selbsterklärend ist, was
1
double d = 0.001;
nicht ist.

Der Leitsatz ist: eine Schnittstelle sollte leicht richtig und schwer 
falsch zu benutzen sein!

: Bearbeitet durch User
von Heiko L. (zer0)


Lesenswert?

Wilhelm M. schrieb:
> Der Leitsatz ist: eine Schnittstelle sollte leicht richtig und schwer
> falsch zu benutzen sein!

Na das wird aber schwierig, wenn alle Typen maximal restringiert sind 
und jedes Modul seine eigenen Einheiten definiert.
Da hätte ich dieses passende Beispiel aus dem Bereich 3d-Grafik. 
Grundsätzlich hat da echt JEDE lib, die man benutzt seinen eigenen 
"Vector" definiert. Da passiert es dann schon mal, dass beim Umformen 
etwas falsch läuft... (buffer overflow)

von Wilhelm M. (wimalopaan)


Lesenswert?

Heiko L. schrieb:
> Wilhelm M. schrieb:
>> Der Leitsatz ist: eine Schnittstelle sollte leicht richtig und schwer
>> falsch zu benutzen sein!
>
> Na das wird aber schwierig, wenn alle Typen maximal restringiert sind
> und jedes Modul seine eigenen Einheiten definiert.
> Da hätte ich dieses passende Beispiel aus dem Bereich 3d-Grafik.
> Grundsätzlich hat da echt JEDE lib, die man benutzt seinen eigenen
> "Vector" definiert. Da passiert es dann schon mal, dass beim Umformen
> etwas falsch läuft... (buffer overflow)

Na dann gibt doch mal ein konkretes Beispiel ...

von batman (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Dann bist du wahrscheinlich auch der Meinung, dassdouble distance1 =
> 42.0; // km?
> double distance2 =  1.0; // mm? cm? who knows ...
> double l = distance1 + distance2;
> einen Wert in l von etwa 43.0 ergeben sollte?

Hier hast du noch selbst double für eine distance verwendet, als 
sinnvolles Beispiel im Gegensatz zur "sinnlosen" Addition von 
Fehlercodes. :)

Also was stimmt nun, sind nun alle Operationen der double-Arithmetik auf 
Distanzen sinnvoll und ist double ein geeigneter Datentyp für Distanzen 
(OHNE die Einheit zu berücksichtigen)?

Tja, hinterher ist man immer schlauer und weiß, wie es besser gewesen 
worden wäre, nech. ;)

von Wilhelm M. (wimalopaan)


Lesenswert?

batman schrieb:
> Wilhelm M. schrieb:
>> Dann bist du wahrscheinlich auch der Meinung, dassdouble distance1 =
>> 42.0; // km?
>> double distance2 =  1.0; // mm? cm? who knows ...
>> double l = distance1 + distance2;
>> einen Wert in l von etwa 43.0 ergeben sollte?
>
> Hier hast du noch selbst double für eine distance verwendet, als
> sinnvolles Beispiel im Gegensatz zur "sinnlosen" Addition von
> Fehlercodes. :)

Hast Du es auch gelesen, das Beispiel?
Du meinst also auch 42km + 1mm = 43km ?

Und schon ist die Raumsonde wieder vorbei ...


> Also was stimmt nun, sind nun alle Operationen der double-Arithmetik auf
> Distanzen sinnvoll und ist double ein geeigneter Datentyp für Distanzen
> (OHNE die Einheit zu berücksichtigen)?

Distanzen sind ohne Einheit gar nicht darstellbar.

von Vincent H. (vinci)


Lesenswert?

batman schrieb:
> Wilhelm M. schrieb:
>> Dann bist du wahrscheinlich auch der Meinung, dassdouble distance1 =
>> 42.0; // km?
>> double distance2 =  1.0; // mm? cm? who knows ...
>> double l = distance1 + distance2;
>> einen Wert in l von etwa 43.0 ergeben sollte?
>
> Hier hast du noch selbst double für eine distance verwendet, als
> sinnvolles Beispiel im Gegensatz zur "sinnlosen" Addition von
> Fehlercodes. :)
>
> Also was stimmt nun, sind nun alle Operationen der double-Arithmetik auf
> Distanzen sinnvoll und ist double ein geeigneter Datentyp für Distanzen
> (OHNE die Einheit zu berücksichtigen)?
>
> Tja, hinterher ist man immer schlauer und weiß, wie es besser gewesen
> worden wäre, nech. ;)


Du hast weder kapiert dass das Distanz Beispiel ein DONT und kein DO 
war, noch siehst du die Vorteile starker Typisierung bei der Arithmetik.

Also... nein, du bist hinterher nicht schlaucher, "nech". ;)

von batman (Gast)


Lesenswert?

Lies dir noch mal den ganzen Beitrag durch. Das DONT war da die Addition 
der Fehlercodes.

von batman (Gast)


Lesenswert?

..was er dann später auch wieder fallenließ und zur Multiplikation 
auswich. Naja, sinnloses Versteckspiel hier.

von Heiko L. (zer0)


Lesenswert?

Wilhelm M. schrieb:
> Na dann gibt doch mal ein konkretes Beispiel ...

Warum es problematisch ist, verschiedene Vector-Klassen mit OpenGL zu 
verwenden? Weil man immer an den Punkt kommt, wo man die Werte verwenden 
muss.
Da passiert es dann u.U. dass man glColor4fv schreibt, wo glColor3fv 
stehen müsste, wenn da zu viele Sachen durcheinander fliegen. C++ macht 
die Sache da potentiell noch unübersichtlicher, weil man dort ggf. 
implizite Conversions über primitive Typen triggern kann, die man aber 
leider braucht, weil der Source-Code sonst so clumsy wird.
Und das ist bei Sensoren nicht anders: in welcher Einheit arbeit das 
Ding?
Genauso wenig wie beim Datenaustausch im Netzwerk: Kommen die Werte in 
Big oder Little Endian?

Ich würde wetten, dass das ganze NASA Zeugs in ADA geschrieben war und 
es trotzdem nichts genützt hat...

von Heiko L. (zer0)


Lesenswert?

Hier ist übrigens noch eine interessante Studie zum Thema 
"Fehleranfälligkeit von Programmiersprachen"
https://cacm.acm.org/magazines/2017/10/221326-a-large-scale-study-of-programming-languages-and-code-quality-in-github/fulltext

von Nop (Gast)


Lesenswert?

Heiko L. schrieb:

> Ich würde wetten, dass das ganze NASA Zeugs in ADA geschrieben war und
> es trotzdem nichts genützt hat...

Die Sache war etwas komplexer, und natürlich hätte C++ hier nichts 
genutzt.

"The problem involved reading data from a file and a miscommunication 
about what the numbers in the file were. I don't know of any language, 
no matter how type-strict, that forces you to tag the string "123.45" in 
a file with the units of force (newtons vs foot-pounds), nor do I know 
of any language, no matter how type-loose, in which you could not impose 
such a convention if you wanted to."

Dazu kam dann noch, daß der Code ohne ausreichendes Review 
wiederverwendet wurde, obwohl seine Wichtigkeit von "reines Logfile" zu 
"missionsentscheidend" angestiegen war. Außerdem waren die Teams strikt 
getrennt. Die Bodenmannschaften am Ende waren auch noch unerfahren, 
sonst hätten sie das rechtzeitig bemerken können.

Übrigens wurde die Anomalie frühzeitig durchaus entdeckt, aber die 
Reports dazu wurden ignoriert.

https://skeptics.stackexchange.com/questions/7276/was-nasa-s-mars-climate-orbiter-lost-because-engineering-teams-used-different-me

von A. S. (Gast)


Lesenswert?

Nop schrieb:
> Richtig macht man das so, daß man in einem einzigen Datenformat und
> einer einzigen Einheit rechnet, und zwar konsistent und überall. Bei
> Bedarf kann bei der Ausgabe in andere Datentypen oder Einheiten
> konvertiert werden.

Ein Großteil der Diskussion um Einheiten hier resultiert wohl daher, 
dass einige den Sinn der Sätze nicht erfasst haben, weil sie selber noch 
nicht an dem Punkt waren.

 Ein auch im metrischen häufiges Beispiel wären Winkel, wo man Rad (für 
Mathe) und Grad (für Nutzer) braucht.

von mh (Gast)


Lesenswert?

Nop schrieb:
> Heiko L. schrieb:
>
>> Ich würde wetten, dass das ganze NASA Zeugs in ADA geschrieben war und
>> es trotzdem nichts genützt hat...
>
> Die Sache war etwas komplexer, und natürlich hätte C++ hier nichts
> genutzt.
>
> "The problem involved reading data from a file and a miscommunication
> about what the numbers in the file were. I don't know of any language,
> no matter how type-strict, that forces you to tag the string "123.45" in
> a file with the units of force (newtons vs foot-pounds), nor do I know
> of any language, no matter how type-loose, in which you could not impose
> such a convention if you wanted to."

Natürlich hätte C++ da nichts genützt. Aber das Prinzip der strong types 
lässt sich doch 1:1 Übertragen. In die Datei gehört neben dem Betrag der 
Kraft auch die Einheit. Natürlich kann man dann immer noch Fehler machen 
und man kann die Einheit einfach ignorieren. Aber es gibt keine 
Unklarheit über die Einheit dieses Werts.

> Dazu kam dann noch, daß der Code ohne ausreichendes Review
> wiederverwendet wurde, obwohl seine Wichtigkeit von "reines Logfile" zu
> "missionsentscheidend" angestiegen war. Außerdem waren die Teams strikt
> getrennt. Die Bodenmannschaften am Ende waren auch noch unerfahren,
> sonst hätten sie das rechtzeitig bemerken können.
>
> Übrigens wurde die Anomalie frühzeitig durchaus entdeckt, aber die
> Reports dazu wurden ignoriert.
>
> 
https://skeptics.stackexchange.com/questions/7276/was-nasa-s-mars-climate-orbiter-lost-because-engineering-teams-used-different-me

Das zeigt doch noch um so deutlicher, wie wichtig es ist Fehler so früh 
wie möglich zu verhindern, in diesem Fall indem man den Wert mit einer 
Einheit ausstattet.

von Nop (Gast)


Lesenswert?

mh schrieb:

> In die Datei gehört neben dem Betrag der Kraft auch die Einheit.

Genau das steht doch in dem Zitat - und das hätte man auch in einer 
Sprache mit schwacher bis fehlender Typisierung machen können. Man hätte 
es auch in C machen können. Hat man aber nicht, und DAS war das Problem.

Auch C++ hätte einen nicht gezwungen, Einheiten in die Datei zu 
schreiben - und man hätte es daher auch in C++ nicht gemacht. Und zwar 
aus denselben Gründen, wieso man es in der Realität nicht gemacht hat.

von mh (Gast)


Lesenswert?

Nop schrieb:
> mh schrieb:
>
>> In die Datei gehört neben dem Betrag der Kraft auch die Einheit.
>
> Genau das steht doch in dem Zitat - und das hätte man auch in einer
> Sprache mit schwacher bis fehlender Typisierung machen können. Man hätte
> es auch in C machen können. Hat man aber nicht, und DAS war das Problem.
>
> Auch C++ hätte einen nicht gezwungen, Einheiten in die Datei zu
> schreiben - und man hätte es daher auch in C++ nicht gemacht. Und zwar
> aus denselben Gründen, wieso man es in der Realität nicht gemacht hat.

Ok ... möchtest du vllt noch ein paar Wörter in dem Zitat weglassen, 
wenn du die anderen Sätze schon weglässt?

von Nop (Gast)


Lesenswert?

mh schrieb:

> Ok ... möchtest du vllt noch ein paar Wörter in dem Zitat weglassen,
> wenn du die anderen Sätze schon weglässt?

Ich bin auf das Wesentliche eingegangen. Das Zitat von Norvig hat Deinen 
Punkt schließlich schon vorweggenommen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Die Problematik beim Mars Orbiter ist dieselbe wie in diesem Thread 
generell:

Hier gibt es Leute, die sagen:

A) Es ist doch alles dokumentiert, es kann deswegen nichts schief gehen.

oder

B) Wir wissen, dass es nicht gut / falsch ist. Deswegen ändern wir es 
nicht.

Genauso wurde damals gehandelt.

Es ist schon erschütternd, das manche nicht einmal die Chance ergreifen, 
Dinge zu verbessern.

Aus der Rezension zu "Elements of Programming":

Ask a mechanical, structural, or electrical engineer how far they would 
get without a heavy reliance on a firm mathematical foundation, and they 
will tell you, ‘not far.’ Yet so-called software engineers often 
practice their art with little or no idea of the mathematical 
underpinnings of what they are doing. And then we wonder why software is 
notorious for being delivered late and full of bugs, while other 
engineers routinely deliver finished bridges, automobiles, electrical 
appliances, etc., on time and with only minor defects.

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Yet so-called software engineers often
> practice their art with little or no idea of the mathematical
> underpinnings of what they are doing.

Sowas wie z.B. OOP hat auch keine mathematische Grundlage. Und im 
Übrigen braucht man sich nur mal Code von Mathematikern und 
Wissenschaftlern anzuschauen, diese Fortranwüsten (auch dann, wenn sie 
nicht in Fortran geschrieben sind).

Konkret zum Mars-Orbiter hätte mein Vorschlag von 1500 Postings weiter 
oben geholfen: man rechnet projektübergreifend immer im selben System. 
Das schafft Konsistenz. Dazu hätte man keine abgefahrene Arithmetik 
gebraucht, die hier im Übrigen wegen der Schnittstelle der Datei sowieso 
nicht geholfen hätte.

von mh (Gast)


Lesenswert?

Nop schrieb:
> Wilhelm M. schrieb:
>> Yet so-called software engineers often
>> practice their art with little or no idea of the mathematical
>> underpinnings of what they are doing.
>
> Sowas wie z.B. OOP hat auch keine mathematische Grundlage. Und im
> Übrigen braucht man sich nur mal Code von Mathematikern und
> Wissenschaftlern anzuschauen, diese Fortranwüsten (auch dann, wenn sie
> nicht in Fortran geschrieben sind).

Du hast offensichtlich sowas von keine Ahnung, was Mathematiker und 
Wissenschafler für Software schreiben.

von Nop (Gast)


Lesenswert?

mh schrieb:

> Du hast offensichtlich sowas von keine Ahnung, was Mathematiker und
> Wissenschafler für Software schreiben.

Ich hab davon so einiges gesehen, danke der Nachfrage.

von Wilhelm M. (wimalopaan)


Lesenswert?

Nop schrieb:
> Wilhelm M. schrieb:
>> Yet so-called software engineers often
>> practice their art with little or no idea of the mathematical
>> underpinnings of what they are doing.
>
> Sowas wie z.B. OOP hat auch keine mathematische Grundlage.

Ja, wissen wir mittlerweile: nur 2'er-Komplimentarithmetik des Datentyps 
int ist wahre Mathematik ;-)

Kennst Du das Buch überhaupt, auf das sich die Rezension bezieht?

> Und im
> Übrigen braucht man sich nur mal Code von Mathematikern und
> Wissenschaftlern anzuschauen,

Genau: Ritchie war Mathematiker, Keringhan war Physiker, Thompson war 
E-Techniker. LOL!

> Konkret zum Mars-Orbiter hätte mein Vorschlag von 1500 Postings weiter
> oben geholfen: man rechnet projektübergreifend immer im selben System.

Ja genau: hat ja wahnsinnig geholfen.

> Das schafft Konsistenz. Dazu hätte man keine abgefahrene Arithmetik
> gebraucht,

Von den Grundrechenarten einige weg zu lassen ist für die schon 
abgefahrene Arithmetik. WoW! Kein Wunder, dass Du mit anderen Dingen 
schnell überfordert bist.

von Nop (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Ja genau: hat ja wahnsinnig geholfen.

Hätte geholfen - wenn man ihn denn gemacht hätte. Vorschläge, die man 
nicht macht bzw. verwirft, helfen nicht soviel. D'oh!

> Von den Grundrechenarten einige weg zu lassen ist für die schon
> abgefahrene Arithmetik.

Es geht nicht um "weglassen". Worum es geht, habe ich mehrfach erklärt. 
Wenn Du es nicht verstehst, lies es nochmal nach - diesmal möglichst 
sinnerfassend.

von Markus F. (mfro)


Lesenswert?

Blöd ist, überhaupt erst auf die Idee zu kommen, man könne Flugbahnen 
(oder in diesem Fall - Drehmomente) mit irgendwas anderem als mit 
SI-Einheiten berechnen (so was kann nur einem Ami einfallen, ich rechne 
ja auch nicht mit kp/Elle, was wertmässig etwa auf dasselbe rauskäme).

Noch blöder ist, wenn die verwendete Einheit (foot-pound) nur um den 
Faktor 1,35 "daneben" liegt, da helfen auch "ingenieurmässige 
Überschlagsrechnungen" angesichts der sowieso vorhandenen 
Ungenauigkeiten wenig.

Aus diesem Kardinalfehler Softwareengineering-Regeln ableiten zu wollen, 
erscheint mir persönlich etwas weit hergeholt.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Es ist schon erschütternd, das manche nicht einmal die Chance ergreifen,
> Dinge zu verbessern.
>
> Aus der Rezension zu "Elements of Programming":
>
> Ask a mechanical, structural, or electrical engineer how far they would
> get without a heavy reliance on a firm mathematical foundation, and they
> will tell you, not far. Yet so-called software engineers often
> practice their art with little or no idea of the mathematical
> underpinnings of what they are doing. And then we wonder why software is
> notorious for being delivered late and full of bugs, while other
> engineers routinely deliver finished bridges, automobiles, electrical
> appliances, etc., on time and with only minor defects.

Statt ein Buch zu lesen, das erklärt, wie man seine Programme mit viel
Mühe mathematisch untermauert, könnte man auch eine Programmiersprache
lernen, die diese Untermauerung schon von vornherein mitbringt, nämlich
Haskell.

Das Sprachkonzept, das Typsystem, die in der Standardbibliothek
vordefinierten Typen und Typklassen und nicht zuletzt die Syntax sind
stärker als bei allen anderen mir bekannten Programmiersprachen
mathematisch beeinflusst. Zudem erzwingt die Sprache eine mathematische
Denkweise des Programmierers, weswegen schlampige Programmierung in
Haskell sehr schwer ist.

Der Softwarebug des Mars-Orbiters hat seine Ursache aber nicht in der
fehlenden mathematischen Untermauerung der Software, sondern ganz
einfach in der fehlenden Kommunikation zwischen den Entwicklern. Reden
diese nicht miteinander, hilft auch noch so viel Mathematik nicht,
Fehler zu vermeiden.

von Carl D. (jcw2)


Lesenswert?

Niemand wird daran gehindert, die "distance" Klasse inklusive 
Stream-Io-Operatoren zu implementieren. Dann MUSS eben in der Datei eine 
Einheit dabei stehen. Und dann ist es egal, ob man Unterarme, Daumen 
oder Füße als Referenz benutzt oder ob man auf klimatisierte 
Edelmetallzylinder schwört, das Ergebnis in der eingelesenen Variable 
ist eine Distanz.

von Nop (Gast)


Lesenswert?

Carl D. schrieb:
> Dann MUSS eben in der Datei eine Einheit dabei stehen.

Die hätte man aber selbst dann haben können, wenn die schreibende Seite 
in DOS-Basic gewesen wäre. Dann wäre übrigens der anderen Projektseite 
auch klar gewesen, was da eigentlich steht, selbst wenn die in 
Excelmacros gearbeitet hätte.

Der Punkt ist, daß das eben nicht gemacht wurde. Der Einheitenfehler kam 
eben nicht so zustande, wie hier im Thread zunächst suggeriert wurde.

von Carl D. (jcw2)


Lesenswert?

Nop schrieb:
> Carl D. schrieb:
>> Dann MUSS eben in der Datei eine Einheit dabei stehen.
>
> Die hätte man aber selbst dann haben können, wenn die schreibende Seite
> in DOS-Basic gewesen wäre. Dann wäre übrigens der anderen Projektseite
> auch klar gewesen, was da eigentlich steht, selbst wenn die in
> Excelmacros gearbeitet hätte.
>
> Der Punkt ist, daß das eben nicht gemacht wurde. Der Einheitenfehler kam
> eben nicht so zustande, wie hier im Thread zunächst suggeriert wurde.

Man kann das sogar in Assembler manchen, aber es sollte eben
  "Easy TO use correct"
und
  "Difficult TO use incorrect"

Denn codierende Dumpfbacken findet man überall und muß man halt 
irgendwie unter Kontrolle bringen. Alternativ abschrecken und damit los 
werden. Klappt!

von Nop (Gast)


Lesenswert?

Carl D. schrieb:

> Man kann das sogar in Assembler manchen, aber es sollte eben
>   "Easy TO use correct"
> und
>   "Difficult TO use incorrect"

Das wäre durch Einheiten in der Datei bereits erfüllt worden, ebenso wie 
durch konsistente Benutzung ein- und desselben Einheitensystems im 
ganzen Projekt. Das ganze Beispiel taugt somit eben nicht zur 
Untermauerung von Spezialarithmetik, die in diesem Fall den Fail auch 
nicht verhindert hätte - denn an dem Punkt, wo die zum Zug gekommen 
wäre, wäre der Fehler bereits passiert gewesen.

Einfach mal hören "Mars-Sonde, Einheitenfehler" und dann drauflos 
argumentieren funktioniert so nicht.

von Carl D. (jcw2)


Lesenswert?

Nop schrieb:
> Carl D. schrieb:
>
>> Man kann das sogar in Assembler manchen, aber es sollte eben
>>   "Easy TO use correct"
>> und
>>   "Difficult TO use incorrect"
>
> Das wäre durch Einheiten in der Datei bereits erfüllt worden, ebenso wie
> durch konsistente Benutzung ein- und desselben Einheitensystems im
> ganzen Projekt. Das ganze Beispiel taugt somit eben nicht zur
> Untermauerung von Spezialarithmetik, die in diesem Fall den Fail auch
> nicht verhindert hätte - denn an dem Punkt, wo die zum Zug gekommen
> wäre, wäre der Fehler bereits passiert gewesen.
>
> Einfach mal hören "Mars-Sonde, Einheitenfehler" und dann drauflos
> argumentieren funktioniert so nicht.

Die "Spezialarithmetk" wäre schon durch den ersten Test gefallen, hätte 
sie Zahlensalat ohne Einheiten vorgefunden.

Ich z.B. bin froh, daß mein Gehalt mit Wert und Einheit überwiesen wird 
und keiner unterwegs Rätseln muß ob das Euro oder Rupien sein sollen.

Das Mars-Problem war nicht, daß die Teams nicht vereinbart hatten, was 
in der Datei stehen soll, sondern daß sie es nicht reingeschrieben 
haben. Dazu brauch ich keinen Bericht lesen, das kann mein Hirn 
selbstständig ableiten.

von Nop (Gast)


Lesenswert?

Carl D. schrieb:

> Das Mars-Problem war nicht, daß die Teams nicht vereinbart hatten, was
> in der Datei stehen soll, sondern daß sie es nicht reingeschrieben
> haben.

Eben. Hätten sie es aber getan, dann wäre auf der Empfängerseite mit 
ganz banaler double-Arithmetik alles paletti gewesen.

Sie haben es übrigens deswegen nicht reingeschrieben, weil der Code 
ursprünglich bloß für eine unwesentliche Log-Funktion war - und dank 
Wiederverwendung dann auf einmal missionsentscheidend wurde.

Direkte Wiederverwendung von Code ist keine so gute Idee, wenn dabei 
dessen Wichtigkeitsstufe massiv ansteigt. Wie man schon aus menschlicher 
Textanalyse weiß, ist Text eben nicht nur ein Dokument, hier der Code, 
sondern eben auch das, was nicht im Dokument selber steht, also der 
Kontext.

Das ist eine Erkenntnis, die ausgerechnet für Geisteswissenschaftler 
absolut trivial gewesen wäre, die aber in "hard sciences" oftmals 
untergeht.

von A. S. (Gast)


Lesenswert?

“Numbers as invented by mathematicians are abstract and beautiful. This 
should not be spoiled by engineers”

(Die Reaktion vor 50 Jahren, mit der Einheitenrechnungen der Ingenieure 
abgewiegelt wurden)

von MaWin (Gast)


Lesenswert?

Warum glauben so viele, man könne dem Einheitenchaos begegnen, indem man 
die Einheiten mit Magie irgendwie konvertiert?

Das Gegenteil ist der Fall.
Wenn man 1 au + 1 mm rechnet, dann kann man das in C++ sicher machen mit 
Operatorüberladung und Typen.
Sinnvoll ist es trotzdem nicht, denn das Programm ist trotzdem falsch.
Z.B. Rundungsfehler werden so nur noch besser vor dem Programmierer 
versteckt. (vgl. https://en.wikipedia.org/wiki/Loss_of_significance )

Besser einigt man sich global im Projekt auf sinnvolle Einheiten und 
verwendet diese dann konsistent.
Dabei hilft einem die Programmiersprache kaum.

Interfaces definieren ist ein soziales und kein technisches Problem.

von Nop (Gast)


Lesenswert?

MaWin schrieb:

> Interfaces definieren ist ein soziales und kein technisches Problem.

Wobei diese Architektur mit zwei Systemen und verschiedenen 
Einheitensystemen dafür ja ein Beispiel ist - Stichwort "Conways's Law".

Und ja, soziale Probleme mit technischen Mitteln lösen zu wollen 
funktioniert meistens nicht - was Geeks nicht daran hindert, es immer 
wieder zu versuchen. Liegt auch daran, daß sie die Problemklasse oftmals 
nur mangelhaft verstehen.

von Carl D. (jcw2)


Lesenswert?

Nop schrieb:
> Carl D. schrieb:
>
>> Das Mars-Problem war nicht, daß die Teams nicht vereinbart hatten, was
>> in der Datei stehen soll, sondern daß sie es nicht reingeschrieben
>> haben.
>
> Eben. Hätten sie es aber getan, dann wäre auf der Empfängerseite mit
> ganz banaler double-Arithmetik alles paletti gewesen.
>
> Sie haben es übrigens deswegen nicht reingeschrieben, weil der Code
> ursprünglich bloß für eine unwesentliche Log-Funktion war - und dank
> Wiederverwendung dann auf einmal missionsentscheidend wurde.

Ok, in Summe: weil man sich keine Mühe gemacht hat, es von Anfang an 
richtig zu machen, ist richtig machen blöd.
Typische Ausrede für Frickel-Code, "reicht doch so", die ich in 30Jahren 
Softwareentwicklung oft genug gehört hab.

> Direkte Wiederverwendung von Code ist keine so gute Idee, wenn dabei
> dessen Wichtigkeitsstufe massiv ansteigt.

Wenn er eben nach der Methode "reicht schon" hingeschmiert wurde. Wenn 
es dabei um Aufwand gehen sollte, dann könnte man auch nach dem Risiko 
fragen. 5Tage gespart + x% Risiko einen 9-Stelligen Betrag zu verlieren. 
Eigentlich eine simple Rechnung. Zumal "richtig" oft nicht länger 
dauert, nur eben halt bei manchen länger als ihre Laufzeit.

> Wie man schon aus menschlicher
> Textanalyse weiß, ist Text eben nicht nur ein Dokument, hier der Code,
> sondern eben auch das, was nicht im Dokument selber steht, also der
> Kontext.
>
> Das ist eine Erkenntnis, die ausgerechnet für Geisteswissenschaftler
> absolut trivial gewesen wäre, die aber in "hard sciences" oftmals
> untergeht.

Geisteswissenschaftler mögen über den Inhalt einer Datei diskutieren und 
ihn interpretieren, wie sie wollen. Bei dem Projekt waren Ingenieure 
gefragt, die legen fest.

von MaWin (Gast)


Lesenswert?

Carl D. schrieb:
> Wenn er eben nach der Methode "reicht schon" hingeschmiert wurde.

Nein.

Jedes Programm, jeder Programmabschnitt, hat eine Anforderung.
Die Anforderungen an Code der printf befüttert sind ganz andere, als die 
Anforderungen an Code, der Steuerdüsen befeuert.

Wenn jetzt ein Entwickler hingeht und eine printf-Funktion einfach so 
zur Ansteuerung von Steuerdüsen wiederverwendet, dann ist das 
Anforderungsmanagement sehr sehr sehr sehr kaputt. Oder hat der 
Entwickler einfach ignoriert, dass die Anforderungen der Funktion gar 
nicht zu seinen Anforderungen passen? Das ist ein massiver Fehler in 
der Softwareentwicklung. Und zwar lange bevor auch nur eine Zeile des 
fehlerhaften Codes geschrieben wurde.

Der kleinste Teil der Softwareentwicklung ist die Programmierung.

von Nop (Gast)


Lesenswert?

Carl D. schrieb:

> Ok, in Summe: weil man sich keine Mühe gemacht hat, es von Anfang an
> richtig zu machen, ist richtig machen blöd.

Hat zwar nichts mit dem zu tun, was ich geschrieben habe, aber wird 
schon passen.

> Wenn er eben nach der Methode "reicht schon" hingeschmiert wurde.

Man hat für den unwichtigen Code, ALS er unwichtig war, einen 
Junior-Entwickler drangesetzt. In der realen Welt hat man nicht die 
Ressourcen, auch die unwichtigen Sachen von den Obercracks machen zu 
lassen, weil es für die Menge an Code, die weltweit gebraucht wird, 
nicht genug Obercracks gibt.

Das ist auch völlig in Ordnung so - nur die Hochstufung war eben nicht 
OK. In normaler Luftfahrt wäre es ja auch nicht möglich, daß man Code 
aus einem unwichtigen System wie Inflight-Entertainment hernimmt, den 
mal eben in den Turbinencontroller setzt und mit "paßt schon" abnickt.

> Geisteswissenschaftler mögen über den Inhalt einer Datei diskutieren und
> ihn interpretieren, wie sie wollen. Bei dem Projekt waren Ingenieure
> gefragt, die legen fest.

Oder auch nicht. Schlimmer noch, ihnen war weder beim Code noch bei der 
Datei die Bedeutung von "Kontext" bewußt, weswegen sie auch nicht drauf 
gekommen sind, daß sie da etwas festlegen hätten sollen. Eben um 
relevante Information aus dem Kontext in den Text zu ziehen.

Ändert alles nichts daran, daß die Spezial-Arithmetik zum Vermeiden von 
Einheitenfehlern hier weder notwendig noch hinreichend gewesen wäre.

von S. R. (svenska)


Lesenswert?

MaWin schrieb:
> Warum glauben so viele, man könne dem Einheitenchaos begegnen, indem man
> die Einheiten mit Magie irgendwie konvertiert?

Zumal auch nicht alle Einheiten verlustfrei ineinander umgerechnet 
werden können. Loss of Significance ist da nur ein Faktor.

von Heiko L. (zer0)


Lesenswert?

Carl D. schrieb:
> Ok, in Summe: weil man sich keine Mühe gemacht hat, es von Anfang an
> richtig zu machen, ist richtig machen blöd.

Was soll "richtig" denn hier bedeuten? Vermutlich hat der Code genau 
seine ursprüngliche Spec erfüllt. Das war einfach Zweckentfremdung nach 
dem Motto "Wir brauchen etwas hartes, um den Nagel in die Wand zu 
kriegen."
Ein Logfile ist dazu bestimmt, menschenlesbar zu sein und schon deswegen 
zur Kommunikation zwischen Systemen völlig ungeeignet.

von Carl D. (jcw2)


Lesenswert?

Heiko L. schrieb:
> Carl D. schrieb:
>> Ok, in Summe: weil man sich keine Mühe gemacht hat, es von Anfang an
>> richtig zu machen, ist richtig machen blöd.
>
> Was soll "richtig" denn hier bedeuten? Vermutlich hat der Code genau
> seine ursprüngliche Spec erfüllt. Das war einfach Zweckentfremdung nach
> dem Motto "Wir brauchen etwas hartes, um den Nagel in die Wand zu
> kriegen."
> Ein Logfile ist dazu bestimmt, menschenlesbar zu sein und schon deswegen
> zur Kommunikation zwischen Systemen völlig ungeeignet.

Worin besteht das Problem in einem Logfile 42km von 42inch unterscheiden 
zu können? Weil man dann weniger raten kann.
Wenn man Längen protokolliert, dann bestehen die eben aus mehr als einer 
Zahl, dannist es menschen- und maschinenlesbar.
Alles andere ist Pfusch, besonders im hunderte-Mega-$/€-Bereich.

: Bearbeitet durch User
von Heiko L. (zer0)


Lesenswert?

Carl D. schrieb:
> Worin besteht das Problem in einem Logfile 42km von 42inch unterscheiden
> zu können? Weil man dann weniger raten kann.

Es gibt halt unterschiedliche Konventionen in unterschiedlichen 
Bereichen. Geschwindigkeiten werden oft nicht in m/s angegeben. Das wird 
schon so gewesen sein, wie einstmals gedacht war. In ein Logfile eine 
Flughöhe in Inches einzutragen ist absurd, auch wenn es korrekt ist.

von Einer K. (Gast)


Lesenswert?

Heiko L. schrieb:
> In ein Logfile eine
> Flughöhe in Inches einzutragen ist absurd, auch wenn es korrekt ist.
feet

von S. R. (svenska)


Lesenswert?

Auch mm oder yards wären korrekt...

von Carl D. (jcw2)


Lesenswert?

S. R. schrieb:
> Auch mm oder yards wären korrekt...

Mit Meilen wäre man aber viel flexibler. Die gibt es in Einheiten von 
500..11299 SI-Meter. Aber immer wichtig: keine Einheiten reinschreiben!
</ironie>
Wenn ich hier so manches lese, bin ich froh die Schreiber nie persönlich 
zu treffen.

von S. R. (svenska)


Lesenswert?

Ich sprach nicht von "gut", ich sprach von "korrekt".

Und nein, zwingend Meter oder andere SI-Einheiten in Logfiles schreiben 
ist nicht überall die beste Idee. Wenn das intern verwendete 
Einheitensystem aus Timer-Ticks und Fixpoint-Arithmetik besteht, ist 
eine Float-Umrechnung fürs Log vermutlich sogar ziemlich doof.

Aber was schreibe ich hier, ihr habt die Weisheit natürlich auch alle 
mit Löffeln gefressen.

von Einer K. (Gast)


Lesenswert?

S. R. schrieb:
> Aber was schreibe ich hier, ihr habt die Weisheit natürlich auch alle
> mit Löffeln gefressen.

Wie so "auch"?

Wenn dann: ICH!

von Nop (Gast)


Lesenswert?

Es ist übrigens völlig normal, daß die Einheit nicht mitgesendet wird. 
Das wird es in Autos und Flugzeugen auch nicht. Was es da aber gibt, 
sind klare Dokumente, die das Nachrichtenformat im Detail beschreiben, 
und da steht die Einheit dann drin.

von Schönen Sonntag (Gast)


Lesenswert?

Ihr diskutiert sehr nahe am Thema. :-(

Der Thread wurde geentert und ist gekentert.

von Einer K. (Gast)


Lesenswert?

Naja...

Das ursprüngliche Thema wurde schon längst tot geritten.
Es wurde doch alles schon dazu gesagt.
Nur noch nicht von jedem.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.