W.A. schrieb:> verwendeten Programmiersprache
Programmiersprache ist C.
Compiler AtmelStudio 7.
Wenn es funktioniert, wäre es besonders "unsauber" programmiert?
__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.
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?
good coding schrieb:> Wilhelm M. schrieb:>> Begründung>> conditional breakpoint, Variable im Debugger verändern, ..., typesicher,> ...
In diesem switch-stmt? Wozu?
Typ-sicher?
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_tselector=func();
2
switch(selector)
3
{
erfüllt seinen Zweck. ist kein wesentlicher Mehraufwand. Erzeugt keinen
Overhead bei Optimierung und hat keine [versteckten] Nebeneffekte.
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:
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_tselector=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?
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.
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)
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.
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
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.
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.
__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.
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 ...
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.
> 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.
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.
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.
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.
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.
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.
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.
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;
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.
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.
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?
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.
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.
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.
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.
> 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.
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.
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.
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?
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.
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:
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_terfolg=try_get_herrschaft(&welt);
2
switch(erfolg)
3
{
4
caseOK:display("Beuget die Knie!");break;
5
caseFAILED:display("Entschuldigung, war nicht so gemeint.");break;
6
caseNOT_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_terfolg=try_get_herrschaft(&welt);
2
switch(erfolg)
3
{
4
caseOK:display("Beuget die Knie!");break;
5
caseFAILED:display("Entschuldigung, war nicht so gemeint.");break;
6
caseNOT_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.
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
caseRUNNING:bla();break;
4
caseOFF:fasel();break;
5
caseERROR: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_terfolg=try_get_herrschaft(&welt);
2
switch(erfolg)
3
{
4
caseOK:display("Beuget die Knie!");break;
5
caseFAILED:display("Entschuldigung, war nicht so gemeint.");break;
6
caseNOT_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.
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_terfolg=try_get_herrschaft(&welt);
2
>switch(erfolg)
3
>{
4
>caseOK:display("Beuget die Knie!");break;
5
>caseFAILED:display("Entschuldigung, war nicht so gemeint.");
6
>break;
7
>caseNOT_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:
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.
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.
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_terfolg=try_get_herrschaft(&welt);
3
switch(erfolg)
4
{
5
caseOK:display("Beuget die Knie!");break;
6
caseFAILED:display("Entschuldigung, war nicht so gemeint.");break;
7
caseNOT_FOUND:display("Wo ist die Welt hin?");retry_herrschaft=false;break;
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:
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_terfolg=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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
__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?
__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?
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.
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!
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.
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.
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.
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.
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.
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.
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
__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.
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?
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?
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. ;-)
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
intrtc=foo();
2
switch(rtc)
3
...
ist das einfach:
1
intrtc=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
intrtc=foo();
2
switch(rtc)
3
...
ist das einfach:
1
intrtc=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!"
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
>intrtc=foo();
2
>switch(rtc)
3
>...
4
>
>> ist das einfach:>>
1
>intrtc=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.
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.
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
returnrtc;
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.
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(autoresult=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
>returnrtc;
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).
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.
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ß ;-)
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!
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ß ;-)
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.
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.
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
intresult;
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.
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
>intresult;
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.
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
>intresult;
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.
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.
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.
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
> 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.
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.
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!
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 ...
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.
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.
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
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
autodistance=13_km;
2
autotime=1_sec;
3
4
autospeed=distance/time;
"Unsinnige" Operationen werden nicht realisiert. Dann kann fast nichts
mehr schief gehen.
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.
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.
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.
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.
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.
@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.
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.
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.
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.
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.
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.
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.
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!"
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.
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.
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....
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?
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.
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?
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.
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.
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! :)
batman schrieb:> Aha. Es soll ja auch Leute geben, die schachteln gleich 3 oder sogar 4> (vier) Funktionen ineinander. Boooaaaaaaa! :)
Nennt sich "functional programming". ;-)
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:
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?
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
intmain(){
4
intc=EOF;
5
while((c=getchar())!=EOF){
6
if(putchar(c)==EOF){
7
return1;
8
}
9
}
10
return0;
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
intmain(){
2
while(autoc=get<AsciiChar>()){
3
if(!put(*c)){
4
return1;
5
}
6
// c *= 3; // nicht sinnvoll
7
// c <<= 2; // nicht sinvoll
8
}
9
// hier kein c mehr existent
10
11
while(autoc=get<std::byte>()){
12
if(!put(*c)){
13
return1;
14
}
15
// c *= 3; // nicht sinnvoll
16
// c <<= 2; // sinnvoll und machbar
17
}
18
return0;
19
}
Zum nachvollziehen ist dazu folgendes noch notwendig:
1
structAsciiChar{
2
explicitAsciiChar(unsignedcharv):value(v){
3
assert(value<128);
4
}
5
unsignedcharvalue{};
6
};
7
8
template<typenameT>std::optional<T>get();
9
10
template<>
11
std::optional<AsciiChar>get<AsciiChar>(){
12
if(intc=getchar();c!=EOF){
13
returnAsciiChar{static_cast<unsignedchar>(c)};
14
}
15
return{};
16
}
17
template<>
18
std::optional<std::byte>get<std::byte>(){
19
if(intc=getchar();c!=EOF){
20
returnstd::byte(c);
21
}
22
return{};
23
}
24
25
boolput(AsciiCharc){
26
returnputchar(c.value)!=EOF;
27
}
28
boolput(std::bytec){
29
returnputchar(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 ...)
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...
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.
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.
Ü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.
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.
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.
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
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.
;-)
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.
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.
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
unsignedcharc=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_tconvert(constData*);
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(constData&);
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
AsciiCharc1,c2;
2
autom=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.
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.
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.
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.
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.
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.
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.
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:
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.
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.
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.
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(intc=getchar();c!=EOF){
4
returnstd::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.
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.
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(intc=getchar();c!=EOF){
4
>returnstd::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.
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:
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.
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.
was von einigen anderen als unsauber, schwer zu debuggen, etc. teilweise
ohne Begründung abgelehnt wurde, und teilweise zu
1
{
2
intv=foo();
3
switch(v){
4
...
5
}
6
}
mutierte, habe ich ein
1
switch(autov=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(constautoc=get<AsciiChar>()){
2
if(!put(*c)){
3
return1;
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?
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?!
@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.
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.
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 ;-)
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
doubledistance1=42.0;// km?
2
doubledistance2=1.0;// mm? cm? who knows ...
3
doublel=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
inta=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?
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ö.
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.
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.
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...
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 ;-)
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.
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.
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.
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
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.
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.
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.
>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!"
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 ... ;-)
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...
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.
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.
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.
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
autod=distance();
2
autotime=1_s;
3
Velocityv=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].
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.