Forum: Compiler & IDEs Code unleserlich schreiben um Zeilen zu sparen.


von Dr. Sommer (Gast)


Lesenswert?

Possetitjel schrieb:
> Frage am Rande: Aufgrund welcher Verschwörung fehlt
> Tcl regelmäßig in solchen Aufzählungen?

Weil das keine besonders verbreitete Sprache ist, und funktionale 
Programmierung nur ein Teilaspekt ist, im Gegensatz zu z.B. Haskell? In 
C++ kann man auch funktional programmieren, insbesondere das 
template-System ist quasi funktionale Programmierung.

von Datenlutscher (Gast)


Lesenswert?

Gerhard O. schrieb:
> Ich habe mir das Wiki dazu durchgelesen. Leider verstehe ich von der
> Unterliegenden Mathe zum Teil nur Bahnhof. Das ist mir zu theoretisch.
Lamdakalkül für Doofies: "Funktionen können Funktionen als Parameter 
haben."
Und schon hast du die funktionale Programmierung verstanden.

von Markus F. (mfro)


Lesenswert?

Dr. Sommer schrieb:
> char* greeting (const char* firstName, size_t firstNameLength, const
> char* lastName, size_t lastNameLength, ) {
>   const char a [] = "Hallo, ";
>   char* res = malloc (sizeof (a) + firstNameLength + lastNameLength +
> 2);
>   if (!res) return NULL;
>   memcpy (res, a, sizeof (a));
>   memcpy (res+sizeof (a), firstName, firstNameLength);
>   res [sizeof (a)+firstNameLength] = ' ';
>   memcpy (res+sizeof (a)+firstNameLength+1, lastName, lastNameLength);
>   res [sizeof (a)+firstNameLength+lastNameLength+1] = ' ';
>   return res;
> }

Ich kenne keinen C-Programmierer, der so was schreiben würde. Auch die 
Erfinder der String-Library haben sich dabei was gedacht.

Die Entsprechung zum C++-Beispiel sieht eher so aus:
1
 char *res = malloc(strlen(a) + strlen(firstName) + strlen(lastName) + 2);
2
 if (res != NULL)
3
      strcat(strcat(strcat(strcpy(res, a), firstName), " "), lastName);
4
 return res;

Ich gebe zu, längst nicht so schön wie die C++-Variante, aber deutlich 
"hübscher" und lesbarer als deine.

von Dr. Sommer (Gast)


Lesenswert?

Markus F. schrieb:
> Die Entsprechung zum C++-Beispiel sieht eher so aus:

Funktioniert nicht mit Strings die 0-Bytes enthalten. Zugegeben, das ist 
bei Real-Namen nicht besonders sinnvoll, aber es gibt oft Situationen wo 
man das braucht. Besonders effizient ist das ständige Iterieren mittels 
strlen auch nicht.

von Possetitjel (Gast)


Lesenswert?

Dr. Sommer schrieb:

> Possetitjel schrieb:
>> Ich habe überhaupt nichts gegen höhere Abstraktion, wenn
>> zuvor die einfachen, leicht zugänglichen Modelle und
>> Vorstellungen erklärt würden.
>
> Ich finde OOP sehr intuitiv und verständlich.

OOP ist für mich ein einfaches und sehr sinnvolles Konzept,
das typischerweise auf eine für mich absolut unverständliche
Art erklärt wird.

Ich habe sie nur durch konsequentes Umdeuten aller Begriffe
in Kategorien der Automatentheorie verstanden, und ich werde
immer noch hippelig, wenn ich Auslassungen echter Informatiker
über OOP lesen muss. Ich kann den Frust darüber, wie man eine
einfache und sinnvolle Sache dermaßen frachtbriefmäßig
umständlich erklären kann, nur schlecht unterdrücken.


> Kurioserweise haben meistens die E-Techniker Probleme damit,
> obwohl da eigentlich noch mehr in Komponenten und Black-Boxes
> gedacht wird (IC's, Platinen, Baugruppen, ...).

Das ist sehr interessant, dass Du das sagst, weil ich mich in
meiner Vermutung bestätigt sehe, dass es nicht das Konzept
an sich ist, das die E-Techniker abstößt, sondern die Art und
Weise der Darstellung.

Es könnte sein, dass Wilhelm auf eine Art Recht hat, die ich
vorhin nicht gesehen habe: Möglicherweise erwerben Informatiker
trotz der Überbetonung von Sprachen und Grammatiken genügend
Wissen über Automatentheorie, so dass sich für sie die Konzepte
der OOP quasi nebenbei durch genaues Hinsehen erschließen.

E-Techniker haben diese Kenntnisse der Automatentheorie nicht
zwingenderweise; also entsteht da erstmal eine Blockade. Es
ist aber, um einigermaßen Programmieren zu können, gar nicht
notwendig, die Initiation durch TheoInf I + II über sich
ergehen zu lassen -- es genügt, den deutlich kleineren Teil
des Feldes zu beherrschen, der sich mit (endlichen) Automaten
befasst. Das wiederum sehen die Informatiker nicht, weil ihnen
mangels Notwendigkeit nicht bewusst ist, dass man Automaten-
theorie auch betreiben kann, ohne sich um die Korrespondenzen
zu den Grammatiken zu scheren.

von Markus F. (mfro)


Lesenswert?

Dr. Sommer schrieb:
> ... Strings die 0-Bytes enthalten...

das ist für C-Programmierer ein Oxymoron.

von Possetitjel (Gast)


Lesenswert?

Dr. Sommer schrieb:

> Possetitjel schrieb:
>> Frage am Rande: Aufgrund welcher Verschwörung fehlt
>> Tcl regelmäßig in solchen Aufzählungen?
>
> Weil das keine besonders verbreitete Sprache ist, und
> funktionale Programmierung nur ein Teilaspekt ist, im
> Gegensatz zu z.B. Haskell?

Okay, das gibt Sinn. Danke.

von Niklas Gürtler (Gast)


Lesenswert?

Possetitjel schrieb:
> es genügt, den deutlich kleineren Teil des Feldes zu beherrschen, der
> sich mit (endlichen) Automaten befasst.

Endliche Automaten reichen da nicht, es muss mindestens bis zur Turing 
Maschine gehen damit übliche Programme abbildbar werden. Und dafür 
brauchts dann doch ein Semester.

Possetitjel schrieb:
> dass man Automaten- theorie auch betreiben kann, ohne sich um die
> Korrespondenzen zu den Grammatiken zu scheren.
Da das aber die Hauptanwendung ist gehört es irgendwie dazu.

In Anbetracht der Tatsache dass die meisten Informatiker TheoInf 
überhaupt nicht verstehen aber gewöhnliches OOP mit Kapselung und 
Datenstrukturen schon, würde ich nicht sagen dass es besonders hilfreich 
wäre Automaten als Grundlage für alles darzustellen. Eine String Klasse 
ist als Text intuitiver verstanden denn als Turing Maschine, wo auf dem 
Band Zeichen, Länge usw liegen. Schlimmer wird es z.B. bei Listen oder 
Graphen. Komplexe OOP-Strukturen sind da noch viel weiter entfernt...

Possetitjel schrieb:
> OOP ist für mich ein einfaches und sehr sinnvolles Konzept, das
> typischerweise auf eine für mich absolut unverständliche Art erklärt
> wird.
Für dich mag das zutreffen, aber ich glaube nicht dass das für die 
Mehrheit gilt. Das Konzept der Datenkapselung dürfte vielen besser 
liegen. Insbesondere weil die meisten Informatiker Begriffe wie 
Schaltwerk, Kombinatorik (in dem Zusammenhang), und 74er nie gehört 
haben.

von Dr. Sommer (Gast)


Lesenswert?

Markus F. schrieb:
> Dr. Sommer schrieb:
> ... Strings die 0-Bytes enthalten...
>
> das ist für C-Programmierer ein Oxymoron.

Dann halt Byte-Array. In den meisten Sprachen muss man sich um solche 
Probleme halt keine Sorgen machen...

PS: Ich würde im Studium eher noch mehr auf die Details der 
Programmiersprachen eingehen, und dafür Dinge wie Jura oder BWL 
reduzieren. Wenn ich sehe was so am Code produziert wird (insbesondere 
in C und C++) überkommt einen das Gruseln. In C sind viele Dinge einfach 
falsch und werden trotzdem wie selbstverständlich überall so gemacht...

von Ach ja ;) (Gast)


Lesenswert?

Markus F. schrieb:
> Dr. Sommer schrieb:
>> ... Strings die 0-Bytes enthalten...

... sind dann vorzeitig nullterminiert.

;)

http://www.c-howto.de/tutorial/strings-zeichenketten/nullterminiert/
1
char text[10] = "abcdefg";
2
printf("%s\n", text);
3
text[3] = '\0';
4
printf("%s\n", text);
5
6
abcdefg
7
abc

von Roland F. (rhf)


Lesenswert?

Hallo,

Dr. Sommer schrieb:

> PS: Ich würde im Studium eher noch mehr auf die Details der
> Programmiersprachen eingehen...

Was meinst du mit "Details"?

rhf

von TriHexagon (Gast)


Lesenswert?

Possetitjel schrieb:
> Es könnte sein, dass Wilhelm auf eine Art Recht hat, die ich
> vorhin nicht gesehen habe: Möglicherweise erwerben Informatiker
> trotz der Überbetonung von Sprachen und Grammatiken genügend
> Wissen über Automatentheorie, so dass sich für sie die Konzepte
> der OOP quasi nebenbei durch genaues Hinsehen erschließen.

Das ist komplett daneben. Ich habe fast zehn Jahre vor dem Studium mit 
dem Programmieren angefangen, direkt mit C# und OOP. Gerade Java und C# 
erzwingen OOP ja extrem. Da hatte ich keine Ahnung von Automatentheorie 
oder Ähnliches. Und trotzdem haben sich mir diese Konzepte erschlossen. 
Und ich war da nicht der Einzige, ein paar Jungs aus der Computer-AG 
haben das auch verstanden. Es ist einfach nicht nötig. Das Buch, dass 
ich damals verwendete, nutze die Analogie eines Autos oder eines 
Bauernhofes, bin mir nicht mehr so sicher. Auf jeden Fall so, dass es 
ein 0815 Mensch ohne jegliche Vorkenntnisse verstehen kann. Andererseits 
ist es im Studium auch so, dass viele im ersten Semester direkt mit Java 
anfangen, Theoretische Informatik kommt hingegen erst später.

Possetitjel schrieb:
> Ich habe sie nur durch konsequentes Umdeuten aller Begriffe
> in Kategorien der Automatentheorie verstanden, und ich werde
> immer noch hippelig, wenn ich Auslassungen echter Informatiker
> über OOP lesen muss. Ich kann den Frust darüber, wie man eine
> einfache und sinnvolle Sache dermaßen frachtbriefmäßig
> umständlich erklären kann, nur schlecht unterdrücken.

Genau das ist eben dein Problem, Umdeuten ist halt nicht Verstehen. Du 
bist von der Automathentheorie zu sehr vereinnahmt, weshalb du den 
Sachverhalt nicht mehr anders betrachten kannst. Die Betrachtungsweise 
musst du fallen lassen. Deswegen auch die Abneigung gegenüber der 
Vererbung, die aber ein essentieller Teil von OOP ist, weil sie mit 
"deiner Betrachtungsweise" nicht harmoniert.

von Possetitjel (Gast)


Lesenswert?

Niklas Gürtler schrieb:

> Possetitjel schrieb:
>> es genügt, den deutlich kleineren Teil des Feldes zu
>> beherrschen, der sich mit (endlichen) Automaten befasst.
>
> Endliche Automaten reichen da nicht, es muss mindestens
> bis zur Turing Maschine gehen damit übliche Programme
> abbildbar werden.

Es ging um die Frage, warum viele E-Techniker mit OOP
solche Probleme haben.

Um die Grundidee der OOP zu erklären, muss ich nicht
bis zur Turingmaschine gehen -- das habe ich nämlich
in meinem ersten, überlangen Beitrag schon getan.


> Possetitjel schrieb:
>> dass man Automatentheorie auch betreiben kann, ohne
>> sich um die Korrespondenzen zu den Grammatiken zu
>> scheren.
>
> Da das aber die Hauptanwendung ist gehört es irgendwie
> dazu.

Kein Zweifel: Hier spricht ein Informatiker.

Für mich liegt die Domäne der Automatentheorie in allen
Arten von Prozess- und Ablaufsteuerungen. Die Korrespondenz
zu den Grammatiken ist für mich ein winziges Spezialthema,
das nur für die theoretischen Informatiker wichtig ist.

Ich schreibe das nicht als Provokation, sondern weil es
die Wahrheit ist.
Ich hatte eine kleine Weile Berührung mit Steuerungs-
programmierung (SPS), und ich wäre völlig erschossen gewesen,
wenn ich nichts von endlichen Automaten gewusst hätte.

> In Anbetracht der Tatsache dass die meisten Informatiker
> TheoInf überhaupt nicht verstehen aber gewöhnliches OOP
> mit Kapselung und Datenstrukturen schon, würde ich nicht
> sagen dass es besonders hilfreich wäre Automaten als
> Grundlage für alles darzustellen.

Das beweist erstmal nur, dass die Art und Weise der
Darstellung von TheoInf schlecht ist.

> Eine String Klasse ist als Text intuitiver verstanden
> denn als Turing Maschine, wo auf dem Band Zeichen, Länge
> usw liegen.

Wenn ich "Automatentheorie" sage, dann denke ich primär
an ENDLICHE Automaten.

> Possetitjel schrieb:
>> OOP ist für mich ein einfaches und sehr sinnvolles Konzept,
>> das typischerweise auf eine für mich absolut unverständliche
>> Art erklärt wird.
>
> Für dich mag das zutreffen, aber ich glaube nicht dass das
> für die Mehrheit gilt. Das Konzept der Datenkapselung dürfte
> vielen besser liegen.

Ein "Objekt" IST ein (häufig endlicher) Automat.


> Insbesondere weil die meisten Informatiker Begriffe wie
> Schaltwerk, Kombinatorik (in dem Zusammenhang), und 74er
> nie gehört haben.

Das ist ja der Skandal!

von Possetitjel (Gast)


Lesenswert?

TriHexagon schrieb:

> Genau das ist eben dein Problem, Umdeuten ist halt
> nicht Verstehen.

Netter Versuch.

von Possetitjel (Gast)


Lesenswert?

Mist... überlesen:


Dr. Sommer schrieb:

> Possetitjel schrieb:
>> Die praktische oder angewandte Informatik IST für mich
>> eine Ingenieursdisziplin, genauso wie die Elektrotechnik.
>
> Für andere aber nicht. Ich merke deutlich die Unterschiede
> im Denken bei der Zusammenarbeit mit Ingenieuren.

Nur interessehalber: Welche sind das? Bzw. wie zeigen sie
sich?

von Carl D. (jcw2)


Lesenswert?

Possetitjel schrieb:
> Carl D. schrieb:
>
>> Manchmal kann der Erkenntnisstand der "Pioniere" aber
>> nicht auf Nachzügler warten.
>
> Es geht nicht um den Erkenntnisstand an sich, sondern
> um die Unfähigkeit oder die Unlust, die unter vielen
> persönlichen Mühen gewonnenen Erkenntnisse so umzuformu-
> lieren, dass sie für andere leicht verständlich werden.
>
> Edison sage, Genie sei 1% Inspiration und 99% Transpiration.
>
> Die geniale Erkenntnis zu haben ist 1%, sie verständlich
> zu formulieren und lehrbar zu machen, die restlichen 99%.

Aha, und worin leitet sich der Anspruch derer ab, die sich gedanklich 
auf das Vorgestellte nicht selber einlassen wollen, die Erkenntnisse 
mundgerecht vorgelegt zu bekommen?

von Yalu X. (yalu) (Moderator)


Lesenswert?

@Possetitjel:

Ich habe mir die Mühe gemacht, deinen ellenlangen Beitrag von oben
durchzulesen, jetzt musst du dasselbe auch mit meinem tun ;-)

Alle anderen dürfen ihn gerne überspringen.

Wenn ich dich richtig verstehe, versucht du, sämtliche Eigenschaften von
Programmiersprachen auf Automaten abzubilden, um sie dadurch besser zu
verstehen. Kann es sein, dass du dich damit selber blockierst und so
somit genau das Gegenteil von dem erreichst, was du eigentlich
anstrebst?

Was du in deinen Beispielen mittels Automaten beschreibst, sind im
Wesentlichen Ablaufstrukturen (Sprünge, Unterprogrammaufrufe, Objekte
mit Memberfunktionen, Parallelität usw.). Im Gegensatz zu Assembler und
Ur-Fortran zeichnen sich moderne Sprachen aber durch weit mehr als nur
die von ihnen unterstützten Ablaufstrukturen aus, als da wären:

- Datentypen (numerisch, textuell, primitiv, zusammengesetzt, ...)

- Typsysteme (stark, schwach, statisch, dynamisch, flach, hierarchisch,
  ...)

- Möglichkeiten der Metaprogrammierung (Lisp-Makros, C++-Templates, ...)

- u.v.m.

Ich bin zwar Informatiker, beschäftige mich aber auch mit Elektronik und
sonstiger Technik. Deswegen versuche auch ich, meinem Verständnis
abstrakter Sachverhalte dadurch auf die Sprünge zu helfen, dass ich in
meiner Vorstellung kleine Maschinchen baue, die mit irgendetwas (wie
bspw. Daten, Programmcode, geometrische Objekte oder was auch immer)
gefüttert werden und daraus – entweder einmalig oder laufend wie am
Fließband – unter heftiger (vorgestellter) Geräuschentwicklung
irgendetwas anderes erzeugen.

Das funktioniert für viele Dinge (wie bspw. eine Programmschleife, einen
Stack und sogar die Synchronisation zwischen mehreren Prozessen) ganz
hervorragend, aber bei den in der obigen Aufzählung genannten Punkten
ist meine Phantasie nicht mehr in der Lage, solche Maschinchen zu
konstruieren. Für einige Dinge (wie bspw. Mehrfachrekursion) kann ich
zwar ein entsprechendes Maschinchen bauen, aber bei der Inbetriebnahme
verklemmt es sich oder zerfällt sogar in seine Einzelteile, weil meine
Vorstellungskraft für das Durchspielen der Bewegungsabläufe nicht
ausreicht.

Ich habe deswegen lange nach einem besseren Ersatz für diese Maschinchen
gesucht und schließlich auch gefunden:

  Mathematische Modelle

Diejenigen, die schon lange vor mir auf diese Idee kamen, habe ich
früher immer als unverbesserliche Theoretiker angesehen und ihre
unverständlichen Aussagen als Geschwurbel abgetan.

Der Punkt bei der Sache: Ich musste anhand einiger konkreter Probleme
erst einmal selber auf den Dreh kommen, um zu glauben, dass da etwas
dran ist. Danach hat plötzlich auch das Geschwurbel der anderen (naja,
vieler, nicht aller ;-)) für mich eine Bedeutung bekommen.

Endgültig die Augen aufgegangen sind mir, als ich begann, mich mit
funktionaler Programmierung zu beschäftigen. In der reinen FP gibt es
weder Abläufe noch Zustände, weswegen die Vorstellung von Maschinchen
schon per se zu nichts führen kann.

Mit mathematische Modellen hingegen kann man einige Dinge tun, die mit
den Maschinchen in meinem Kopf kaum möglich sind:

- Man kann sie leicht zu Papier bringen und damit komplizierte
  Gedankengänge auch außerhalb des Kopfs speichern.

- Man kann damit Beweise führen und sich so selber klar machen, das ein
  komplizierter Algorithmus, den man gerade geschrieben hat, auch
  wirklich in allen Kontexten das richtige Ergebnis liefert.

- Man kann sie transformieren. Eine praktische Anwendung davon ist es,
  Programmcode so umzuschreiben, dass er effizienter wird, ohne dabei
  Fehler einzubauen. Die Vorgehensweise ist letztendlich nichts anderes
  als geschickte Umformen mathematischer Terme. Konkrete Beispiele
  finden sich bspw. in dem Buch "Pearls of Functional Algorithm Design"
  von Richard Bird.

Immer wieder fallen mir bei schwierigen Problemen weitere Wege ein, die
zwar abstrakt anmuten, aber letztendlich dazu geeignet sind, hinderliche
Knoten in Gehirnwindungen zu lösen und damit die Problemstellung zu
vereinfachen.

Und das Beste dabei: Die Erkenntnisse, die man dabei gewinnt, lassen
sich nicht nur auf die FP, sondern mit leichten Einschränkungen auch auf
die klassische Programmierung in C oder C++ anwenden.

Was ich damit ausdrücken möchte: Die Informatiker, deren Geschwurbel du
nicht verstehst, sind entweder tatsächlich Dummschwätzer oder Angeber,
oder aber sie denken in Modellen, die sie persönlich zwar befähigen,
selbst schwierigste Probleme zu lösen, die sich aber leider nicht in
deine Welt der Automaten übersetzen lassen. Umgekehrt haben diese Leute
vielleicht (wenn sie sich nie damit beschäftigt haben) Schwierigkeiten
damit, sich ein zeitlich veränderliches Signal im Frequenzbereich
vorzustellen, was für einen Elektroingenieur nichts besonders ist.

von Possetitjel (Gast)


Lesenswert?

Carl D. schrieb:

> Possetitjel schrieb:
>> Carl D. schrieb:
>>
>>> Manchmal kann der Erkenntnisstand der "Pioniere" aber
>>> nicht auf Nachzügler warten.
>>
>> Es geht nicht um den Erkenntnisstand an sich, sondern
>> um die Unfähigkeit oder die Unlust, die unter vielen
>> persönlichen Mühen gewonnenen Erkenntnisse so umzuformu-
>> lieren, dass sie für andere leicht verständlich werden.
>>
>> Edison sage, Genie sei 1% Inspiration und 99% Transpiration.
>>
>> Die geniale Erkenntnis zu haben ist 1%, sie verständlich
>> zu formulieren und lehrbar zu machen, die restlichen 99%.
>
> Aha, und worin leitet sich der Anspruch derer ab, die sich
> gedanklich auf das Vorgestellte nicht selber einlassen
> wollen, die Erkenntnisse mundgerecht vorgelegt zu bekommen?

Auch wenn das wenig bis nichts mit Wilhelm zu tun hat:

Der Anspruch leitet sich schlicht aus der normativen Kraft
des Faktischen ab: Wenn ich die Wahl zwischen einem
unverständlichen und einem klar verständlichen Buch habe,
dann wähle ich ganz sicher nicht das unverständliche. Eins
meiner wenigen Privilegien besteht darin, mir meine Lehrer
aussuchen zu können.

Es interessiert mich keinen Deut, wenn der Autor des ersten
ein verkanntes Genie, der Autor des zweiten Buches aber nur
gutes Mittelmaß ist.

Wer sich zu fein ist, Arbeit in verständliche Darstellung zu
stecken, der bleibt am Ende eben auch genau das: Unverstanden.

Das ist sein Problem, nicht meins.

von Carl D. (jcw2)


Lesenswert?

Possetitjel schrieb:
> Carl D. schrieb:
>
>> Possetitjel schrieb:
>>> Carl D. schrieb:
>>>
>>>> Manchmal kann der Erkenntnisstand der "Pioniere" aber
>>>> nicht auf Nachzügler warten.
>>>
>>> Es geht nicht um den Erkenntnisstand an sich, sondern
>>> um die Unfähigkeit oder die Unlust, die unter vielen
>>> persönlichen Mühen gewonnenen Erkenntnisse so umzuformu-
>>> lieren, dass sie für andere leicht verständlich werden.
>>>
>>> Edison sage, Genie sei 1% Inspiration und 99% Transpiration.
>>>
>>> Die geniale Erkenntnis zu haben ist 1%, sie verständlich
>>> zu formulieren und lehrbar zu machen, die restlichen 99%.
>>
>> Aha, und worin leitet sich der Anspruch derer ab, die sich
>> gedanklich auf das Vorgestellte nicht selber einlassen
>> wollen, die Erkenntnisse mundgerecht vorgelegt zu bekommen?
>
> Auch wenn das wenig bis nichts mit Wilhelm zu tun hat:
>
> Der Anspruch leitet sich schlicht aus der normativen Kraft
> des Faktischen ab: Wenn ich die Wahl zwischen einem
> unverständlichen und einem klar verständlichen Buch habe,
> dann wähle ich ganz sicher nicht das unverständliche. Eins
> meiner wenigen Privilegien besteht darin, mir meine Lehrer
> aussuchen zu können.
>
> Es interessiert mich keinen Deut, wenn der Autor des ersten
> ein verkanntes Genie, der Autor des zweiten Buches aber nur
> gutes Mittelmaß ist.

Warum liest und kommentierst du dann das erste Buch?

> Wer sich zu fein ist, Arbeit in verständliche Darstellung zu
> stecken, der bleibt am Ende eben auch genau das: Unverstanden.

Richtig, von manchen unverstanden.
Und nochmal, es gibt keinen Anspruch auf Erkenntnis und schon gar nicht 
in der Passivform.

> Das ist sein Problem, nicht meins.

Dann Versuch doch nicht, ihm das wegzunehmen.

von Ach ja ;) (Gast)


Lesenswert?

Yalu X. schrieb:
>
> Ich habe mir die Mühe gemacht, [.]einen ellenlangen Beitrag von oben
> durchzulesen ..
>
> Alle anderen dürfen ihn gerne überspringen.

Falls es dich interessiert, ich habe bei dir nicht das Gefühl "irgendein 
Geschwurbel" zu lesen im Gegensatz zu manch anderen Texten hier, die mir 
reichlich wirr und ohne erkennbaren Faden erscheinen. Ich frage mich nur 
gerade angesichts vieler alltäglicher Programmieraufgaben, welche 
speziellen Applikationen es denn überhaupt notwendig machen den Pfad des 
"normalen", intuitiven Lösungsansatzes zu verlassen, um sich einer 
völlig anderen Herangehensweise bedienen zu müssen?

von Gerhard O. (gerhard_)


Lesenswert?

Ach ja ;) schrieb:
> Ich frage mich nur
> gerade angesichts vieler alltäglicher Programmieraufgaben, welche
> speziellen Applikationen es denn überhaupt notwendig machen den Pfad des
> "normalen", intuitiven Lösungsansatzes zu verlassen, um sich einer
> völlig anderen Herangehensweise bedienen zu müssen?

Jetzt wage ich mich nochmals aufs Eis:

Ich stelle mir vor, daß im herkömmlichen Feld der Hardware intensiven 
Steueraufgaben traditionelle Methoden immer noch sinnreich und 
vernünftig sind weil man tonnenweise eine wiederverwendbare, verläßliche 
und bewährte Codebasis hat.

Es sind eher die modernen Applikation wie IoT, Internet verbundener 
Sachen die sehr Daten intensiv und von Protokollen abhängig sind und 
viel kommunizieren wo die neuen Werkzeuge dem Designer das Leben 
leichter machen. Es gibt ja heute so viele Aufgaben die aus 90% GUI und 
Communication und der Rest bestehen und irgendwelche einfache Steuer- 
und Meßaufgaben abarbeiten. Da sehe ich zumindest die Vorteile und man 
sollte sie nützen sofern man es kann:-)

Als eingefleischter Praktiker tue ich mir im Augenblick schwer mich mit 
der neuen Materie vertraut zu machen. Aber vielleicht klappt es allen 
anfänglichen Schwierigkeiten zum Trotz doch einmal.

Abgesehen davon hängt Vieles davon ab welche Entwicklungswerkzeuge und 
uC Ressourcen vorhanden sind. Viele der neuen Tols benötigen eher 
großzügige Ressourcen und sind auf kleinen uC eher mißplaziert.

Wie Arduino beweist ist der Gebrauch eines Subsets von C++ durchaus 
realistisch und würde persönlich mehr Zeit und Muße dort reinstecken 
wollen um zumindest auf dieser Ebene etwas kompetenter zu sein.

Die Experten auf diesen Gebiet wissen sowieso wie sie ihre 
Projektprobleme lösen wollen und können und brauchen keine 
Besserwisser:-)

Ok. Das wärs so ziemlich für mich was dieses Thema angeht. Ich brauche 
mir nicht meine Brötchen damit zu verdienen und kann mir aussuchen was 
ich noch lernen will. Vielleicht fällt doch irgendwann der Groschen. Ich 
werde sicherlich verfolgen was auf diesem Gebiet noch erörtert wird.

Tut mir leid wenn ich Euch mit meinen Betrachtungen und Meinungen zu 
Tode gelangweilt habe. Mir war es nur wichtig mal die andere Seite 
aufzuzeigen.

Schönes Wochenende noch,
Gerhard

von TriHexagon (Gast)


Lesenswert?

Ach ja ;) schrieb:
> Yalu X. schrieb:
>>
>> Ich habe mir die Mühe gemacht, [.]einen ellenlangen Beitrag von oben
>> durchzulesen ..
>>
>> Alle anderen dürfen ihn gerne überspringen.
>
> Falls es dich interessiert, ich habe bei dir nicht das Gefühl "irgendein
> Geschwurbel" zu lesen im Gegensatz zu manch anderen Texten hier, die mir
> reichlich wirr und ohne erkennbaren Faden erscheinen. Ich frage mich nur
> gerade angesichts vieler alltäglicher Programmieraufgaben, welche
> speziellen Applikationen es denn überhaupt notwendig machen den Pfad des
> "normalen", intuitiven Lösungsansatzes zu verlassen, um sich einer
> völlig anderen Herangehensweise bedienen zu müssen?

Weil mal das eine Paradigma Vorteile gegenüber andere in gewissen 
Situationen bietet. Vor allem, was soll denn dieser "normale intuitiver 
Lösungsansatz" sein? Ist das frei nach dem Moto "wer nur einen Hammer 
kennt, sieht in jedem Problem nur einen Nagel"? Es gibt auch in diesem 
Fall nicht diesen einen intuitiven Lösungsansatz. Jeder scheint darunter 
auch etwas anderes zu verstehen, für den einen ist die 
Metaprogrammierung in C++ absolut schlüssig und vorteilhaft, der andere 
verabscheut gleich " aufgeblassene, bürokratische" Hochsprachen und 
nimmt immer Assembler...

von Wilhelm M. (wimalopaan)


Lesenswert?

Oh, die Diskussion ist ja ganz woanders ...

Possetitjel schrieb:
> Auch wenn das wenig bis nichts mit Wilhelm zu tun hat:
>
> Der Anspruch leitet sich schlicht aus der normativen Kraft
> des Faktischen ab: Wenn ich die Wahl zwischen einem
> unverständlichen und einem klar verständlichen Buch habe,
> dann wähle ich ganz sicher nicht das unverständliche. Eins
> meiner wenigen Privilegien besteht darin, mir meine Lehrer
> aussuchen zu können.

Ich verstehe dieses Forum immer noch als Hilfe zur Selbsthilfe und ich 
habe nicht den Anspruch hier Kapitel eines Lehrbuches abzuliefern oder 
alles vorzuverdauen. Es geht mir nur um Anregungen. Wer nicht die 
Vorstellungskraft hat, dahinter einen Wert zu erkennen, der ihn dazu 
bringt, sich selbst(!) damit zu beschäftigen, der soll es eben sein 
lassen. Wer also ein Motiv hat, kann ja fragen. Ich bin kein 
Motivationstrainer sondern gehe von instrinsischer Motivation aus. 
Gerade in diesem Forum!

Und alles auf einen Automaten zurückzuführen finde ich ähnlich 
abenteuerlich wie das oben Yalu geschrieben hat. Ich finde die Modelle 
und Werkzeuge der theoretischen Informatik sehr nützlich, vor allem, 
weil man es mit Papier und Bleistift erledigen kann, allerdings kann ich 
auch verstehen, dass hier ggf. das Abstraktionsvermögen anderer aufhört.

von Wilhelm M. (wimalopaan)


Lesenswert?

Gerhard O. schrieb:

> Als eingefleischter Praktiker tue ich mir im Augenblick schwer mich mit
> der neuen Materie vertraut zu machen. Aber vielleicht klappt es allen
> anfänglichen Schwierigkeiten zum Trotz doch einmal.

Aus meiner Sicht sollte man sich die folgenden Fragen (ausgehend von 
einer C-Welt) stellen:

* wann kann/soll der Berechnungszeitpunkt sein: Compilezeit, 
Auführungszeit
* kann ich das Typsystem der Sprache modifizieren: ja/halb/nein
* wie/wann kann ich das Typsystem modifizieren: statisch im Quelltext, 
durch Berechnung zur Compilzeit, zur Laufzeit

Das Typsystem ist deswegen so wichtig, weil es der Kern der 
imperativ/prozeduralen Sprachen wie C oder C++ (hat noch mehr 
Paradigmen) ist.

In C kann ich keine Berechnungen zur Compilezeit machen und ich kann nur 
das eingebaute Typsystem verwenden. Somit ist es sehr schwer, 
abstrakteren Code zu schreiben. Je komplizierter die Aufgabe, desto mehr 
Abstraktion ist notwendig, um das Problem beherrschen zu können.

In C++ kann man:

* Berechnungen zur Compilezeit durchführen: der Compiler "interpretiert" 
den Code im Moment des Compilierens

* das Typsystem modifizieren, etwa einen Datentyp erfinden, der beim 
inkrementieren bei bspw. 10 saturiert oder zirkulär die Werte 0...9 
annimmt. Oder einen Datentyp String, der von der C-orientieren 
Sichtweise einzelner Zeichen abstrahiert, die es erlaubt, Strings zu 
addieren/konkatenieren (+) oder lexikografisch zu vergleichen (<).

* die Modifikation des Typsystems kann im Quelltext statisch stattfinden 
(Klassen erstellen) oder zur Compilezeit "berechnet" werden. Wenn es 
bspw. bekannt ist, dass ein Array maximal 100 Elemente enthält, reicht 
als DT für den Index ein uint8_t aus, es muss kein size_t sein. Das kann 
der Compiler zur Compilezeit selbst ermitteln.

Dies sind ein paar Dinge, die das Abstraktionsniveau erhöhen und damit 
Fehler vermeiden helfen, z.B. unerwünschter Wrap-around bei zu kleinen 
Ganzzahltypen, und die erstmal mit OOP in engeren Sinne, also 
Laufzeitpolymorphie (Liskov) gar nichts zu tun haben.

> Ok. Das wärs so ziemlich für mich was dieses Thema angeht.

Vielleicht sind die o.g. Dinge eine kleine Motivation, etwas tiefer 
einzusteigen.

von Gerhard O. (gerhard_)


Lesenswert?

Wilhelm M. schrieb:
> Vielleicht sind die o.g. Dinge eine kleine Motivation, etwas tiefer
> einzusteigen

Wilhelm,

Ist es schon und bedanke mich auch für Deine Hinweise.

Aber der Appettit kommt mit dem Essen und es ist notwendig eine 
Entwicklungsumgebung zu installieren die das auch kann. Ich kann nur 
lernen wenn ich mich damit praktisch befassen kann. Nur darüber zu lesen 
ist zwecklos für mich.

Ich hatte vergessen zu fragen ob es Sinn hätte am PC mit Visual Studio 
zuarbeiten. Das dürfte so ziemlich uptodate sein. Zum Lernen wäre das 
ideal weil es in Windows lauffähig sein müßte.

Den CYgwin GCC compiler wollte ich installieren, hatte aber Internet 
Verbindungsprobleme und das Installierprogramm gab auf. Da müßte ich mal 
rausfinden warum das nicht geht.


Also halte mir den Daumen,
Gerhard

von Wilhelm M. (wimalopaan)


Lesenswert?

Gerhard O. schrieb:

> Ich hatte vergessen zu fragen ob es Sinn hätte am PC mit Visual Studio
> zuarbeiten. Das dürfte so ziemlich uptodate sein. Zum Lernen wäre das
> ideal weil es in Windows lauffähig sein müßte.

Natürlich, man lernt auf dem PC, und MSVS ist ok, wenn es recht aktuell 
ist.

> Also halte mir den Daumen,
> Gerhard

Klaro - und bei Fragen ... fragen

von Gerhard O. (gerhard_)


Lesenswert?

Wilhelm M. schrieb:
> Gerhard O. schrieb:
>
>> Ich hatte vergessen zu fragen ob es Sinn hätte am PC mit Visual Studio
>> zuarbeiten. Das dürfte so ziemlich uptodate sein. Zum Lernen wäre das
>> ideal weil es in Windows lauffähig sein müßte.
>
> Natürlich, man lernt auf dem PC, und MSVS ist ok, wenn es recht aktuell
> ist.
>
>> Also halte mir den Daumen,
>> Gerhard
>
> Klaro - und bei Fragen ... fragen

Ok. Danke. Werde Dich beim Wort nehmen:-)

von Gerhard O. (gerhard_)


Lesenswert?

Im vorherigen Beitrag beschriebst Du die Compile Möglichkeiten. Das hört 
sich toll an obwohl ich mir im Augenblick noch wenig darunter vorstellen 
kann wie das im Einzeln vor sich geht. Deshalb wäre Experimentation 
wahrscheinlich der beste Weg um dafür ein praktisches Gefühl dafür zu 
bekommen.

Soweit es uC angeht wäre wahrscheinlich Atollic die einzige Umgebung wo 
das vielleicht auch mit uC wie STM32 Typen funktioniert. Da müßte man 
mal recherchieren inwieweit deren GCC Compiler uptodate ist.

PC Apps programmieren tue ich eigentlich sonst nicht.

von Wilhelm M. (wimalopaan)


Lesenswert?

Gerhard O. schrieb:

> Soweit es uC angeht wäre wahrscheinlich Atollic die einzige Umgebung wo
> das vielleicht auch mit uC wie STM32 Typen funktioniert. Da müßte man
> mal recherchieren inwieweit deren GCC Compiler uptodate ist.

GCC für ARM hat auch ein libstdc++.
Aber das Testen Deiner Programme ist doch auf dem PC in einer IDE viel 
einfacher. Später machst Du den Übergang zu µC.

von Ach ja ;) (Gast)


Lesenswert?

TriHexagon schrieb:
> was soll denn dieser "normale intuitiver
> Lösungsansatz" sein?

Wie ja jeder weiß, entsteht Software stets ingenieursmäßig durch 
ausgefuchstes, intensives Modellieren streng nach Entwicklungsplan, wie 
aus dem Lehrbuch und jeder der auch nur eine Zeile Code schreibt, bevor 
nicht auch der letzte Detailentwurf ausgearbeitet und auf Folie 
präsentiert ist, der ist ein ganz, ganz schlimmer Mensch. :)

Oder vielleicht eher doch nicht?

Das wäre dann der erwähnte eher intuitive Lösungsansatz. Soll heißen, 
natürlich schon ein paar Gedanken machen wo es hingehen soll, aber dann 
auch zügig anfangen zu Programmieren, um schnell erste Ergebnisse zu 
haben.

Wie jeder persönlich da vorgeht (oder vorgehen muss, weil's der 
Arbeitgeber bestimmt) und welcher Mittel er sich dabei bedient muss 
natürlich jeder für sich selber entscheiden. Yalu beispielsweise baut 
sich gedanklich seine kleinen Maschinchen, die er anschließend mit Daten 
füttert, in der Hoffnung, dass hinterher auch das "richtige" bei 
rauskommt. Inzwischen hat er diesen Ansatz aber wohl hinter sich 
gelassen.

Gerhard greift auf den Fundus seiner wiederverwendbaren und 
verlässlichen Codebasis zurück. Dr. Sommer wird seinen C++ -Kasten in 
Schwung bringen. Von einem hier in Forum las ich mal Nim hätte es ihm 
angetan. Der Admin (Schwarz) schwört wohl auf Python und Ruby (las ich 
mal). Es soll auch manche hier geben, die ihr Glück in einem 
Bezahl-BASIC gefunden haben. Mein Eindruck ist jeder ist da seinen 
Gewohnheiten irgendwie erlegen und nimmt halt das was er beherrscht. Was 
natürlich niemandem daran hindert auch neue Pfade auszuprobieren.

Was den Hammer betrifft, eine Software ist nicht ein simples 
Metall-Hau-Werkzeug, dass jeder in schneller Anlernmanier mal eben 
austauschen kann. Eine lieb gewonnene Radfahr-Gewohnheit auf dem eigenen 
Rennrad lässt sich auch nicht so einfach gegen die auf einem (fremden) 
Tandem eintauschen. Das ist nun mal nicht das gleiche bzw. fühlt sich 
völlig anders an. Genau so sieht es auch mit einer langjährig eingeübten 
Programmiersprache oder einem Programmier-Paradigma wie OOP vs. 
Prozedurales Programmieren aus. So ein Wechsel ist nicht mal eben auf 
die Schnelle gemacht. Nicht wenige scheitern ja schon am Eingewöhnen an 
FreeCAD oder an den MS Office Ribbons ;-) oder wie ich letztens las 
proggen lieber auf der Linux Kommandozeile in C, weil's so schön einfach 
geht anstatt sich mit der aufgeblasenen MS Visual C++ IDE 
herumzuschlagen. :)

Es kommt hinzu, mit zunehmendem Alter nimmt die einstige studentische 
Ausprobier-Euphorie auch eher steil ab. Da ist der Alltag einfach anders 
(straffer) strukturiert und die Zeit für viel Experimentierfreude nicht 
mehr so vorhanden. Also nimmt man was man kennt und gut ist.

:)

von Gerhard O. (gerhard_)


Lesenswert?

Wilhelm M. schrieb:
> Gerhard O. schrieb:
>
>> Soweit es uC angeht wäre wahrscheinlich Atollic die einzige Umgebung wo
>> das vielleicht auch mit uC wie STM32 Typen funktioniert. Da müßte man
>> mal recherchieren inwieweit deren GCC Compiler uptodate ist.
>
> GCC für ARM hat auch ein libstdc++.
> Aber das Testen Deiner Programme ist doch auf dem PC in einer IDE viel
> einfacher. Später machst Du den Übergang zu µC.

Das stimmt und hatte ich sowieso vor. Bevor ich nicht ein gewisses Maß 
Selbstvertrauen diesbezüglich erworben habe, hätte es wenig Sinn auf dem 
uC zu experimentieren.

Naja. Vielleicht wird es besser gehen wie ich mir es ausmale. Allerdings 
wird ein Teil der Bemühungen Eine Dokubasis zum Nachschlagen aufzubauen 
da ich zur Zeit noch nichts habe.

Wie ist eigentlich z.B mit der Stringdoku?

Wenn ich jetzt eine String search machen wollte, geht das mit 
trickreicher Operator Magie oder Funktionsaufruf alleine? Das sind so 
die Fragen die ich mir beantworten muß. Auch wie ein Minimal Program 
aufgebaut sein müßte und die notwendigen Header Files. Da habe ich noch 
einiges vor mir. Naja, Dein Beispielprogram vorher, gibt mir da schon 
einen Start.

Laßt Euch von mir überraschen...

von Gerhard O. (gerhard_)


Lesenswert?

Ach ja ;) schrieb:
> Oder vielleicht eher doch nicht?

Ich bin ein ganz schlimmer Mensch:-)

Da ich meist meine eigene HW entwickle denke ich auch schon parallel mit 
wie die HW Gestaltung Aspekte die zukünftige SW beeinflussen würde um 
unnötige SW Klimmzüge zu vermeiden. Dann, wenn die HW existiert teste 
ich alles durch und schreibe und teste mal zuerst die Treiber oder 
verwende schon vorhandene Treiber von vorherigen Projekten, so daß man 
die Peripherie ansprechen kann. Dafür habe ich mir ein Standard Test 
Program geschneidert mit dem ich meine HW dann virtualisieren kann und 
testen. Da habe ich dann sofort Zugang auf die meisten Peripherien des 
uC, extern oder intern.

Die eigentliche SW wird erst dann wirklich geplant und detailliert. Da 
man zu diesem Zeitpunkt weiß was HW mäßig funktioniert oder nicht, macht 
dann die spätere Arbeit viel weniger nervig.

: Bearbeitet durch User
von Possetitjel (Gast)


Lesenswert?

Carl D. schrieb:

>> Es interessiert mich keinen Deut, wenn der Autor des
>> ersten ein verkanntes Genie, der Autor des zweiten
>> Buches aber nur gutes Mittelmaß ist.
>
> Warum liest und kommentierst du dann das erste Buch?

Das tue ich nicht.

Ich habe DIR geantwortet, nicht Wilhelm oder sonstwem.
Es war DEINE elitäre Behauptung, die "Pioniere" könnten
nicht auf jeden Nachzügler warten.

Meine Antwort DARAUF ist: Wenn sich die "Pioniere" zu
fein sind, sich allgemeinverständlich auszudrücken,
sollen sie nicht rumheulen, dass man sie nicht versteht.
Ursache -- Wirkung. Kausalität.

Mit Wilhelm hat das nix zu tun. Mich nerven Deine elitären
Unterstellungen.

von Possetitjel (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Possetitjel schrieb:
>> Auch wenn das wenig bis nichts mit Wilhelm zu tun hat:
>>
>> Der Anspruch leitet sich schlicht aus der normativen Kraft
>> des Faktischen ab: Wenn ich die Wahl zwischen einem
>> unverständlichen und einem klar verständlichen Buch habe,
>> dann wähle ich ganz sicher nicht das unverständliche. Eins
>> meiner wenigen Privilegien besteht darin, mir meine Lehrer
>> aussuchen zu können.
>
> Ich verstehe dieses Forum immer noch als Hilfe zur Selbsthilfe

Du hast aber schon gelesen, dass ich den Abschnitt überschrieben
habe mit "Auch wenn das wenig bis nichts mit Wilhelm zu tun hat"?


> und ich habe nicht den Anspruch hier Kapitel eines Lehrbuches
> abzuliefern oder alles vorzuverdauen. Es geht mir nur um
> Anregungen.

Das verstehe ich.

Eine Anregung hat ja aber den Sinn, Lust auf mehr zu machen.
Das erreichen Deine Anregungen aber bei mir nicht, sie
schrecken mich eher ab.

Der Grund dafür liegt darin, dass Deine Anregungen zu wenig
an das anknüpfen, was ich kenne.
Das kannst Du natürlich als Unverschämtheit und als Anspruchs-
haltung interpretieren -- aber es ist erstmal nur eine sachliche
Aussage. Was Du damit anfängst, ist Deine Sache.

> Und alles auf einen Automaten zurückzuführen finde ich
> ähnlich abenteuerlich wie das oben Yalu geschrieben hat.

Steht Dir völlig frei.
Im Gegensatz zu Dir formuliert Yalu seine Zweifel und Fragen
an mich ausführlich und gibt mir die Chance, mich zu erklären.

> Ich finde die Modelle und Werkzeuge der theoretischen
> Informatik sehr nützlich,

Ich auch.
Zu den Modellen und Werkzeugen der theoretischen Informatik
gehören allerdings auch die Automaten. Insofern verstehe ich
Deine Bemerkung von oben nicht. Immerhin ist die Turing-Maschine
auch ein Automat.

Ich habe auch nirgendwo gegen die theoretische Informatik
polemisiert. Meine Aussage war nur, dass die Bücher über
PROGRAMMIERSPRACHEN, die ich kenne, Scheisse sind, weil sie
sich in Details verlieren, die die Konzepte eher verdecken
als erklären. Das hat wenig bis nichts mit theoretischer
Informatik zu tun.

> vor allem, weil man es mit Papier und Bleistift erledigen
> kann, allerdings kann ich auch verstehen, dass hier ggf.
> das Abstraktionsvermögen anderer aufhört.

???

Ich pflege die Automatentheorie, die ich brauche, fast immer
mit Papier und Bleistift zu erledigen.

Und woher weisst Du, wo mein Abstraktionsvermögen endet?

von Carl D. (jcw2)


Lesenswert?

Possetitjel schrieb:
> Carl D. schrieb:
>
>>> Es interessiert mich keinen Deut, wenn der Autor des
>>> ersten ein verkanntes Genie, der Autor des zweiten
>>> Buches aber nur gutes Mittelmaß ist.
>>
>> Warum liest und kommentierst du dann das erste Buch?
>
> Das tue ich nicht.
>
> Ich habe DIR geantwortet, nicht Wilhelm oder sonstwem.
> Es war DEINE elitäre Behauptung, die "Pioniere" könnten
> nicht auf jeden Nachzügler warten.
Den Begriff "Pionier" haben ich als leicht überzeichneten Gegensatz zu 
den "was wir immer gemacht haben, hat uns immer gereicht"-Schreibern 
gewählt. Ich hab nichts gegen diese, möchte mich im Gegenzug von ihnen 
aber auch nicht bremsen lassen.

> Meine Antwort DARAUF ist: Wenn sich die "Pioniere" zu
> fein sind, sich allgemeinverständlich auszudrücken,
> sollen sie nicht rumheulen, dass man sie nicht versteht.
> Ursache -- Wirkung. Kausalität.
Sie sind "sich zu fein" auch denen, denen die Happen warum auch immer zu 
groß sind, diese vorzukauen.

> Mit Wilhelm hat das nix zu tun. Mich nerven Deine elitären
> Unterstellungen.

Wenn es elitär sein sollte, sein Hirn für mehr zu benutzen als die 
Ausrede "ich kann's nicht probieren, weil meine 9 Kollegen das nie 
verstehen", dann muß ich mich damit abfinden.

Aber was soll's, ich hatte eigentlich den Plan, mal ein Beispiel zu 
bauen, das komplex genug ist, um die Vorteile erkennbar zu machen und 
einfach genug, daß es auch mit leichter mental-Gastritis verdaubar ist. 
Offenbar kann ich mir die Zeit sparen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Gerhard O. schrieb:

> Naja. Vielleicht wird es besser gehen wie ich mir es ausmale. Allerdings
> wird ein Teil der Bemühungen Eine Dokubasis zum Nachschlagen aufzubauen
> da ich zur Zeit noch nichts habe.
>
> Wie ist eigentlich z.B mit der Stringdoku?

Die offizielle Doku zur Sprache und zur libstdc++ findest Du hier:

http://en.cppreference.com/w/


> Wenn ich jetzt eine String search machen wollte, geht das mit
> trickreicher Operator Magie oder Funktionsaufruf alleine?

Mmh, jetzt bin ich etwas verwirrt: ich war davon ausgegangen, dass Du 
ein Grundverständnis von C++ hast?

Der Typ std::string ist kein primitiver DT in C++, sondern in der 
libstdc++ definiert (eigentlich als template-Spezialisierung).

Grundsätzlich gilt in der libstdc++ eine Zweiteilung in (generische) 
Algorithmen und Datentypen. Das Bindeglied zwischen beidem sind 
Iteratoren. Iteratoren kann man im ersten Ansatz als verallgemeinerte 
Zeiger ansehen (DT mit den Op * ,-> ,++, !=).

Eine Substring-Suche findest Du als Elementfunktion des DT std::string. 
Andere Algorithmen auf den Containern in der Algorithmus-Teilbibliothek.

Ist das jetzt alles neu für Dich?

Würde auch vorschlagen, das Du andere Threads eröffnest, um Deine Fragen 
zu stellen.

von Gerhard O. (gerhard_)


Lesenswert?

Wilhelm,

Um diese Zeit hätte ich jetzt keine Antwort mehr erwartet:-) bei mir ist 
es fast 23:00 Uhr.

Wilhelm M. schrieb:
> Gerhard O. schrieb:
>
>> Naja. Vielleicht wird es besser gehen wie ich mir es ausmale. Allerdings
>> wird ein Teil der Bemühungen Eine Dokubasis zum Nachschlagen aufzubauen
>> da ich zur Zeit noch nichts habe.
>>
>> Wie ist eigentlich z.B mit der Stringdoku?
>
> Die offizielle Doku zur Sprache und zur libstdc++ findest Du hier:
>
> http://en.cppreference.com/w/
Danke.
>
>> Wenn ich jetzt eine String search machen wollte, geht das mit
>> trickreicher Operator Magie oder Funktionsaufruf alleine?
>
> Mmh, jetzt bin ich etwas verwirrt: ich war davon ausgegangen, dass Du
> ein Grundverständnis von C++ hast?
Noch nicht viel weil ich bis jetzt uC nur in C gequält habe. Ich 
modifizerte allerdings die Core Serial vom Arduino um RS485 native zu 
unterstützen. Das ist auch alles und kam auch ganz gut zurecht damit 
weil da ja schon ein funktionierendes Beispiel vorhanden war das ich nur 
erweitern mußte.
>
> Der Typ std::string ist kein primitiver DT in C++, sondern in der
> libstdc++ definiert (eigentlich als template-Spezialisierung).
>
> Grundsätzlich gilt in der libstdc++ eine Zweiteilung in (generische)
> Algorithmen und Datentypen. Das Bindeglied zwischen beidem sind
> Iteratoren. Iteratoren kann man im ersten Ansatz als verallgemeinerte
> Zeiger ansehen (DT mit den Op * ,-> ,++, !=).
>
> Eine Substring-Suche findest Du als Elementfunktion des DT std::string.
Ok. Werde ich nachschauen. Ich beginne zu verstehen. Das ist definitiv 
sehr "powerful".
> Andere Algorithmen auf den Containern in der Algorithmus-Teilbibliothek.
Vielen Dank. Das werde ich mir gleich vorknöpfen weil es für mich 
praktisch wichtig ist.
>
> Ist das jetzt alles neu für Dich?
Absolut. Bis jetzt kam ich ja mit C immer zurecht. Will aber meinen 
Horizont doch erweitern weil mir einige C++ Sachen beim Arduino als sehr 
nützlich vorkamen.
>
> Würde auch vorschlagen, das Du andere Threads eröffnest, um Deine Fragen
> zu stellen.
Da hast Du recht. Werde ich machen.

Beitrag "Praktische C++ Anwendungsfragen"

Jedenfalls bin ich Dir für alle Deine Ratschläge und Informationen 
dankbar.


Gruß,
Gerhard

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Roland F. schrieb:
> Was meinst du mit "Details"?
u.a.:
- "using namespace std;" in C++
- "int" für Array-Indices
- Makros für alles und jedes statt inline-Funktionen und Konstanten
- Nutzung von "int" statt "int32_t" o.ä.
- *NULL
- char x [7]; x [3] = NULL;
- int x = ... ; char lowerByte = ((char*) &x) [0];
- printf ("%d\n", irgendEinPointer);
... sieht man sehr häufig. Das könnte man gerne besser lehren...

Wegen C++: Das lernt man am Besten mit einem guten Buch. Viele Bücher 
sind schlecht. Hier ist eine Liste guter Bücher:
https://stackoverflow.com/a/388282

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Dr. Sommer schrieb:
> Nutzung von "int" statt "int32_t"

Kann aber gut und gern sinnvoll sein.  int32_t schreibt halt zwingend
vor, dass es genau 32 Bits sein müssen – auch dann, wenn das für
irgendeine Architektur vielleicht gerade nicht optimal ist.

int_fast32_t würde sagen: „nimm mindestens 32 Bits, aber es soll schnell
sein“.  Das ist das, was man meistens brauchen würde an dieser Stelle.

"int" allein hat aber eine hinreichend garantierte Minimalgröße, dass
es für viele Fälle genügt, und die Annahme ist, dass es für eine
bestimmte Architektur die Größe ist, die auf dieser Maschine am
besten verarbeitbar ist (auf einem AVR sind es daher 16 Bits, auf
einem ARM 32, auf anderen können es auch 64 sein).

Aber ich gebe dir Recht: genau solche Abwägungen müsste man natürlich
besser unter die Leute bringen.

von Dr. Sommer (Gast)


Lesenswert?

Jörg W. schrieb:
> Kann aber gut und gern sinnvoll sein

Klar. Aber die meisten nutzen, wie in Java, für alles int. Man sollte 
wissen wann es sinnvoll ist und wann nicht ist. Statt int würde ich aber 
auch lieber int_least16_t schreiben, hat die gleiche Bedeutung aber es 
zeigt klarer was man haben will.

von Dr. Sommer (Gast)


Lesenswert?

Possetitjel schrieb:
>> Für andere aber nicht. Ich merke deutlich die Unterschiede
>> im Denken bei der Zusammenarbeit mit Ingenieuren.
>
> Nur interessehalber: Welche sind das? Bzw. wie zeigen sie
> sich?

Schwierig in Worte zu fassen. Ein Versuch:
- Ingenieure denken in Bauplänen und Komponenten. Alles was nicht als 
Blockdiagramm darstellbar ist ist für den Ing kaum fassbar. Software 
lässt sich aber nur sehr begrenzt so darstellen, oder immer nur eine 
Sichtweise davon...
- Wenn für den Ing zwei Komponenten irgendwie zusammenpassen, ist alles 
ok. Aus Software-Sicht ist die Kombination beliebiger Teile oft nicht 
sinnvoll, was nicht so leicht vermittelbar ist. Bspw. einen ESP32 an 
einem R-PI mit Linux anzuschließen ist nur auf den 3. Blick "irgendwie 
komisch".
- Hardware lässt sich gut wasserfall-artig entwickeln, von Anforderungen 
bis Tests. Ist bei Software oft weniger angemessen, und agile Prozesse 
passen ggf. nicht gut in ein Ingenieurs-Unternehmen. Software ist auch 
nie wirklich fertig.
- Für Ing.-Arbeiten lassen sich wunderbar ToDo-Listen und 
Arbeitsvorgänge planen - à la der Abteilungsleiter plant wer was wann 
macht - das ist bei Software und den komplexen Abhängigkeiten schwierig.
- Ing.-Arbeit ist oft "richtige Arbeit", die man Schritt für Schritt 
abarbeiten kann. SW-Entwicklung ist oft ein kreativer Prozess mit 
ungewissem Weg, Dauer und Ende.
- Ings haben oft kaum das Deployment/Inbetriebnahme im Blick. Als 
Embedded-Entwickler darf man sich dann mit Platinen befassen, die keine 
Test-LED's/Taster, Testpunkte, komische JTAG-Stecker, allgemein winzige 
unpraktische Stecker haben. Der klitzekleine "SW-Entwicklung"-Schritt 
zwischen Löten und Einbauen wird da gern übersehen.
- Ings haben nicht so das Gefühl für Fehlersuche. Wenn da was nicht 
klappt stehen die oft ratlos da. Als Informatiker hat man mehr so das 
Verlangen möglichst viele Informationen zu sammeln um das Problem 
einzugrenzen, so klärt sich schon vieles.

Das soll alles keine Beleidigung sein, sind nur ein paar Erfahrungen. 
Ings können haben bestimmt auch genug Probleme mit Informatikern. 
Natürlich hängt das alles stark vom Einzelfall ab, und die Probleme 
lassen sich mit Kommunikation und Organisation angehen. Aber dran denken 
muss man...

von Jobst Q. (joquis)


Lesenswert?

Dr. Sommer schrieb:
> Funktioniert nicht mit Strings die 0-Bytes enthalten

Markus F. schrieb:
>> ... Strings die 0-Bytes enthalten...
>
> das ist für C-Programmierer ein Oxymoron.

Ist es nicht. Jeder C-String enthält ein 0-Byte, selbst ein leerer 
String.
Aber eben nur eins.

Ein Byte-Array, das mehrere 0-Bytes oder auch keins enthalten kann, ist 
eben kein String, sondern ein Mem-Block. Dazu gibt es eigene Funktionen.

Die haben aber den Nachteil, dass sie zwingend als zweiten Parameter 
eine Länge brauchen, während zum Lesen eines Strings ein Pointer genügt. 
Zum Schreiben von Strings ist ein zweiter Pointer als Begrenzung oder 
die Restlänge des Puffers sinnvoll.

Da Strings die Aufgabe haben, sowohl von Menschen als auch von Maschinen 
lesbar und schreibbar zu sein, ist die Sonderfunktion des 0-Bytes keine 
Beeinträchtigung.

von Possetitjel (Gast)


Lesenswert?

Yalu X. schrieb:

> Ich habe mir die Mühe gemacht, deinen ellenlangen Beitrag
> von oben durchzulesen,

Ohh. Vielen Dank.

> jetzt musst du dasselbe auch mit meinem tun ;-)

Sehr gern.


> Wenn ich dich richtig verstehe, versucht du, sämtliche
> Eigenschaften von Programmiersprachen auf Automaten
> abzubilden, um sie dadurch besser zu verstehen.

Ähh... Nein.

Ich habe die Beobachtung gemacht, dass BESTIMMTE Eigen-
schaften von Programmiersprachen für mich sehr viel
leichter verständlich werden, wenn ich sie auf (endliche)
Automaten abbilde.

Das Verständnis fällt mir SO viel leichter, dass ich
mich ernsthaft frage, warum die übliche Darstellung
auf eine so hirnrissig komplizierte Weise erfolgt.

> Kann es sein, dass du dich damit selber blockierst und
> so somit genau das Gegenteil von dem erreichst, was du
> eigentlich anstrebst?

Sicher kann das sein -- aber erlaube die Gegenfrage:
Was bringt Dich auf den Verdacht, es sei so?


> Was du in deinen Beispielen mittels Automaten beschreibst,
> sind im Wesentlichen Ablaufstrukturen (Sprünge, Unter-
> programmaufrufe, Objekte mit Memberfunktionen, Parallelität
> usw.).

Hmm, ja.

> Im Gegensatz zu Assembler und Ur-Fortran zeichnen sich
> moderne Sprachen aber durch weit mehr als nur die von
> ihnen unterstützten Ablaufstrukturen aus, als da wären:
>
> - Datentypen (numerisch, textuell, primitiv, zusammengesetzt,
>   ...)
>
> - Typsysteme (stark, schwach, statisch, dynamisch, flach,
>   hierarchisch, ...)
>
> - Möglichkeiten der Metaprogrammierung (Lisp-Makros,
>   C++-Templates, ...)
>
> - u.v.m.

"If it's not broken, don't fix it."

Warum sollte ich Dinge umdeuten, mit denen ich in der
traditionellen Formulierung keine Verständnisprobleme
habe?

Nehmen wir die Datentypen: Ich habe da nie tiefer drüber
nachgedacht, aber man kennt es ja aus der Physik, dass eine
physikalische Größe aus Zahlenwert und Einheit besteht, und
dass man als Folge dessen nur Größen rechnerisch verknüpfen
darf, deren Einheiten kompatibel sind. (Dafür gibt es sogar
eine m.o.w. algebraische Begründung.)
Das ist ja letztlich genau das, was auch Datentypen im
Computer bewirken; das ist eine vertraute Geschichte.

Warum sollte ich das auf Krampf umdeuten wollen?

[...]
> Ich habe deswegen lange nach einem besseren Ersatz für
> diese Maschinchen gesucht und schließlich auch gefunden:
>
>   Mathematische Modelle

Ich verstehe die Worte, nicht aber ihren Sinn: Ein Automat
IST doch (auch bzw. primär) ein mathematisches Modell --
nämlich ein Quintupel (X, Y, Z, f, g) mit bestimmten Eigen-
schaften.
Nur hat dieses mathematische Modell den großen Vorzug, dass
ich sehr einfache elektronische Realisierungen für wenige
Cent bei der eingetragenen Kauffrau in Sande kaufen kann.

Wann hat man dieses Privileg schon mal: Anschauungsmodelle
für mathematische Konzepte, die man im Bastlerladen kaufen
kann?

Und ein weiterer Vorzug: Mir als E-Techniker ist das Modell
des endlichen Automaten vertraut -- also warum sollte ich
dieses Wissen nicht in Anschlag bringen, wenn es möglich
ist?

> Endgültig die Augen aufgegangen sind mir, als ich begann,
> mich mit funktionaler Programmierung zu beschäftigen. In
> der reinen FP gibt es weder Abläufe noch Zustände, weswegen
> die Vorstellung von Maschinchen schon per se zu nichts
> führen kann.

Ich bin... verwirrt.

> Mit mathematische Modellen hingegen kann man einige Dinge
> tun, die mit den Maschinchen in meinem Kopf kaum möglich
> sind:

Wahrscheinlich missverstehe ich Deine Intention gründlich,
aber was ist Automatentheorie für Dich? Sowas wie die
Backförmchen für die Kindergartenkinder? Cargo-Mathematik
(--> Cargo-Kult) für Debile (=Elektrotechniker)?


[...]
> Immer wieder fallen mir bei schwierigen Problemen weitere
> Wege ein, die zwar abstrakt anmuten, aber letztendlich
> dazu geeignet sind, hinderliche Knoten in Gehirnwindungen
> zu lösen und damit die Problemstellung zu vereinfachen.

Naja... ist das nicht die Stärke jeder guten, verständigen
Abstraktion?

"Es gibt nichts praktischeres als eine gute Theorie."


> Und das Beste dabei: Die Erkenntnisse, die man dabei gewinnt,
> lassen sich nicht nur auf die FP, sondern mit leichten
> Einschränkungen auch auf die klassische Programmierung in
> C oder C++ anwenden.

Ich weiss immer noch nicht, worauf Du mit der funktionalen
Programmierung hinauswillst.

Die Sache ist faszinierend, ja -- unter anderem deshalb,
weil sie (genau wie die Deutung durch endliche Automaten)
ohne "Algorithmen" und die dadurch entstehenden "parasitären"
Zustände auskommt.
Vielleicht mache ich einen Fehler, aber ich sehe noch nicht,
wie mir die FP z.B. im Kontext der OOP helfen sollte.


> Was ich damit ausdrücken möchte: Die Informatiker, deren
> Geschwurbel du nicht verstehst, sind entweder tatsächlich
> Dummschwätzer oder Angeber, oder aber sie denken in Modellen,
> die sie persönlich zwar befähigen, selbst schwierigste
> Probleme zu lösen, die sich aber leider nicht in deine Welt
> der Automaten übersetzen lassen.

Nein, weder -- noch. Völlig anders.

Ich habe kein grundsätzliches Problem mit der theoretischen
Informatik. Das ist halt mathematischer Stoff genau wie
Feldtheorie, Numerik oder Algebra. Es ist hart, sich den
Stoff zu erarbeiten, aber es lohnt sich in der Regel, weil
man sich dauerhaftes Hintergrundwissen aufbaut. Das Hinter-
grundwissen ist zwar interessant, aber kurzfristig nicht
unmittelbar praktisch anwendbar.

Unmittelbar praktisch anwendbar wäre die zuverlässige
Beherrschung einer Programmiersprache. Leider habe ich ein
MASSIVES Problem mit den meisten Büchern, die mir eine
Programmiersprache beibringen wollen. Die Darstellungen
kleben dermaßen schrecklich an syntaktischen Details und
geben so wenig Einblick in die hinterliegenden Konzepte,
dass ich i.d.R. nach wenigen Seiten einen Wutanfall
bekomme.

Wenn ich von Digitaltechnik keine Ahnung hätte, würde ich
mich schätzungsweise deprimiert und demütig in die Ecke
setzen und mich schämen, weil ich so dumm bin.

Sehr zum Nachteil meiner Mitmenschen HABE ich aber gewisse
Grundkenntnisse in Automatentheorie. Gestützt auf diese
Kenntnisse fährt mir ein ums andere Mal in die Nase, dass
die angewandten Informatiker, die die Bücher verfasst
haben, KONSEQUENT alle die schönen, einfachen, klaren
theoretischen Modelle ignorieren, die ihre Fachkollegen
aus der theoretischen Informatik und der Digitaltechnik
bereitgestellt haben.

Man kann (nicht alle, aber) viele Sachverhalte, mit denen
übliche Programmiersprachen hantieren, klar und verständlich
in den Begriffen der Automatentheorie darstellen -- ABER
DAS WIRD NICHT GEMACHT!

Wozu wurden die Begriffe der Automatentheorie entwickelt,
wenn sie dann nicht bei einer der häufigsten Tätigkeiten
in der Informatikausbildung -- nämlich dem Lehren von
Programmiersprachen -- verwendet werden?

von Roland F. (rhf)


Lesenswert?

Hallo,

Dr. Sommer schrieb:
> Wegen C++: Das lernt man am Besten mit einem guten Buch. Viele Bücher
> sind schlecht. Hier ist eine Liste guter Bücher:
> https://stackoverflow.com/a/388282

Das Problem C++ (oder auch andere Programmiersprachen) zu lernen ist 
nicht ein gutes Buch über C++ durch zuarbeiten, sondern die hinter den 
neueren Versionen von C++ stehenden Programmierparadigmen zu verstehen.
Jedes mal wenn ich versuche diese Konzepte zu begreifen, muss ich 
feststellen, das sich die entsprechende Literatur in syntaktischen 
Einzelheiten der Programmiersprache verliert, anstatt erst mal 
grundsätzlich zu erklären welche eigentliche Idee hinter bestimmten 
Sprachkonstrukten (und den damit verbundenen Datentypen) steht. Mir geht 
es da genau wie Possetitjel,

Zitat:
> Leider habe ich einMASSIVES Problem mit den meisten Büchern, die mir
> eine Programmiersprache beibringen wollen. Die Darstellungen kleben
> dermaßen schrecklich an syntaktischen Details und geben so wenig
> Einblick in die hinterliegenden Konzepte, dass ich i.d.R. nach wenigen
> Seiten einen Wutanfall bekomme.

rhf

von Wilhelm M. (wimalopaan)


Lesenswert?

Roland F. schrieb:

> Das Problem C++ (oder auch andere Programmiersprachen) zu lernen ist
> nicht ein gutes Buch über C++ durch zuarbeiten, sondern die hinter den
> neueren Versionen von C++ stehenden Programmierparadigmen zu verstehen.
> Jedes mal wenn ich versuche diese Konzepte zu begreifen, muss ich
> feststellen, das sich die entsprechende Literatur in syntaktischen
> Einzelheiten der Programmiersprache verliert, anstatt erst mal
> grundsätzlich zu erklären welche eigentliche Idee hinter bestimmten
> Sprachkonstrukten (und den damit verbundenen Datentypen) steht. Mir geht
> es da genau wie Possetitjel,

Die Menge der primitiven DT ist ja doch sehr einfach und gering, dann 
gibt es noch die abgeleiteten Typen und die modifier 
const/volatile/mutable, s.a.:

http://en.cppreference.com/w/cpp/language/type

Nur diese Typen haben einen direkten Bezug zu Sprachkonstrukten.

Oder meinst Du die Templates der libstdc++?

von mh (Gast)


Lesenswert?

Roland F. schrieb:
> Das Problem C++ (oder auch andere Programmiersprachen) zu lernen ist
> nicht ein gutes Buch über C++ durch zuarbeiten, sondern die hinter den
> neueren Versionen von C++ stehenden Programmierparadigmen zu verstehen.
> Jedes mal wenn ich versuche diese Konzepte zu begreifen, muss ich
> feststellen, das sich die entsprechende Literatur in syntaktischen
> Einzelheiten der Programmiersprache verliert, anstatt erst mal
> grundsätzlich zu erklären welche eigentliche Idee hinter bestimmten
> Sprachkonstrukten (und den damit verbundenen Datentypen) steht. Mir geht
> es da genau wie Possetitjel,

Das kann man meistens nicht den Büchern/Autoren anlasten. Es ist z.B. 
schwer die objektorientierten Features von C++ zu verstehen, ohne 
grundlegendes OOP Kenntnisse. Es ist allerdings nicht sinnvoll den 
kompletten Inhalt eines C++ und eines OOP Lehrbuch in ein einzelnes Buch 
zu packen, denn niemand möchte für Grundlangen ein 3000 Seiten 200€ Buch 
haben. Und was würde man dann mit den ganzen anderen Themen wie 
prozeduale, funktionale und generische Programmierung machen? Alles in 
ein Buch packen? Es müssen also immer mehrere Bücher benutzt werden.

von Ach ja ;) (Gast)


Lesenswert?

mh schrieb:
> Roland F. schrieb:
>> Das Problem C++ (oder auch andere Programmiersprachen) zu lernen ist
>> nicht ein gutes Buch über C++ durch zuarbeiten, sondern die hinter den
>> neueren Versionen von C++ stehenden Programmierparadigmen zu verstehen.

> Das kann man meistens nicht den Büchern/Autoren anlasten. Es ist z.B.
> schwer die objektorientierten Features von C++ zu verstehen, ohne
> grundlegendes OOP Kenntnisse.

Das hat sich C++ nun mal leider selber zu eigen gemacht. C# 
beispielsweise hat den Vererbungsrattenschwanz auf ein notwendiges Maß 
eingeschränkt und es scheint auch zu gehen. Wenn C# kritisiert wird, 
dann stets z.B. weil man der automatisierten, hausinternen 
Daten-Müllabfuhr genannt GC misstraut bzw. sie für wenig performant 
hält, aber nicht, weil man wie bei C++ rückwirkend bis zu August dem 
Starken noch Erbansprüche seiner Klasse beifügen kann.

C++ ist für viele anscheinend nur dann erträglich und alltagstauglich, 
indem sie es als besseres C verwenden. Das wird auch immer wieder 
zugegeben und ist ja auch nicht verwerflich, wirft aber halt Fragen auf 
bezüglich des Konzepts und der daraus abgeleiteten Lerntauglichkeit 
dieser Sprache. Und die vielen recht oberflächlichen C++ Bücher 
untermauern letztlich doch diesen Eindruck. Auch den Autoren dieser 
Bücher scheint es offensichtlich große Mühe zu bereiten, die wahren 
Vorteile von C++ dem geneigten Leser verständlich zu vermitteln.

von Ach ja ;) (Gast)


Lesenswert?

Korrektur, muss heißen

" ... aber nicht, weil man NICHT wie bei C++ rückwirkend bis zu August 
dem
Starken noch Erbansprüche seiner Klasse beifügen kann."

von Yalu X. (yalu) (Moderator)


Lesenswert?

Possetitjel schrieb:
>> Wenn ich dich richtig verstehe, versucht du, sämtliche
>> Eigenschaften von Programmiersprachen auf Automaten
>> abzubilden, um sie dadurch besser zu verstehen.
>
> Ähh... Nein.

Ok, dann habe ich dich wahrscheinlich komplett missverstanden.

> Ich habe die Beobachtung gemacht, dass BESTIMMTE Eigen-
> schaften von Programmiersprachen für mich sehr viel
> leichter verständlich werden, wenn ich sie auf (endliche)
> Automaten abbilde.

Mir persönlich geht es eher so:

Diejenigen Aspekte von Programmiersprachen, für die es mir gelingt, sie
auf endliche Automaten abzubilden, verstehe ich auch ohne diese.
Deswegen hat es mich zunächst etwas überrascht, dass das bei dir anders
ist. Aber da hat wohl jeder seine eigene Denkweise, mit der er am besten
klar kommt, was ja vollkommen in Ordnung ist.

> Das Verständnis fällt mir SO viel leichter, dass ich
> mich ernsthaft frage, warum die übliche Darstellung
> auf eine so hirnrissig komplizierte Weise erfolgt.

Es könnte sein, dass es relativ wenig Leute gibt, die in diesem
Zusammenhang in Automaten denken. Deswegen gibt es ebenso wenige, die
dir etwas in deiner Sprache bzw. Denkweise erklären können.

>> Im Gegensatz zu Assembler und Ur-Fortran zeichnen sich
>> moderne Sprachen aber durch weit mehr als nur die von
>> ihnen unterstützten Ablaufstrukturen aus, als da wären:
>>
>> - Datentypen (numerisch, textuell, primitiv, zusammengesetzt,
>>   ...)
>>
>> - Typsysteme (stark, schwach, statisch, dynamisch, flach,
>>   hierarchisch, ...)
>>
>> - Möglichkeiten der Metaprogrammierung (Lisp-Makros,
>>   C++-Templates, ...)
>>
>> - u.v.m.
>
> "If it's not broken, don't fix it."
>
> Warum sollte ich Dinge umdeuten, mit denen ich in der
> traditionellen Formulierung keine Verständnisprobleme
> habe?

Jetzt verstehe ich gar nichts mehr :)

Wenn du nicht nur die Ablaufstrukturen verstanden hast, sondern auch
abstraktere Dinge wie die oben genannten, dann bist du ja fast schon auf
einer Ebene mit Wilhelm, und ich frage mich, was du denn noch nicht
verstanden hast. Vielleicht nennst du mal ein konkretes Beispiel, dann
können wir darüber diskutieren, mit welchen Hilfsmitteln man das am
besten erklären kann.

>> Ich habe deswegen lange nach einem besseren Ersatz für
>> diese Maschinchen gesucht und schließlich auch gefunden:
>>
>>   Mathematische Modelle
>
> Ich verstehe die Worte, nicht aber ihren Sinn: Ein Automat
> IST doch (auch bzw. primär) ein mathematisches Modell --
> nämlich ein Quintupel (X, Y, Z, f, g) mit bestimmten Eigen-
> schaften.

Es ist ein mathematisches Modell, das zur Beschreibung bestimmter
Sachvehalte sehr gut taugt, für andere weniger gut und für wieder andere
überhaupt nicht.

> Nur hat dieses mathematische Modell den großen Vorzug, dass
> ich sehr einfache elektronische Realisierungen für wenige
> Cent bei der eingetragenen Kauffrau in Sande kaufen kann.

Du baust doch deine Automaten zur Erklärung von Programmieraspekten doch
nicht etwa in realer Elektronik auf? =8o

> Und ein weiterer Vorzug: Mir als E-Techniker ist das Modell
> des endlichen Automaten vertraut -- also warum sollte ich
> dieses Wissen nicht in Anschlag bringen, wenn es möglich
> ist?

Wenn sie dir weiterhelfen, dann ist das in Ordnung. Mir (und
offensichtlich auch Wilhelm) helfen sie nicht weiter. Das ist für mich
ebenfalls in Ordnung, solange ich einen anderen Weg finde, etwas zu
verstehen :)

>> Endgültig die Augen aufgegangen sind mir, als ich begann,
>> mich mit funktionaler Programmierung zu beschäftigen. In
>> der reinen FP gibt es weder Abläufe noch Zustände, weswegen
>> die Vorstellung von Maschinchen schon per se zu nichts
>> führen kann.
>
> Ich bin... verwirrt.

Ich anfangs auch ;-)

> Wahrscheinlich missverstehe ich Deine Intention gründlich,
> aber was ist Automatentheorie für Dich? Sowas wie die
> Backförmchen für die Kindergartenkinder? Cargo-Mathematik
> (--> Cargo-Kult) für Debile (=Elektrotechniker)?

Nein, überhaupt nicht. Ich benutze Automatentheorie durchaus, bspw. als
Hilfsmittel für den Entwurf von Digitalschaltungen. Automaten ohne die
Theorie setze ich manchmal als Entwurfsmuster in der Softwareentwicklung
ein.

> Ich weiss immer noch nicht, worauf Du mit der funktionalen
> Programmierung hinauswillst.

Sie hat bei mir dazu geführt, bei der Softwareentwicklung weniger in
Schritt-für-Schritt-Abläufen, sondern mehr in mathematischen Funktionen
zu denken. Das heißt aber nicht, dass ich das "Schrittdenken" komplett
aufgegeben habe, aber ich beschränke es auf diejenigen Anwendungen, die
zu deren Natur es gut passt.

Beispiele:

- Eine Waschmaschinensteuerung schaltet schrittweise, abhängig von der
  Zeit und von Sensorinformationen verschiedene Aktoren (Ventile, Pumpen
  Trommelantrieb) ein und aus. Dafür ist das Schrittdenken und die
  Formalisierung mit Automaten sehr gut geignet.

- Ein Sortieralgorithmus wird zwar vom Prozessor ebenfalls schrittweise
  ausgeführt, von der Natur der Sache ist er aber eine mathematische
  Funktion, deren Argument eine unsortierte, und deren Funktionswert die
  sortierte Liste ist. Einen solchen Algorithmus mittels Automaten zu
  beschreiben oder seine Ausführung in einem Debugger Schritt für
  Schritt mitzuverfolgen, ist viel Arbeit und bringt für mich keinerlei
  Nutzen.

> Die Sache ist faszinierend, ja -- unter anderem deshalb,
> weil sie (genau wie die Deutung durch endliche Automaten)
> ohne "Algorithmen" und die dadurch entstehenden "parasitären"
> Zustände auskommt.
> Vielleicht mache ich einen Fehler, aber ich sehe noch nicht,
> wie mir die FP z.B. im Kontext der OOP helfen sollte.

So richtig gesehen habe ich das auch erst, als ich es gemacht habe. So
wie einer, der in der OOP zu Hause ist, auch gewöhnliche C-Programme
anders strukturieren wird, als einer, der von klassischem Basic kommt,
wird der C-Code von einem FPler noch einmal anders aussehen. In beiden
Fällen wird der resultierende C-Code an Verständlichkeit, Wartbarkeit
und Eleganz gewinnen.

Dass FP (zwar langsam, aber stetig) zu einem Trend wird, zeigt sich u.a.
darin, dass immer mehr Mainstream-Programmiersprachen (wie bspw. Python,
C# und seit einigen Jahren auch C++), prozedurale, objektorientierte und
funktionale Elemente in sich vereinen. Da sie dem Programmierer kein
bestimmtes Paradigma aufzwingen, ist mit ihnen ein sanfter Einstig in
die jeweiligen Paradigmen möglich.

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:

> Dass FP (zwar langsam, aber stetig) zu einem Trend wird, zeigt sich u.a.
> darin, dass immer mehr Mainstream-Programmiersprachen (wie bspw. Python,
> C# und seit einigen Jahren auch C++), prozedurale, objektorientierte und
> funktionale Elemente in sich vereinen.

Und als Zusatz bzw. als das ganz Besondere (meiner Meinung nach) die 
generische Programmierung, die sich wunderbar mit FP zusammenfügt. Ich 
gebe zu, über die syntaktische Ausgestaltung bei C++ kann man geteilter 
Meinung sein ;-) Durch Gewohnheit stört es mich nicht mehr.

> Da  sie dem Programmierer kein
> bestimmtes Paradigma aufzwingen, ist mit ihnen ein sanfter Einstig in
> die jeweiligen Paradigmen möglich.

Und das sehe ich als die ganz große Stärke gerade von C++ an. Dadurch 
kann man m.E. eine sehr große Expressivität im Code erreichen.

von Mark B. (markbrandis)


Lesenswert?

Dr. Sommer schrieb:
> Klar. Aber die meisten nutzen, wie in Java, für alles int. Man sollte
> wissen wann es sinnvoll ist und wann nicht ist.

Toll finde ich die Programmierer, die für eine Masse oder für ein 
Volumen int verwenden (in C oder C++).

:-(

von Wilhelm M. (wimalopaan)


Lesenswert?

Mark B. schrieb:
> Dr. Sommer schrieb:
>> Klar. Aber die meisten nutzen, wie in Java, für alles int. Man sollte
>> wissen wann es sinnvoll ist und wann nicht ist.
>
> Toll finde ich die Programmierer, die für eine Masse oder für ein
> Volumen int verwenden (in C oder C++).

Ach, die Diskussion führe ich hier doch regelmäßig! Alles ist ein int, 
und wenn nicht, dann ein String.

: Bearbeitet durch User
von Alexander T. (Firma: Arge f. t. abgeh. Technologie) (atat)


Lesenswert?

Markus F. schrieb:
> Dr. Sommer schrieb:
>> ... Strings die 0-Bytes enthalten...
>
> das ist für C-Programmierer ein Oxymoron.

Im Gegentum: Ein C-String, der am Ende kein NUL enthält, ist kaputt. 
Also ist's eher eine Tautologie.

von Dr. Sommer (Gast)


Lesenswert?

Alexander T. schrieb:
> Im Gegentum: Ein C-String, der am Ende kein NUL enthält, ist kaputt.
> Also ist's eher eine Tautologie

Und was ist NUL? Hoffentlich nicht NULL?

Ersetzt einfach "String" durch Byte-Array, dann passt es wieder. Oft 
genug hat man Byte-Arrays die 0-Bytes enthalten können.

Lustig wird sowas:
1
std::string fileName = getUserInput();
2
if(fileName == "geheim.txt")
3
  Error ("Permission denied");
4
fopen (fileName.c_str (), "r");
Wenn der User jetzt "geheim.txt\0bla" eingibt, greift die Sicherheits 
Prüfung nicht, aber fopen sieht nur "geheim.txt"... Wer ist hier schuld? 
Sowas ähnliches ist/war eine Sucherheitslücke in PHP.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Yalu X. schrieb:
>
>> Dass FP (zwar langsam, aber stetig) zu einem Trend wird, zeigt sich u.a.
>> darin, dass immer mehr Mainstream-Programmiersprachen (wie bspw. Python,
>> C# und seit einigen Jahren auch C++), prozedurale, objektorientierte und
>> funktionale Elemente in sich vereinen.
>
> Und als Zusatz bzw. als das ganz Besondere (meiner Meinung nach) die
> generische Programmierung, die sich wunderbar mit FP zusammenfügt. Ich
> gebe zu, über die syntaktische Ausgestaltung bei C++ kann man geteilter
> Meinung sein ;-)

Am besten ist es, wenn man die Generizität für jede Funktion sozusagen
gratis mit dazu bekommt, ohne dass man dazu eine spezielle Syntax
bemühen muss. Haskell und F# zeigen, wie's geht.

Eine Funktion, die die Summe einer Zahlenfolge berechnet, könnte in
Haskell bspw. so definiert werden:

1
mySum = foldl' (+) 0

foldl' ist dabei eine Bibliotheksfunktion, (+) die Funktion, die auf die
Elemente der Folge angewandt werden soll, und 0 der Initialwert für
die Aufsummierung.

Der Aufruf

1
mySum [1, 2, 5]

liefert das Resultat 8. Die Funktion erlaubt dabei beliebige faltbare
Argumente (List, Array, Set usw.) mit beliebigen numerischen Elementen
(Integer, Rational, Double, Complex Double usw.).

So leicht kann das Schreiben von wiederverwendbarem Code gehen :)

von Jobst Q. (joquis)


Lesenswert?

Dr. Sommer schrieb:
> Lustig wird sowas:std::string fileName = getUserInput();
> if(fileName == "geheim.txt")
>   Error ("Permission denied");
> fopen (fileName.c_str (), "r");Wenn der User jetzt "geheim.txt\0bla"
> eingibt, greift die Sicherheits
> Prüfung nicht, aber fopen sieht nur "geheim.txt"... Wer ist hier schuld?
> Sowas ähnliches ist/war eine Sucherheitslücke in PHP.

Mit purem C wäre das nicht passiert.

Der Murks liegt also  bei getUserInput() und der Sicherheitsprüfung, für 
die die Konstrukteure von std::string verantwortlich sind.

: Bearbeitet durch User
von Keiner N. (nichtgast)


Lesenswert?

Dr. Sommer schrieb:

> Lustig wird sowas:
>
1
std::string fileName = getUserInput();
2
> if(fileName == "geheim.txt")
3
>   Error ("Permission denied");
4
> fopen (fileName.c_str (), "r");
> Wenn der User jetzt "geheim.txt\0bla" eingibt, greift die Sicherheits
> Prüfung nicht, aber fopen sieht nur "geheim.txt"... Wer ist hier schuld?
> Sowas ähnliches ist/war eine Sucherheitslücke in PHP.

Man mischt ja auch nicht C Code mit C++ Code. Das sollte man nur machen, 
wenn es gar nicht anders geht.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Dr. Sommer schrieb:
> Statt int würde ich aber auch lieber int_least16_t schreiben, hat die
> gleiche Bedeutung aber es zeigt klarer was man haben will.

int_fast16_t :)

int_least16_t würde ggf. auf Kosten von Rechenzeit (Bitmaskierung)
verlangen, dass es möglichst 16 Bit sind, dürfen nur dann mehr sein,
wenn die Architektur gar nicht mit 16-Bit-Einheiten umgehen kann.

von Alex D. (allu)


Lesenswert?

Zur ursprünglichen Frage:

Mark W. schrieb:
> schreibt Ihr auch gelegentlich mal Code, nur um Zeilen zu sparen?
Nein, nie.

Mark W. schrieb:
> Oder geht das gar nicht?
Ja!

Erst die Aufgabe analysieren und dafür großzügig Zeit nehmen, dann das 
Programm (gilt auch für Hardware) entwickeln und siehe da, man spart 
insgesamt gesehen Zeit!

von Dr. Sommer (Gast)


Lesenswert?

Keiner N. schrieb:
> Man mischt ja auch nicht C Code mit C++ Code. Das sollte man nur machen,
> wenn es gar nicht anders geht.

Leider sind die meisten Kernel in C geschrieben, und syscalls wie open 
nutzen 0-terminierte Strings, auch wenn man sie direkt aus C++ aus 
aufruft.

Jobst Q. schrieb:
> Mit purem C wäre das nicht passiert.

Mit purem C++ auch nicht. Leider ist POSIX C-orientiert...

Jobst Q. schrieb:
> für die die Konstrukteure von std::string verantwortlich sind.

Die Entwickler von std::string können wohl kaum was für diese Prüfung. 
Man sollte wohl vor der Prüfung eventuelle 0-Bytes suchen... Aber wer 
denkt schon an sowas.

von Jobst Q. (joquis)


Lesenswert?

Keiner N. schrieb:
> Man mischt ja auch nicht C Code mit C++ Code. Das sollte man nur machen,
> wenn es gar nicht anders geht.

C++ ist eine Erweiterung von C. Das ist ja auch der Vorteil gegenüber 
anderen objektorientierten Sprachen. Also sollte es kompatibel mit C 
sein.

Eine Neudefinition von String von einer Zeichenfolge, die mit dem 
Null-Byte abgeschlossen wird, zu einem beliebigen Byte-Array ist ein 
schwerwiegender Konstruktionsfehler.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Dr. Sommer schrieb:
> Die Entwickler von std::string können wohl kaum was für diese Prüfung.
> Man sollte wohl vor der Prüfung eventuelle 0-Bytes suchen... Aber wer
> denkt schon an sowas.

Ich wollte gerade schreiben, dass die Prüfung auf '\0'-Zeichen innerhalb
des Dateinamens Aufgabe von [i][o]fstream::open(string &, ...) wäre,
denn genau hier geschieht die Umwandlung eines std::string-Dateinamens
in einen POSIX-, d.h. C-Dateinamen, und der Nutzer solcher High-Level-
Bibliotheksfunktionen sollte sich nicht darum kümmern müssen, ob sein
Betriebssystem POSIX-konform ist oder nicht.

Allerdings hat mich das ISO-Dokument eines Besseren belehrt:

1
   void open(const string& s, ios_base::openmode mode = ios_base::in);
2
   void open(const filesystem::path& s, ios_base::openmode mode = ios_base::in);
3
4
   Effects: Calls open(s.c_str(), mode).

Daraus geht hervor, dass der Dateiname am ersten '\0'-Zeichen unabhängig
vom Betriebssystem immer abgeschnitten wird und sogar werden muss. Wer
darauf reinfällt, hat halt vorher nicht genau genug nachgelesen ;-)

Vor C++11 gab es die open()-Variante mit dem std::string-Dateinamen noch
nicht, weswegen der Programmierer die c_str()-Konvertierung selber
hinschreiben musste, und somit eher auf das Problem aufmerksam wurde.

Die mit C++11 eingeführte Falle hätte man anständigerweise entschärfen
können, indem man in open() eine Prüfung auf '\0'-Zeichen eingebaut
hätte. Man hat es nicht getan, was wieder einmal bestätigt, dass die
Programmiererei kein Ponyhof ist, sondern nur der des Lesens Mächtige
erfolgreich sein kann  ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:

> Eine Funktion, die die Summe einer Zahlenfolge berechnet, könnte in
> Haskell bspw. so definiert werden:
>
>
>
1
> mySum = foldl' (+) 0
2
>
>
> foldl' ist dabei eine Bibliotheksfunktion, (+) die Funktion, die auf die
> Elemente der Folge angewandt werden soll, und 0 der Initialwert für
> die Aufsummierung.
>
> Der Aufruf
>
>
>
1
> mySum [1, 2, 5]
2
>
>
> liefert das Resultat 8. Die Funktion erlaubt dabei beliebige faltbare
> Argumente (List, Array, Set usw.) mit beliebigen numerischen Elementen
> (Integer, Rational, Double, Complex Double usw.).
>
> So leicht kann das Schreiben von wiederverwendbarem Code gehen :)

In C++:
1
    auto mysum = [](auto... vv){return (vv + ... + 0);};
2
    result = mysum(1, 2, 5);

Ist allerdings nur statisch.

: Bearbeitet durch User
von Roland F. (rhf)


Lesenswert?

Hallo,

mh schrieb:
> Es ist allerdings nicht sinnvoll den kompletten Inhalt eines C++ und
> eines OOP Lehrbuch in ein einzelnes Buch zu packen, denn niemand möchte
> für Grundlangen ein 3000 Seiten 200€ Buch haben.

Ja, das ist richtig. Aber das Buch "Einführung in die Programmierung mit 
C++" von Bjarne Stroustrup hat z.B. 1224 Seiten und trotzdem gibt es 
keinen Platz für einen Überblick über die Konzepte, die hinter C++ 
stehen.
Dafür findet man im Kapitel 1 unter 1.5 "Computer sind allgegenwärtig" 
eine _9_-seitige Aufstellung über Lebensbereiche, in denen Computer eine 
wichtige Rolle spielen (das angeeignete Wissen wird dann mit Frage wie 
"Wo spielt Software eine wichtige Rolle?. Nennen Sie einige Beispiele" 
abgefragt. Mal ehrlich: was soll das?). Wenn für "sowas" Platz ist, dann 
sollte es doch ein leichtes sein, auf 20-30 Seiten die Ideen, die hinter 
C++ stehen zu beschreiben.

> Und was würde man dann mit den ganzen anderen Themen wie
> prozeduale, funktionale und generische Programmierung machen? Alles in
> ein Buch packen?

Da stellt sich die Frage was die Aufgabe eines 
Programmiersprachenlehrbuches ist. Wenn die Darstellung der Konzepte 
nicht dazu gehört, reicht ja eigentlich auch die Sprachreferenz der 
aktuellen Version.

> Es müssen also immer mehrere Bücher benutzt werden.

Darauf wird es wohl hinauslaufen. Das erinnert mich allerdings an eine 
Aussage eines Lehrers bezüglich des Lernens von Fremdsprachen. Er war 
der Meinung, das man (europäische) Sprachen viel einfacher erlernen 
könnte wenn man vorher Latein gelernt hätte. Da ist sicherlich auch was 
dran, aber wenn jemand nur Französisch lernen will, kann man ja nicht 
sagen "dann mach erst mal das große Latinum, danach ist Französisch ganz 
einfach".

Vielleicht hast du aber recht und es geht wirklich nicht anders. 
Allerdings stellt sich dann die Frage ob die momentan aktuellen 
Programmiersprachenkonzepte wirklich sinnvoll sind. In Anbetracht der 
der immer größer werdenden Wichtigkeit Software erstellen zu können, 
kann man nicht erwarten das jeder zukünftige Programmierer über intimste 
Informatikkenntnisse verfügt.

rhf

von TriHexagon (Gast)


Lesenswert?

Roland F. schrieb:
> Ja, das ist richtig. Aber das Buch "Einführung in die Programmierung mit
> C++" von Bjarne Stroustrup hat z.B. 1224 Seiten und trotzdem gibt es
> keinen Platz für einen Überblick über die Konzepte, die hinter C++
> stehen.
> Dafür findet man im Kapitel 1 unter 1.5 "Computer sind allgegenwärtig"
> eine _9_-seitige Aufstellung über Lebensbereiche, in denen Computer eine
> wichtige Rolle spielen (das angeeignete Wissen wird dann mit Frage wie
> "Wo spielt Software eine wichtige Rolle?. Nennen Sie einige Beispiele"
> abgefragt. Mal ehrlich: was soll das?).

Das ist natürlich unverständlich. Allerdings ist das Buch wohl auch 
einfach hauptsächlich für Studenten und Dozenten geschrieben. Sieht für 
mich jedenfalls so aus.

Roland F. schrieb:
> Wenn für "sowas" Platz ist, dann
> sollte es doch ein leichtes sein, auf 20-30 Seiten die Ideen, die hinter
> C++ stehen zu beschreiben.

Das wird nicht funktionieren, einfach weil C++ eine Multiparadigma 
Sprache ist. Da braucht es dann schon ein paar Seiten mehr, um alle zu 
behandeln.

Roland F. schrieb:
> Vielleicht hast du aber recht und es geht wirklich nicht anders.
> Allerdings stellt sich dann die Frage ob die momentan aktuellen
> Programmiersprachenkonzepte wirklich sinnvoll sind. In Anbetracht der
> der immer größer werdenden Wichtigkeit Software erstellen zu können,
> kann man nicht erwarten das jeder zukünftige Programmierer über intimste
> Informatikkenntnisse verfügt.

Die Konzepte sind völlig in Ordnung. Aber wenn man zu viel auf einmal 
lernen will, wird man wahrscheinlich daran scheitern. Man muss sich halt 
fragen, ob es sinnvoll ist gleichzeitig das Programmieren zu lernen, 
eine solche mächtige Programmiersprache wie C++ und dann noch ein 
Paradigma wie OOP. Das ist einfach zu viel.

Deshalb würde ich nie und nimmer C++ bei Anfängern für die Lehre 
einsetzen. Das ist eine Sprache, genauso wie C, die ganz klar die 
erfahrenen Nutzer ansprechen will und auf Anfänger keine Rücksicht 
nimmt.

Bei uns im Studium gab es uns die Informatiker und dann die 
Wirtschaftsinformatiker. Wir haben mit C und C++ angefangen, die haben 
ausschließlich Java gemacht. Und wer konnte insgesamt besser mit dem 
Programmieren zurecht (mal diejenigen ausgenommen, die sowie schon 
Programmieren konnten)? Tatsächlich die Wirtschaftsinformatiker, weil 
ihnen die Sprache weniger Steine in den Weg gelegt hatte. Bei uns hatten 
die Studenten schon mit C zu kämpfen (Zeiger, Speicherverwaltung, 
nullterminierte Strings etc.), bei C++ war dann erst recht die Hölle 
los. Auch ist mir aufgefallen, dass die Wirt.Inf. OOP sehr schnell intus 
hatten. Erstens kannten die gar nichts anderes und zweitens ist das 
ganze Thema in Java viel verständlicher (oder die Autoren davon können 
es besser erklären).

Also mein Ratschlag: wer OOP lernen will, sollte einen weiten Bogen um 
C++ machen und lieber C# oder Java dafür hernehmen. Es ist wirklich viel 
einfacher. Danach kann man sich mit C++ befassen. Ich habe das selber so 
gemacht und bin damit sehr gut gefahren, heute ist C++ meine 
Lieblingssprache.

von Jobst Q. (joquis)


Lesenswert?

Dr. Sommer schrieb:
> Die Entwickler von std::string können wohl kaum was für diese Prüfung.
> Man sollte wohl vor der Prüfung eventuelle 0-Bytes suchen... Aber wer
> denkt schon an sowas.

Der Murks fängt doch schon damit an, dass es überhaupt möglich ist ein 
Null-Byte inmitten eines Strings einzugeben. Die Funktion getUserInput() 
in deinem Beispiel kenne ich nicht und weiß daher auch nicht,ob die 
Entwickler von std::string dafür verantwortlich sind.

Aber spätestens beim Vergleich mit dem überladenen Operator == hätten 
sie dafür sorgen können, dass ihre Strings mit den üblichen C-Strings 
kompatibel sind.

Bei der Entwicklung einer neuen Sprache wäre es egal, ob sie einen 
String als beliebiges Bytearray definieren. Aber als Erweiterung von C 
ist diese Inkompatibilität nicht zu verantworten.

von Dr. Sommer (Gast)


Lesenswert?

Jobst Q. schrieb:
> Die Funktion getUserInput()
> in deinem Beispiel kenne ich nicht und weiß daher auch nicht,ob die
> Entwickler von std::string dafür verantwortlich sind.

Die hab ich mir ausgedacht als Platzhalter für irgendeine Art von 
Eingabe. Könnte z.B. über ein Netzwerk kommen.
BMP-Dateien enthalten z.B. Nullbytes. Wenn man die in einen std::string 
einliest möchte man die erhalten. Wenn man also eine Eingabe/Protokoll 
hat welches allgemein Binär-Daten überträgt, und es auch für Texte 
nutzt, werden 0-Bytes mit übernommen. Dies ist z.B. bei HTTP-Formularen 
der Fall; hier könnte ein Angreifer 0-Bytes mit senden. 0-Bytes in HTTP 
komplett auszuschließen würde aber das Hochladen von BMP-Dateien 
verhindern.
Die 0-Bytes sind auch kein Problem, bis man ein solches Byte-Array an 
eine C/POSIX-Funktion übergibt.

Jobst Q. schrieb:
> Aber spätestens beim Vergleich mit dem überladenen Operator == hätten
> sie dafür sorgen können, dass ihre Strings mit den üblichen C-Strings
> kompatibel sind.
Du meinst, es soll immer nur bis zum 0-Byte verglichen werden? Dann wäre 
also std::string("foo\0bar") == "foo" wahr, aber std::string("foo\0bar") 
== std::string("foo") falsch? Auch nicht sehr schön.

Jobst Q. schrieb:
> Aber als Erweiterung von C
> ist diese Inkompatibilität nicht zu verantworten.

std::string ist keine Erweiterung von "const char*" und somit von 
C-Strings!!! std::string ist ein Datentyp für Zeichen-Arrays inklusive 
0-Bytes, wobei ein Zeichen char, wchar, char32_t usw sein kann. Wenn man 
unvorsichtigerweise c_str () aufruft und die Länge nicht mit 
verarbeitet, nutzt man es halt falsch.
C-Strings funktionieren in C++ noch genauso, aber std::string ist eben 
kein Wrapper für C-Strings.

von Wilhelm M. (wimalopaan)


Lesenswert?

Dr. Sommer schrieb:

> Jobst Q. schrieb:
>> Aber spätestens beim Vergleich mit dem überladenen Operator == hätten
>> sie dafür sorgen können, dass ihre Strings mit den üblichen C-Strings
>> kompatibel sind.
> Du meinst, es soll immer nur bis zum 0-Byte verglichen werden? Dann wäre
> also std::string("foo\0bar") == "foo" wahr, aber std::string("foo\0bar")
> == std::string("foo") falsch? Auch nicht sehr schön.

Der Ctor von std::string interpretiert den C-String auch richtig. Also
std::string("foo\0bar") hat die Länge 3, denn "foo\0bar" ist ein 
C-String der Länge 3 (per Konvention).

Einen String aus einem std::byte Array zu konstruieren geht nicht.

Der Fehler liegt ggf. schon in dem Netzwerkbeispiel darin, die Bytes vom 
Netzwerk als char zu deklarieren, was sie ja nicht sind. Es sind Bytes.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Der Ctor von std::string interpretiert den C-String auch richtig. Also
> std::string("foo\0bar") hat die Länge 3, denn "foo\0bar" ist ein
> C-String der Länge 3 (per Konvention).

Achja, ich hatte irgendwie in Erinnerung dass die dem template-Trick 
nutzen um die tatsächliche Länge zu erhalten... Ok, wenn man 0-Bytes 
übergibt muss man die Länge angeben:
1
std::string ("foo\0bar", 7);

Wilhelm M. schrieb:
> die BYtes vom
> Netzwerk als char zu deklarieren, was sie ja nicht sind. Es sind Bytes.
Okay, wie hat man dann Bytes vom Netzwerk interpretiert bevor es 
std::byte gab? byte ist ja auch nur als enum vom typ unsigned char 
definiert. char darf definitiv 0 werden.
In Anbetracht der Tatsache dass die read/write -Funktionen von 
istream/ostream "char*" akzeptieren hätte ich jetzt gedacht, dass man 
char-Arrays, und damit auch std::string, sehr wohl zum Speichern von 
Netzwerkdaten nutzen kann.

von Dr. Sommer (Gast)


Lesenswert?

PS: Das User-Defined Literal "s" für std::string übernimmt 0-Bytes 
korrekt:
1
#include <cstdio>
2
#include <string>
3
#include <iostream>
4
 
5
int main()
6
{
7
    using namespace std::string_literals;
8
 
9
    std::string s1 = "abc\0\0def";
10
    std::string s2 = "abc\0\0def"s;
11
    std::cout << "s1: " << s1.size() << " \"" << s1 << "\"\n";
12
    std::cout << "s2: " << s2.size() << " \"" << s2 << "\"\n";
13
    std::puts (s2.c_str ());
14
    std::fwrite (s2.data (), 1, s2.size (), stdout);
15
    std::puts ("");
16
}
Ausgabe:
1
s1: 3 "abc"
2
s2: 8 "abc^@def"
3
abc
4
abc^@def
http://en.cppreference.com/w/cpp/string/basic_string/operator%22%22s

Der << Operator gibt den ganzen String aus, auch nach dem 0-Byte, weil 
das eine "richtige" C++-Ausgabe ist welche eben die Länge auswertet. 
puts geht nur bis zum 0-Byte, aber wenn man an fwrite die Länge übergibt 
wird alles ausgegeben. Die 0-Bytes werden entsprechend dem Terminal gar 
nicht oder irgendwie umschrieben ausgegeben (hier: ^@).

von Wilhelm M. (wimalopaan)


Lesenswert?

Dr. Sommer schrieb:
> Wilhelm M. schrieb:
>> Der Ctor von std::string interpretiert den C-String auch richtig. Also
>> std::string("foo\0bar") hat die Länge 3, denn "foo\0bar" ist ein
>> C-String der Länge 3 (per Konvention).
>
> Achja, ich hatte irgendwie in Erinnerung dass die dem template-Trick
> nutzen um die tatsächliche Länge zu erhalten... Ok, wenn man 0-Bytes
> übergibt muss man die Länge angeben:
>
1
std::string ("foo\0bar", 7);

Ja, denn der C-String endet per Definitionem am Sentinel. Deswegen macht 
der Typw.-ctor von std::string das auch korrekt.

> Wilhelm M. schrieb:
>> die BYtes vom
>> Netzwerk als char zu deklarieren, was sie ja nicht sind. Es sind Bytes.
> Okay, wie hat man dann Bytes vom Netzwerk interpretiert bevor es
> std::byte gab?

Konnte man ja machen seit es scoped enums gibt (C++11).

> byte ist ja auch nur als enum vom typ unsigned char
> definiert.

Aber eben nich implizit konvertierbar in einen arithmetischen Typ 
(char).

> char darf definitiv 0 werden.

Ja klar.

> In Anbetracht der Tatsache dass die read/write -Funktionen von
> istream/ostream "char*" akzeptieren hätte ich jetzt gedacht, dass man
> char-Arrays, und damit auch std::string, sehr wohl zum Speichern von
> Netzwerkdaten nutzen kann.

Das ist doch nur ein C-Krücke, für ein Byte ein char zu nehmen. In C 
muss man das so machen schlechterdings.

von Wilhelm M. (wimalopaan)


Lesenswert?

Dr. Sommer schrieb:
> PS: Das User-Defined Literal "s" für std::string übernimmt 0-Bytes
> korrekt:

Beide sind korrekt (s.o.): d.h. sie interpretieren den Quelldatentyp 
richtig.

von Dr. Sommer (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Das ist doch nur ein C-Krücke, für ein Byte ein char zu nehmen. In C
> muss man das so machen schlechterdings.

Und wie macht man es in C++03? In C++17 arbeiten die read/write 
Funktionen immer noch mit "char*".
Und was willst du eigentlich sagen? Dass man in dem Code-Snippet kein 
std::string hätte verwenden dürfen, sondern std::byte? Wie wäre das in 
C++03 gegangen? Dass man mit strcmp hätte vergleichen müssen?

von Wilhelm M. (wimalopaan)


Lesenswert?

Dr. Sommer schrieb:
> Wilhelm M. schrieb:
>> Das ist doch nur ein C-Krücke, für ein Byte ein char zu nehmen. In C
>> muss man das so machen schlechterdings.
>
> Und wie macht man es in C++03?

Das war vor 15 Jahren, das interessiert mich wirklich nicht mehr.

struct byte {...};


> In C++17 arbeiten die read/write
> Funktionen immer noch mit "char*".

Nein, mit void*.

std::fwrite ist ein namespace-aware wrapper für POSIX-fwrite, mehr 
nicht.

> Und was willst du eigentlich sagen? Dass man in dem Code-Snippet kein
> std::string hätte verwenden dürfen, sondern std::byte?

Wenn man einen Protokollbuffer mit binären, nicht-numerischen Daten hat, 
dann wäre das ein std::array<std::byte, ...>, oder ein 
std::vector<std::byte>, aber kein std::string.

> Wie wäre das in
> C++03 gegangen?

Ja, aber da mache ich mir 15-Jahre später keine Gedanken mehr drüber.

> Dass man mit strcmp hätte vergleichen müssen?

Das vergleicht wieder nur C-Strings ... mit der (impliziten) Annahme, 
dass die übergebenen Zeiger auf eine Folge (mindestens eins) Zeichen des 
Typs const char zeigen, und dass diese Folge hoffentlich (keiner weiß 
es) mit '\0' abgeschlossen ist. Falls nicht ... ok wir kennen die Story. 
Sowas ist einfach nur Murks.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Dr. Sommer schrieb:
> Wilhelm M. schrieb:
>> Der Ctor von std::string interpretiert den C-String auch richtig. Also
>> std::string("foo\0bar") hat die Länge 3, denn "foo\0bar" ist ein
>> C-String der Länge 3 (per Konvention).
>
> Achja, ich hatte irgendwie in Erinnerung dass die dem template-Trick
> nutzen um die tatsächliche Länge zu erhalten...

Ja, sowas hatte ich hier mal gepostet, damit man C-Strings (bspw. für 
AVR) automatisch in bestimmte Sections (flash) legen kann. Dazu hatte 
ich dann ein UDL-Op definiert: "abc"_pgm ist dann im Flash. Man kann 
auch das getrost N-mal im Code verwenden, es wird nur einmal ins flash 
gepackt.

von Dr. Sommer (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Das war vor 15 Jahren, das interessiert mich wirklich nicht mehr.

Also keine Erklärung. Top.

Wilhelm M. schrieb:
> Nein, mit void*.

http://en.cppreference.com/w/cpp/io/basic_istream/read
Also hier steht char_type und das ist char bei std::ifstream.

Wilhelm M. schrieb:
> aber kein std::string.

Wer sagt das? Und warum kann std::string dann problemlos damit umgehen? 
Das o.g. Problem existiert nur bei der Nutzung als C-String.

Wilhelm M. schrieb:
> Ja, sowas hatte ich hier mal gepostet
Daher kenn ich das nicht...

von Jobst Q. (joquis)


Lesenswert?

Dr. Sommer schrieb:
> BMP-Dateien enthalten z.B. Nullbytes. Wenn man die in einen std::string
> einliest möchte man die erhalten.

Was soll das für einen Sinn haben, eine Binärdatei in einen String 
einzulesen?


> Wenn man also eine Eingabe/Protokoll
> hat welches allgemein Binär-Daten überträgt, und es auch für Texte
> nutzt, werden 0-Bytes mit übernommen. Dies ist z.B. bei HTTP-Formularen
> der Fall; hier könnte ein Angreifer 0-Bytes mit senden. 0-Bytes in HTTP
> komplett auszuschließen würde aber das Hochladen von BMP-Dateien
> verhindern.

HTTP heißt Hypertext Transfer Protokoll. Es geht also um Texte. 0-Bytes 
haben in Texten aber nichts zu suchen.

Um Binärdateien zu übertragen, werden sie meines Wissens mit Base64 oder 
ähnlichem in Textdateien umgewandelt.

> Die 0-Bytes sind auch kein Problem, bis man ein solches Byte-Array an
> eine C/POSIX-Funktion übergibt.

Sie sind auch in purem C kein Problem, wenn man die mem-Funktionen 
verwendet. Der Fehler ist, die Byte-Arrays String zu nennen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Dr. Sommer schrieb:
> Wilhelm M. schrieb:
>> Das war vor 15 Jahren, das interessiert mich wirklich nicht mehr.
>
> Also keine Erklärung. Top.

Lies genau: steht oben.

>
> Wilhelm M. schrieb:
>> Nein, mit void*.
>
> http://en.cppreference.com/w/cpp/io/basic_istream/read
> Also hier steht char_type und das ist char bei std::ifstream.

Du hast oben aber std::fwrite() benutzt.

>
> Wilhelm M. schrieb:
>> aber kein std::string.
>
> Wer sagt das? Und warum kann std::string dann problemlos damit umgehen?

Wenn Du die Protokollintegrität sicherstellen kannst und Du wirklich nur 
Zeichen überträgt, kannst Du es. Aber das wolltest Du ja nicht. Dein 
Beispiel zielt auf andere Szenarien ab. Und hier ist das Empfangene erst 
mal nur eine Folge von Bytes. Um es als eine Menge von Strings zu 
interpretieren, brauchst Du dann eben einen (robusten) Parser. Der 
filtert dann solche Sachen aus.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Dr. Sommer schrieb:
> Leider sind die meisten Kernel in C geschrieben, und syscalls wie open
> nutzen 0-terminierte Strings, auch wenn man sie direkt aus C++ aus
> aufruft.

Ehrlich mal, POSIX hat genau zwei „verbotene“ Zeichen in Dateinamen,
das ist das Nullbyte und der Schrägstrich.

Vergleich' das mal mit anderen Betriebssystemen …

von Peter D. (peda)


Lesenswert?

Ich hatte das mal für das EA eDIPTFT43-A benötigt, daß Arrays \0 
beinhalten. Man kann sie als Strings definieren, aber natürlich nicht 
mit Stringfunktionen ausgeben.
Ich hab das mit einen Macro gelöst, was per sizeof die Länge ermittelt. 
Da an Strings immer ein 0-Byte angehängt wird, mußte ich noch 1 
abziehen:
1
#define TFT_FKT(x)      tft_wr( sizeof(x) - 1, x, 1 )
2
uint8_t tft_wr( uint8_t len, uint8_t *dat, bool flash ); // write to GLCD from SRAM or Flash
3
prog_uint8_t INIT[] =
4
                "\x1bTC\0"              // text cursor off
5
                "\x1b""DO\2"            // rotate 180°
6
                "\x1b""FD\x8\x1"        // display color
7
                "\x1b""FZ\x8\x1"        // text color
8
                "\x1b""DL"              // display clear
9
                "\x1b""YZ\x0"           // no delay
10
                "\x1b""YH\x64"          // light on
11
                ;
12
void tft_init( void )
13
{
14
  // ...
15
  TFT_FKT( INIT );
16
}

von Wilhelm M. (wimalopaan)


Lesenswert?

Mmh ...
1
void init(uint8_t* s) {
2
    TFT_FKT(s);
3
}
4
5
int main() {
6
    init(INIT);
7
}

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Mmh ...
> void init(uint8_t* s) {
>     TFT_FKT(s);
> }
>
> int main() {
>     init(INIT);
> }

So geht's nicht, aber so, wie Peter geschrieben hat, schon.

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:
> Wilhelm M. schrieb:
>> Mmh ...
>> void init(uint8_t* s) {
>>     TFT_FKT(s);
>> }
>>
>> int main() {
>>     init(INIT);
>> }
>
> So geht's nicht, aber so, wie Peter geschrieben hat, schon.

Das ist mir schon klar: es ist DER Standardfehler. Deswegen sollte man 
den sizeof()-Operator nicht dort benutzen, wo ein Array-Bezeichner zu 
einem Zeiger zerfällt.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Deswegen sollte man den sizeof()-Operator nicht dort benutzen, wo ein
> Array-Bezeichner zu einem Zeiger zerfällt.

Ich glaube, dessen ist sich Peter durchaus bewusst.

Deswegen übergibt er INIT ja auch nicht per Funktionsargument an
tft_init, sondern greift direkt darauf zu.

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:
> Wilhelm M. schrieb:
>> Deswegen sollte man den sizeof()-Operator nicht dort benutzen, wo ein
>> Array-Bezeichner zu einem Zeiger zerfällt.
>
> Ich glaube, dessen ist sich Peter durchaus bewusst.

Ihm mag das ja klar sein. Trotzdem fällt damit dieses Macro in die 
Kategorie "Leicht falsch zu benutzen".

> Deswegen übergibt er INIT ja auch nicht per Funktionsargument an
> tft_init, sondern greift direkt darauf zu.

Du siehst aber, wie schnell man den Fehler machen kann. Vor allem etwas 
"unbedarfte" Anwender, die sich über dieses function-like Macro keine 
gedanken machen. So etwas sollte heute nicht mehr möglich sein!

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Trotzdem fällt damit dieses Macro in die Kategorie "Leicht falsch zu
> benutzen".

Genau darum schreibt man ja Makros auch GROSS.  Damit ist klar, dass
es ein Makro ist und eben keine Funktion, und dass man sich um dessen
Randbedingungen Gedanken machen muss, wenn man ihn benutzt.

Klar könnte man das in C++ alles viel schöner und eleganter schreiben.
Das hilft dir aber gar nichts, wenn du sowas in einem Projekt brauchst,
das über x Jahre mehrere hunderttausend Codezeilen akkumuliert hat, und
für das dir kein Kunde den Aufwand bezahlen würde, es ohne grundlegende
Erweiterung der Funktionalität nur der Eleganz und Schönheit wegen von
C in eine andere Sprache umzuschreiben.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Trotzdem fällt damit dieses Macro in die Kategorie "Leicht falsch zu
> benutzen".

Fällt nicht das komplette C/C++ in diese Kategorie? ;-)

SCNR

von Wilhelm M. (wimalopaan)


Lesenswert?

Jörg W. schrieb:
> Wilhelm M. schrieb:
>> Trotzdem fällt damit dieses Macro in die Kategorie "Leicht falsch zu
>> benutzen".
>
> Genau darum schreibt man ja Makros auch GROSS.

Was aber für den Präprozessor / Compiler unerheblich ist.

> Damit ist klar, dass
> es ein Makro ist und eben keine Funktion, und dass man sich um dessen
> Randbedingungen Gedanken machen muss, wenn man ihn benutzt.

Wenn sich alle Programmierer immer alle Gedanken machen würde, gäbe es 
keine Bugs. Dem scheint aber nicht so zu sein ;-) Es ist besser, wenn 
der Code bei falscher Verwendung der Schnittstelle nicht compiliert.

> Klar könnte man das in C++ alles viel schöner und eleganter schreiben.

Du kannst meine Gedanken lesen ;-)

Es wäre schöner, eleganter, sicherer und vermutlich effizienter gewesen.

> Das hilft dir aber gar nichts, wenn du sowas in einem Projekt brauchst,
> das über x Jahre mehrere hunderttausend Codezeilen akkumuliert hat, und
> für das dir kein Kunde den Aufwand bezahlen würde, es ohne grundlegende
> Erweiterung der Funktionalität nur der Eleganz und Schönheit wegen von
> C in eine andere Sprache umzuschreiben.

Das wollte ich damit auch gar nicht bezwecken. Ich kenne so etwas zur 
Genüge.

Aber: wenn man so etwas (m.E. Murks) immer wieder hier postet, wird sich 
an der Situation leider nichts ändern. Man muss zumindest deutlich 
hervorgehoben werden. Was ich damit getan habe.

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:
> Wilhelm M. schrieb:
>> Trotzdem fällt damit dieses Macro in die Kategorie "Leicht falsch zu
>> benutzen".
>
> Fällt nicht das komplette C/C++ in diese Kategorie? ;-)

Das will ich gar nicht in Gänze bestreiten.

Allerdings sehe ich einen wesentlichen Unterschied:

In C kann man es leider nicht wirklich besser machen, in C++ hat man die 
Möglichkeiten. Und gute Library-API-Designer nutzen diese Möglichkeiten.

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

Jörg W. schrieb:
> Klar könnte man das in C++ alles viel schöner und eleganter schreiben.

Das Beispiel braucht kein C++ um einigermassen wasserdicht zu werden.

Statt das sizeof() ins Makro zu schreiben eine entsprechende Konstante 
definiert:
1
#define TFT_FKT(x)      tft_wr(INIT_SIZE - 1, x, 1 )
2
static const size_t INIT_SIZE = sizeof(INIT);

die und das Array hinter "static" versteckt, das Ganze in eine separate 
Übersetzungseinheit, nur noch die Funktion nach aussen sichtbar und 
schon ist das wesentlich ungefährlicher.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Markus F. schrieb:

> Das Beispiel braucht kein C++ um einigermassen wasserdicht zu werden.

… allerdings massiv auf Kosten der Benutzerfreundlichkeit.

von Wilhelm M. (wimalopaan)


Lesenswert?

Markus F. schrieb:
> Jörg W. schrieb:
>> Klar könnte man das in C++ alles viel schöner und eleganter schreiben.
>
> Das Beispiel braucht kein C++ um einigermassen wasserdicht zu werden.
>
> Statt das sizeof() ins Makro zu schreiben eine entsprechende Konstante
> definiert:
>
>
1
> #define TFT_FKT(x)      tft_wr(INIT_SIZE - 1, x, 1 )
2
> static const size_t INIT_SIZE = sizeof(INIT);
3
>
>
> die und das Array hinter "static" versteckt, das Ganze in eine separate
> Übersetzungseinheit, nur noch die Funktion nach aussen sichtbar und
> schon ist das wesentlich ungefährlicher.

Ok, wenn das Macro weg ist, habe ich sicher kein Problem mehr damit ;-)

Wenn die Signatur von tft_wr() so bleibt, habe ich aber nichts gewonnen. 
Das Macro versuchte gerade, die Fehlermöglichkeiten der Schnittstelle zu 
eliminieren, was aber nicht funktioniert. Jetzt haben wir es wieder mit 
den zwei "zusammenhanglosen" Parametern zu tun ... oh, es sind sogar 
drei Parameter, die eine semantische Einheit bilden.

: Bearbeitet durch User
von A. S. (Gast)


Lesenswert?

Peter D. schrieb:
> prog_uint8_t INIT[] =
>                 "\x1bTC\0"              // text cursor off
>                 "\x1b""DO\2"            // rotate 180°
>                 "\x1b""FD\x8\x1"        // display color
>                 "\x1b""FZ\x8\x1"        // text color
>                 "\x1b""DL"              // display clear
>                 "\x1b""YZ\x0"           // no delay
>                 "\x1b""YH\x64"          // light on
>                 ;

wo ist da eigentlich der Vorteil zu plain C?
1
#define NEW_CMD (0x1b)
2
prog_uint8_t INIT[] = {
3
    NEW_CMD, 'T','C', 0,    // text cursor off
4
    NEW_CMD, 'D','O', 2,    // rotate 180°
5
    NEW_CMD, 'F','D', 8, 1, // display color
6
    NEW_CMD, 'F','Z', 8, 1, // text color
7
    NEW_CMD, 'D','L',       // display clear
8
    NEW_CMD, 'Y','Z', 0,    // no delay
9
    NEW_CMD, 'Y','H', 100   // light on
10
    }
11
12
/* Bzw, wenn die vielen '' stören: */
13
#define CMD(x,y) 0x1b, 'x', 'y'
14
prog_uint8_t INIT[] = {
15
    CMD(T,C), 0,    // text cursor off
16
    CMD(D,O), 2,    // rotate 180°
17
    CMD(F,D), 8, 1, // display color
18
    CMD(F,Z), 8, 1, // text color
19
    CMD(D,L),       // display clear
20
    CMD(Y,Z), 0,    // no delay
21
    CMD(Y,H), 100   // light on
22
    }

von Peter D. (peda)


Lesenswert?

Wilhelm M. schrieb:
> Aber: wenn man so etwas (m.E. Murks) immer wieder hier postet, wird sich
> an der Situation leider nichts ändern.

Es wird bestimmt ein riesen Geschütz in C++ geben, mit dem man das viel 
eleganter erschlagen kann. Nur muß man das erst finden und auch 
verstehen.

Ich wollte auch verdeutlichen, daß man Strings und Arrays nicht einfach 
in einen Topf werfen kann. Für Arrays funktioniert strlen nicht mehr und 
man muß sich eine andere Methode ausdenken, um die Länge zu ermitteln.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Peter D. schrieb:

> Es wird bestimmt ein riesen Geschütz in C++ geben, mit dem man das viel
> eleganter erschlagen kann. Nur muß man das erst finden und auch
> verstehen.

Das "Geschütz" nennt sich dort einfach "Objekt" … man definiert sich
eine Klasse, die intern außer dem Bytehaufen auch noch die Länge als
Eigenschaft hat.  Durch einen passenden Konstruktor kann man beim
Erzeugen eines Objekts (also einer Instanz dieser Klasse) dann einen
ganz normalen String als Initialisierer angeben.

Sehr wahrscheinlich gibt es sogar so eine Klasse schon irgendwie
irgendwo, andererseits ist das so einfach, dass man es als C++-Einstieg
durchaus auch als Fingerübung benutzen kann, das mal selbst zu zimmern.

von Dr. Sommer (Gast)


Lesenswert?

So sollte es gehen:
1
prog_uint8_t INIT[] =
2
                "\x1bTC\0"              // text cursor off
3
                "\x1b""DO\2"            // rotate 180°
4
                "\x1b""FD\x8\x1"        // display color
5
                "\x1b""FZ\x8\x1"        // text color
6
                "\x1b""DL"              // display clear
7
                "\x1b""YZ\x0"           // no delay
8
                "\x1b""YH\x64"          // light on
9
10
void tft_wr (prog_uint8_t* arr, std::size_t length) {
11
  // Normales Senden
12
}
13
14
template <typename T, std::size_t N>
15
inline void tft_wr (T (&arr) [N]) {
16
  tft_wr (arr, N);
17
}
18
19
int main () {
20
  tft_wr (INIT);
21
}

Mit etwas Bastelei ginge auch sowas:
1
constexpr auto INIT = combine (
2
  textCursorOff (),
3
  rotate (180),
4
  displaColor ()
5
  ...
6
);
7
...
Wobei textCursorOff () usw die einzelnen Befehle erzeugen, und combine 
daraus ein Array macht. Kann ich aber gerade nicht vollständig 
ausführen. Wäre so dann auch hübsch:
1
tft_wr (rotate (180));

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> Wilhelm M. schrieb:
>> Aber: wenn man so etwas (m.E. Murks) immer wieder hier postet, wird sich
>> an der Situation leider nichts ändern.
>
> Es wird bestimmt ein riesen Geschütz in C++ geben, mit dem man das viel
> eleganter erschlagen kann. Nur muß man das erst finden und auch
> verstehen.

So schlimm ist das ja eigentlich gar nicht. Allerdings ist der 
Ausgangspunkt ,eine Kommandosequenz sei ein String, nicht gut.

Ein Kommando für dieses Display sind zwei Codes (Ascii-Zeichen) und dann 
eine Payload. Die Kommandos können im Binär- oder Ascii-Modus gesendet 
werden. Im Binär-Modus haben wir ein anderes Startbyte als im 
Ascii-Modus, wo auch noch Trennzeichen gesendet werden sowie alles 
dezimal codiert wird.

Daraus kann man erkennen, dass das Startbyte nicht zum Command gehört 
und auch die Trennzeichen etc. erst durch die Realisierung des 
Übertragungsmodus bestimmt werden.

Man könnte ein Kommando also einfach als Folge von Bytes darstellen:
1
    std::array<std::byte, Size> command;

Allerdings ist das einzig Variable an einem festen Kommando die Payload, 
deswegen kann man die zwei Codes des Kommandos auch gleich in den Typ 
einbeziehen (und noch Bytes sparen):
1
template<char F, char S, auto Size>
2
struct Command {
3
    template<typename... VV>
4
    constexpr Command(VV... vv) : data{vv...} {}
5
    std::array<std::byte, Size> data;  
6
};

Damit kann man ein Kommando instanziieren:
1
    auto c1 = Command<'T', 'C', 2>{std::byte{8}, std::byte{1}};

Ob man das Kommando als std::array oder eigenes Template realisiert, man 
hat es auf jedenfall mit unterschiedlichen Typen der Kommandos zu tun. 
Deswegen kann man für eine Kommandosequenz keine homogenen Container 
benutzen wie std::array<typ-der-commandos, anzahl>, sondern man braucht 
einen heterogenen Container, etwa std::tuple:
1
    auto c1 = Command<'T', 'C', 2>{std::byte{8}, std::byte{1}};
2
    auto c2 = Command<'G', 'CD', 1>{std::byte{1}};
3
    
4
    auto sequence = std::tuple(c1, c2);

Dabei ist natürlich lästig, die Länge der Payload explizit angeben zu 
müssen und auch die explizite Typwandlung nach std::byte ist zwar gut, 
aber sehr verbose.

Das geht dann mit einer Factory-Funktion einfacher:
1
template<char F, char S, typename... VV>
2
constexpr auto command(VV... vv) {
3
    return Command<F, S, sizeof...(VV)>(vv...);
4
}
Die Funktionen leitet die Länge ab und erzeugt die passenden 
Parameterierung.

Also:
1
    auto c3 = command<'T', 'C'>(std::byte{8}, std::byte{1});

(Ein user-defined deduction-guide geht hier nicht wegen der expl. 
Instanziierung durch die Kommando-Codes).

Jetzt ist noch das std::byte etwas lästig, dafür bieten sich 
UDL-Operatoren an:
1
constexpr std::byte operator"" _B(char c) {
2
    return std::byte(c);
3
}

Nun kann man schreiben:
1
    constexpr auto init_sequence = std::make_tuple(
2
                command<'T', 'C'>(0_B, 1_B),
3
                command<'D', 'O'>(2_B)
4
                );

Um die Sequenz ausgeben zu können:
1
template<typename Commands>
2
void tft_write(const Commands& commands) {
3
    std::apply([](auto... c){
4
        // Assertion, das c den richtigen Typ hat, oder familiar-template syntax für diese lambda-expr (c++20)
5
        // Ausgabe auf Ascii- oder Binär-Interface
6
    }, commands);
7
}

Dies geht dann mit jedem Container, der die Anforderungen von 
std::apply<>() erfüllt.
Die Art des Inferfaces entscheidet dann über Start-Byte, Trennzeichen, 
Prüfsumme, etc...

Wenn man sich noch etwas mehr Mühe gibt, kann man auch statisch prüfen, 
ob die Kombination der beiden Ascii-Codes Sinn macht und ob die Payload 
dazu passt. Wie gesagt: statisch. Bei einem Fehler würde das Programm 
nicht compilieren! Man muss also zur Laufzeit dahingehend ggf. nichts 
debuggen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Dr. Sommer schrieb:
> tft_wr (arr, N);

Das sollte
1
  tft_wr (arr, N-1);

heißen, damit das String-Ende-'\0' nicht mit ausgegeben wird.

Im Prinzip gefällt mir deine Lösung gut, aber hundertprozentig
anfängertauglich ist auch sie nicht unbedingt:

Wilhelm kritisierte an Peters Lösung, dass damit folgendes nicht möglich
ist:

1
void init(uint8_t* s) {
2
    TFT_FKT(s);
3
}
4
5
int main() {
6
    init(INIT);
7
}

Möchte man das mit deiner Methode realisieren, muss auch init eine
Template-Funktion sein:

1
template <typename T, std::size_t N>
2
inline void init(T (&arr) [N]) {
3
  tft_wr(arr);
4
  // weiterer Initialisierungcode
5
  // ...
6
}

Wird init mehrfach mit jeweils unterschiedlich langen Init-Strings
genutzt, wird dessen Code durch den Compiler jeweils dupliziert, was auf
den Mikrocontrollern, auf den sich Peter herumtreibt, insbesondere bei
sehr umfangreichem init-Code nicht erwünscht ist.

Wenn man sich des Problems bewusst ist, kann man es leicht umgehen. Wenn
der Programmierer aber dazu in der Lage ist, ist er kein blutiger
Anfänger mehr, so dass für ihn auch das von Wilhelm angeführte Problem
mit dem zum Zeiger zerfallenden Array keines darstellt.

Deswegen stellt sich für mich generell die Frage, wieviel Aufwand man in
zusätzlichem Quellcode man stecken sollte, um damit Programmierfehler zu
vermeiden, die nur mit sehr geringer Wahrscheinlichkeit passieren (in
dem Moment, wo man beginnt, nach einer Alternativlösung zu suchen, hat
man die Gefahr ja schon erkannt und wird deswegen nicht mehr in die
Falle tappen).

Dabei denke ich jetzt weniger an deine Lösung (die ja sehr kompakt und
relativ schnell hingeschrieben ist), sondern an Wilhelms Vorschläge, die
sich oft über zig Codezeilen erstrecken.

@Wilhelm:

Betrachte das, was ich hier schreibe, bitte nicht als generelle Kritik
an deiner Art und Weise zu programmieren. Ich kann aus deinen Beispielen
viel lernen, und ich werde viele deiner Vorschläge künftig auch in
meinem eigenen Code umsetzen, nur nicht unbedingt in so einfachen
Kontexten, wo ich befürchte, dass der Aufwand schnell mal den Nutzen
übersteigt.

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:

> Wird init mehrfach mit jeweils unterschiedlich langen Init-Strings
> genutzt, wird dessen Code durch den Compiler jeweils dupliziert, was auf
> den Mikrocontrollern, auf den sich Peter herumtreibt, insbesondere bei
> sehr umfangreichem init-Code nicht erwünscht ist.

Das ist zwar grundsätzlich richtig, allerdings ist der Effekt in der 
Praxis vernachlässigbar bzw. geht im Rauschen unter. Der Grund dafür 
ist, dass das Compilerfrontend den template-code i.A: besser optimieren 
kann (s.u.).


> @Wilhelm:
>
> Betrachte das, was ich hier schreibe, bitte nicht als generelle Kritik
> an deiner Art und Weise zu programmieren. Ich kann aus deinen Beispielen
> viel lernen, und ich werde viele deiner Vorschläge künftig auch in
> meinem eigenen Code umsetzen, nur nicht unbedingt in so einfachen
> Kontexten, wo ich befürchte, dass der Aufwand schnell mal den Nutzen
> übersteigt.

Das kann jeder gerne so halten, wie er möchte. Ich kann dazu nur sagen, 
dass wir seit Jahren genau diese Strategie verfolgen und damit wirklich 
sehr erfolgreich sind. Der Code, den ich hier ab und zu mal poste bzw. 
die Kommentare, die ich dazu abgebe, sind dann immer aus Sicht des 
Library-Guys, also des Generalisten. Das kann man auch oben sehen, denn 
es ist nun ganz leicht, einen Protokolladapter für das Ascii-Protokoll 
oder einen für das Binärprotokoll des Displays dazwischen zu schieben, 
was natürlich bei der alles-ist-ein-String-Variante nicht geht, die die 
Grenzen zwischen den Kommandos und ihren Bestandteilen sind ja nicht 
(statisch) sichtbar.

Grundsätzlich haben wir folgende Ziele:

* Header-Only Template Code
* Möglichst viele Fehler zur Compilezeit
* Permanentes Code-Refreshment auf Bibliotheksebene (full C++ feature 
set)

Zwar ist AVR nicht unsere Zielplattform sondern SAM/ARM, aber auch dort 
bleibt der allseits postulierte Code-Bloat aus.

Das wesentliche ist dem Compiler so viel statische Information wie 
möglich mitzugeben bzw. die Datentypen optimal zu wählen: Werte sind 
bedeutungslos, Datentypen sind alles. Die Vorteile dieser Strategie 
scheinen evtl. Nachteile (etwa durch Mehrfachinstanziierungen) 
auszugleichen, das zeigen jedenfalls exemplarische Untersuchungen, die 
wir mal gemacht haben.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
>> Wird init mehrfach mit jeweils unterschiedlich langen Init-Strings
>> genutzt, wird dessen Code durch den Compiler jeweils dupliziert, was auf
>> den Mikrocontrollern, auf den sich Peter herumtreibt, insbesondere bei
>> sehr umfangreichem init-Code nicht erwünscht ist.
>
> Das ist zwar grundsätzlich richtig, allerdings ist der Effekt in der
> Praxis vernachlässigbar bzw. geht im Rauschen unter. Der Grund dafür
> ist, dass das Compilerfrontend den template-code i.A: besser optimieren
> kann (s.u.).

Das mag manchmal zutreffen, im gerade diskutieren Fall wird aber an den
Templates überhaupt nichts optimiert.

Ich habe Dr. Sommers Beispiel auf 4 Init-Strings erweitert, die
nacheinander alle durch einen Aufruf von init an das Display geschickt
werden. Um die stattfindende Code-Duplikation zu verdeutlichen, habe ich
in init ein paar Rechenoperationen angefügt.


test.cpp:
1
#include <stddef.h>
2
#include <stdint.h>
3
4
uint8_t INIT1[] = "\x1b""A";
5
uint8_t INIT2[] = "\x1b""BB";
6
uint8_t INIT3[] = "\x1b""CCC";
7
uint8_t INIT4[] = "\x1b""DDDD";
8
9
void tft_wr(uint8_t* arr, size_t length);
10
11
template <typename T, size_t N>
12
inline void tft_wr(T (&arr) [N]) {
13
  tft_wr(arr, N);
14
}
15
16
volatile uint32_t a, b, c, d;
17
18
template <typename T, size_t N>
19
inline void init(T (&arr) [N]) {
20
  tft_wr(arr);
21
22
  // relativ viel zusätzlicher Code
23
  a = b + c; d = a + b; c = d + a; b = c + d;
24
  a = b + c; d = a + b; c = d + a; b = c + d;
25
}
26
27
int main() {
28
  init(INIT1);
29
  init(INIT2);
30
  init(INIT3);
31
  init(INIT4);
32
}

Diesen Code habe ich für einen AVR kompiliert und mir die Größe der
erzeugten Objektdatei ausgeben lassen:

1
avr-gcc -mmcu=atmega8 -std=c++17 -Os -c test.cpp
2
avr-size test.o

Die Größe der text-Section ist mit einem einzelnen init-Aufruf

  468 Bytes,

mit allen 4 Aufrufen

  1854 Bytes,

also fast das Vierfache. Der Compiler erzeugt tatsächlich 4 Instanzen
der Template-Funktion, deren Assembler-Code jeweils 132 Instruktionen
lang ist und sich nur in einer einzigen Instruktion von den anderen
Instanzen unterscheidet.

Diesen schon sehr deutlichen Zuwachs der Codegröße kann man in diesem
Beispiel vermeiden, indem man den von den Template-Parametern
unabhängigen Teil der init-Funktion in eine separate Funktion auslagert.
Dazu muss man sich aber erst einmal des Problems bewusst werden und darf
nicht zu sehr auf die Optimierungsfähigkeiten des Compilers hoffen.

von Carl D. (jcw2)


Lesenswert?

Yalu X. schrieb:
> Die Größe der text-Section ist mit einem einzelnen init-Aufruf
>
>   468 Bytes,
>
> mit allen 4 Aufrufen
>
>   1854 Bytes,
>
> also fast das Vierfache. Der Compiler erzeugt tatsächlich 4 Instanzen
> der Template-Funktion, deren Assembler-Code jeweils 132 Instruktionen
> lang ist und sich nur in einer einzigen Instruktion von den anderen
> Instanzen unterscheidet.
Auch wenn init static, also "wirklich nur hier gebraucht", ist bzw. LTO 
benutzt wird?

Gerade getestet:
  - gcc8.1
  - "static" vor allen Funktionen (außer Main) oder LTO,
  - O3
-> 586 Bytes .text

> Diesen schon sehr deutlichen Zuwachs der Codegröße kann man in diesem
> Beispiel vermeiden, indem man den von den Template-Parametern
> unabhängigen Teil der init-Funktion in eine separate Funktion auslagert.
> Dazu muss man sich aber erst einmal des Problems bewusst werden und darf
> nicht zu sehr auf die Optimierungsfähigkeiten des Compilers hoffen.
Doch, darf man!
Man muß ihm nur die nötige Info mitgeben, z.B. daß Funktionen niemals 
von außerhalb gerufen werden.
Oder eben über den"Trick" LTO, dann bekommt er das selber raus.

PS: einmal init -> 576 Bytes, ergo: kein Bloat!

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

Carl D. schrieb:
> Auch wenn init static, also "wirklich nur hier gebraucht", ist bzw. LTO
> benutzt wird?

Bei mir ändert sich die Codegröße dadurch nur marginal.

Carl D. schrieb:
> - "static" vor allen Funktionen (außer Main) oder LTO,

Was machst du mit

1
void tft_wr(uint8_t* arr, size_t length);

?

Ich bin davon ausgegangen, dass das eine externe Bibliotheksfunktion
ist. Wenn du sie ebenfalls static machen möchtest, musst du natürlich
dafür sorgen, dass sie irgendetwas mit dem Argument length anstellt,
bspw. so:

1
static void tft_wr(uint8_t* arr, size_t length) {
2
  volatile size_t dummy = length;
3
}

Sonst unterscheiden sich die 4 Template-Instanzen nicht voneinander und
können durch den Compiler zu einer einzigen zusammengefasst werden.

: Bearbeitet durch Moderator
von Heiko L. (zer0)


Lesenswert?

Carl D. schrieb:
>> Dazu muss man sich aber erst einmal des Problems bewusst werden und darf
>> nicht zu sehr auf die Optimierungsfähigkeiten des Compilers hoffen.
> Doch, darf man!
> Man muß ihm nur die nötige Info mitgeben, z.B. daß Funktionen niemals
> von außerhalb gerufen werden.

Mal anders argumentiert: Die Größe des Arrays ist in dem einen Fall ein 
Datum, mit dem ein Algorithmus arbeitet, in dem anderen Fall ein 
Parameter des Algorithmus selbst.
 und
kann man sicher so definieren, dass sie das selbe Ergebnis liefern. Aber 
in dem einen Fall ist die 35 ein Argument einer Funktion die somit 
allgemeiner anzuwenden ist.
Das wird bei den Beispielen hier schon dann deutlich, wenn man mal 
versucht eine unbestimmte Menge von Kommandos aus einer Datei 
einzulesen...

Beitrag #5452274 wurde vom Autor gelöscht.
von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:

> Ich habe Dr. Sommers Beispiel auf 4 Init-Strings erweitert, die
> nacheinander alle durch einen Aufruf von init an das Display geschickt
> werden. Um die stattfindende Code-Duplikation zu verdeutlichen, habe ich
> in init ein paar Rechenoperationen angefügt.

Du hast ein Beispiel konstruiert(!), was natürlich eine starke 
Vermehrung des Maschinencodes bewirkt. Dies ist allerdings durch die 
volatile Qualifizierung im template entstanden. Dies entspricht 
allerdings nicht der Praxis.

In der Praxis haben wir SFRs, die notwendigerweise volatile qualifiziert 
sind, oder (ganz wenige) Datenstrukturen, die nebenläufig verwendet 
werden, und deswegen volatile sind.

Jetzt komme ich mit einem etwas praxisrelevanteren Beispiel: wir wollen 
die Daten der Arrays durch ein Protokoll laufen lassen und dann auf 
irgendeiner Schnittstelle ausgeben. Die Schnittstelle ist hier einfach 
mal als ein SFR dargestellt. Natürlich soll das ganze ein bisschen 
modular sein: unterschiedliche Protokolle mit unterschiedlichen 
Schnittstellen kombinierbar.

Variante A in C:
main.c:
1
#include <stdint.h>
2
#include <stddef.h>
3
4
typedef void (*dev_t)(uint8_t);
5
6
void protocol_A(const uint8_t* data, uint8_t length, dev_t dev);
7
8
void spi1_put(uint8_t data);
9
10
uint8_t a1[2];
11
uint8_t a2[4];
12
13
int main(){
14
    protocol_A(a1, sizeof(a1), spi1_put);
15
    protocol_A(a2, sizeof(a2), spi1_put);
16
}
und lib.c
1
#include <stdint.h>
2
#include <stddef.h>
3
4
typedef void (*dev_t)(uint8_t);
5
6
volatile uint8_t sfr1;
7
void spi1_put(uint8_t data) {
8
    sfr1 = data;
9
}
10
11
void protocol_A(const uint8_t* data, uint8_t length, dev_t dev) {
12
    for(uint8_t i = 0; i < length; ++i) {
13
        dev(data[i]);
14
    }
15
}

Variante B in C++:
lib.h
1
#include <cstdint>
2
#include <cstddef>
3
#include <array>
4
5
template<auto N>
6
struct Spi final {
7
    static void put(std::byte data) {
8
        sfr = data;
9
    }
10
    inline static volatile std::byte sfr;
11
};
12
13
struct A;
14
15
template<typename Kind, typename Dev>
16
struct Protocol final {
17
    template<auto L>
18
    static constexpr void put(const std::array<std::byte, L>& data) {
19
        for(const auto& v : data) {
20
            Dev::put(v);
21
        }
22
    }
23
};
und main.cc:
1
#include "lib.h"
2
3
using spi  = Spi<0>;
4
using p1   = Protocol<A, spi>;
5
6
std::array<std::byte, 2> a1;
7
std::array<std::byte, 4> a2;
8
9
int main(){
10
    p1::put(a1);
11
    p1::put(a2);
12
}

Da bekomme ich für das Target atmega328:

C:   238 Bytes text, 7 Bytes bss
C++: 188 Bytes text, 7 Bytes bss

Wenn ich für die C-Variante LTO einschalte:
C:   198 Bytes text, 7 Bytes bss

Soweit zu Beispielen (frei nach Pipi Langstrumpf!).

Hinzu kommt m.E. der besser strukturierte und sicherere Code. Wenn man 
das Beispiel erweitert auf:
1
using spi1  = Spi<0>;
2
using spi2  = Spi<1>;
3
using p1   = Protocol<A, spi1>;
4
using p2   = Protocol<A, spi2>;
5
6
std::array<std::byte, 2> a1;
7
std::array<std::byte, 4> a2;
8
std::array<std::byte, 8> a3;
9
std::array<std::byte, 16> a4;
10
11
int main(){
12
    p1::put(a1);
13
    p1::put(a2);
14
    p2::put(a3);
15
    p2::put(a4);
16
}

bzw.:
1
void spi1_put(uint8_t data);
2
void spi2_put(uint8_t data);
3
4
uint8_t a1[2];
5
uint8_t a2[4];
6
uint8_t a3[8];
7
uint8_t a4[16];
8
9
int main(){
10
    protocol_A(a1, sizeof(a1), spi1_put);
11
    protocol_A(a2, sizeof(a2), spi1_put);
12
    protocol_A(a3, sizeof(a3), spi2_put);
13
    protocol_A(a4, sizeof(a4), spi2_put);
14
}

so haben wir (mit LTO für C):

C:   272 Bytes text, 32 Bytes bss
C++: 224 Bytes text, 32 Bytes bss

Also hier sehen wir nichts von Code-Bloat, im Gegenteil.

von Markus F. (mfro)


Lesenswert?

Schreib' mal in der C-Variante noch ein static vor das volatile.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Yalu X. schrieb:
>
>> Ich habe Dr. Sommers Beispiel auf 4 Init-Strings erweitert, die
>> nacheinander alle durch einen Aufruf von init an das Display geschickt
>> werden. Um die stattfindende Code-Duplikation zu verdeutlichen, habe ich
>> in init ein paar Rechenoperationen angefügt.
>
> Du hast ein Beispiel konstruiert(!), was natürlich eine starke
> Vermehrung des Maschinencodes bewirkt. Dies ist allerdings durch die
> volatile Qualifizierung im template entstanden. Dies entspricht
> allerdings nicht der Praxis.

Konstruiert ist nur dieser Teil als Beispiel für einen etwas größeren
Codeabschnitt:

1
volatile uint32_t a, b, c, d;
2
3
  ...
4
  // relativ viel zusätzlicher Code
5
  a = b + c; d = a + b; c = d + a; b = c + d;
6
  a = b + c; d = a + b; c = d + a; b = c + d;

Ersetze ihn durch irgendwelchen sinnvollen, ähnlich großen Code, der
frei von volatiles ist, und du wirst feststellen, dass er immer noch für
jeden init-Aufruf dupliziert wird.

Wie ich oben geschrieben habe, kann das Problem dadurch behoben werden,
dass der gemeinsame, von den Template-Parameter unabhängige Code in eine
separate Funktion ausgelagert werden.

Fakt ist aber dass diese Optimierung durch den Programmierer geschehen
muss, der Compiler ist dazu (noch) nicht in der Lage.

> Jetzt komme ich mit einem etwas praxisrelevanteren Beispiel: wir wollen
> die Daten der Arrays durch ein Protokoll laufen lassen und dann auf
> irgendeiner Schnittstelle ausgeben.

Ich schrieb ja nicht, dass der Code-Bloat immer auftritt. In deinem
Beispiel tritt das Problem deswegen nicht in Erscheinung, weil die
put-Funktionen sehr klein sind. Sobald diese Funktionen etwas mehr tun
als nur ein paar Bytes zu kopieren, wirst du auch hier die
Codeduplizierung bemerken.

: Bearbeitet durch Moderator
von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:

> Ersetze ihn durch irgendwelchen sinnvollen, ähnlich großen Code, der
> frei von volatiles ist, und du wirst feststellen, dass er immer noch für
> jeden init-Aufruf dupliziert wird.

Das stimmt auf der einen Seite, auf der anderen Seite entstehen durch 
den Template-Code größere Optimierungspotentiale (sowohl durch den 
Compiler wie auch im Algorithmus selbst) wie oben schon gesagt.

Vielleicht wird es deutlich, warum in Summe kein Code-Bloat auftritt, 
wenn wir unser Protokoll für längere Folgen erweitern und fügen eine 
Summe hinzu:
1
void protocol_A(const uint8_t* data, uint16_t length, dev_t dev) {
2
    uint32_t sum = 0;
3
    for(uint16_t i = 0; i < length; ++i) {
4
        dev(data[i]);
5
        sum += data[i];
6
    }
7
    dev(sum);
8
    dev(sum >> 8);
9
    if (length >= 256) {
10
        dev(sum >> 16);
11
        dev(sum >> 24);
12
    }
13
}

bzw:
1
template<typename Kind, typename Dev>
2
struct Protocol final {
3
    template<auto L>
4
    static constexpr void put(const std::array<std::byte, L>& data) {
5
        using sum_type = std::conditional_t<(L < 256), uint16_t, uint32_t>;
6
        sum_type sum = 0;
7
        for(const auto& v : data) {
8
            sum += std::to_integer(v);
9
            Dev::put(v);
10
        }
11
        std::byte lower_sum = std::byte(sum);
12
        std::byte upper_sum = std::byte(sum>> 8);
13
        Dev::put(lower_sum);
14
        Dev::put(upper_sum);
15
        if constexpr(L >= 256) {
16
            std::byte lower2_sum = std::byte(sum >> 16);
17
            std::byte upper2_sum = std::byte(sum >> 24);
18
            Dev::put(lower2_sum);
19
            Dev::put(upper2_sum);
20
        }
21
    }
22
};

Der Unterschied wird weiter größer zu Gunsten C++.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.