Moin,
auch mein Hobby Code soll sauber sein. Bei einem größeren Projekt mit
einer Wetterstation für den F429, die auch noch 3 externe Sensoren
bedient und ein extra Anzeige Display gibt es bei mir eine größere
globale Struktur, in der etliche Wetter-Werte liegen, viele davon auch
noch als Arrays of struct, da 5 Tage stundenweise gespeichert werden.
Deswegen, damit das Ganze mit __packed in einem Rutsch per Funk an einen
PC übertragen werden kann. Der Trümmer ist rund 4k gross, global
external im main.h deklariert und in main.c implementiert, liegt per
Linker Direktive im residenten Batt-gepufferten BKRAM drin, nahezu alle
Routinen greifen darauf zu aus den ca 9 .c Files. Die einen füllen ihn,
andere holen sich für die LCD Grafik alles da raus.
Nur.... ist das im Standard C so usus? Dass Routinen einfach sich das
was rauspicken was sie brauchen, ganz ohne Funktionsparameter? Jede
Routine kann ja alles verändern was sie will. main.h ist von allen
Modulen aus sichtbar.
Wie ist da eine saubere Lösung? Einzelne Werte aus dem Struct als Zeiger
übergeben? Ist natürlich deutlich mehr Aufwand, dem uC dürfte es egal
sein, der Lesbarkeit und Wartbarkeit des Codes aber nicht.
Ok, C++... Klassen und gekapselte Daten aber das kann ich leider nicht,
ging bisher ohne auch.
Wie macht ihr das?
Christian
Du kannst in der main getter und setter implentieren und die Struktur in
der main.c definieren. Dadurch haben externe module einen definierten
Zugriff.
Christian J. schrieb:> Jede> Routine kann ja alles verändern was sie will.
Ja, kann sie.
Daher liegt es an dir (als Programmierer) dieser Versuchung zu
widerstehen.
> Einzelne Werte aus dem Struct als Zeiger> übergeben?
Beim Zeiger ändert sich nichts an der Problematik des Änderns.
I.A. sollten die Funktionen nur (Zugriff auf) so viel Daten bekommen,
wie sie auch brauchen.
structs kann man auch by-value übergeben. (4k auf Mikrocontroller ist
blöd)
Daher gelten da ja auch etwas andere Regeln: globale Variablen sind ok.
Im Übrigen hast du bei GUI meist auch mehrere globale struct.
Christian J. schrieb:> Nur.... ist das im Standard C so usus? Dass Routinen> einfach sich das was rauspicken was sie brauchen,> ganz ohne Funktionsparameter? Jede Routine kann ja> alles verändern was sie will. main.h ist von allen> Modulen aus sichtbar.
Kann man machen.
Hat aber bekanntermaßen den Nachteil, dass man i.d.R.
an vielen verstreuten Stellen etwas ändern muss, wenn
sich diese zentrale Datenstruktur ändert.
> Ok, C++... Klassen und gekapselte Daten aber das> kann ich leider nicht, ging bisher ohne auch.
Heute eine weitere Folge aus der Serie: "Das kannst
auch Du" :)
> Wie macht ihr das?
Unabhängig von der Sprache - Kapseln: Zugriff auf die
Daten konsequent NUR über Getter und Setter, NIEMALS
direkt auf die Variablen. (Letztlich ist das -- die
Kapselung -- auch der entscheidende Trick an der OOP.)
Speziell in C: Ich weiss nicht, ob ich das Lehrbuch
richtig verstanden habe, aber "static" sollte hier
weiterhelfen. Die Variablen sowie alle Getter und
Setter kommen in einen eigenen Quelltext; die Variablen
werden außerhalb der Funktionen ("global") vereinbart
und bekommen das Attribut "static"; die Funktionen
bekommen es aber nicht.
Alle Funktionen sind jetzt von außerhalb sichtbar,
nicht aber die Variablen.
Ok,
soweit verstanden, diese Setter und Getter bzw Wrapper nutze ich schon
lange "intuitiv" im Interface eines Moduls. Vor allem damit Änderungen
nicht überall gemacht werden müssen. Sie werden vom GCC ja ohnehin
spurlos aufgelöst, bewirken halt nur die Kapselung.
Denke, dass das Thema damit schon geklärt ist. Danke, Leute!
Irgend W. schrieb:> Was ist denn daran "blöd" auf eine µC der 256k RAM hat?
Ähm.. hust... genauer gesagt sind es 8MB. 8MB SRAM am ExtMemory
Bus, da es ein kastriertes Disco Board ist, wo das Display runter
ist und durch ein serielles ILI9341 SPI Display ersetzt wurde.
Linker File entsprechend angepasst.
Er meint vielleicht, dass man 4k nicht unbedingt call-by value
übergeben sollte, weil das den Stack massiv aufbläst und
sinnlose Kopie-Rechenzeit verbrät.
Dirk B. schrieb:> structs kann man auch by-value übergeben. (4k auf Mikrocontroller ist> blöd)
Auch am PC wird man hier normalerweise lieber Zeiger auf const
übergeben.
Getter und Setter sind oft sinnvoll. Wenn Du am Ende aber für jedes
Datenfeld 2 oder mehr Funktionen hast, ist das Quatsch. Getter und
Setter sind umso sinnvoller, je komplizierter der Zugriff ist (z.b.
baudrate in Teiler umrechnen) oder je eher die Implementierung sich
ändern kann (uint8 -> uint16 -> Zeiger ...).
Ein reines Feld fester Register ist oft als globale Struktur(en) genauso
gut und bei vielen Bausteinen üblich.
Christian J. schrieb:> Ok, C++... Klassen und gekapselte Daten aber> das kann ich leider nicht, ging bisher ohne auch.
Du musst ja nicht auf C++24 und kryptographischen Quelltext gehen,
sondern dich erstmal auf "C-with-classes" beschränken.
Aus der C-Sicht ist eine Klasse erstmal nichts weiter als eine normale
Struktur mit zusätzlichen Funktionen. Jedes Element (Variable oder
Funktion) kann für externen Zugriff blockiert (private) oder freigegeben
(public) werden.
Fertig. Das allein sollte dir weiterhelfen.
Und wenn Wilhelm loslegt, hörst du erstmal weg. :-)
> Ok, C++... Klassen und gekapselte Daten aber> das kann ich leider nicht, ging bisher ohne auch.
Jetzt zitier ich das auch noch. :-)
Es geht um Kapselung. Deine Datenstruktur geht den Rest des Programms so
wenig wie möglich an.
Daher wandert deine globale Struktur in eine eigene .c und wird dort als
static deklariert.
Im .h stehen nur die typedefs, enums, function, ... die nach aussen hin
nötig sind.
Dann hast u (wie mehrfach genannt) getter und setter Funktionen die auf
die Daten zugreifen können.
Alle Sicherheitsüberprüfungen (index out of range etc.) hast du somit
nur in dem einen neuen .c. Der Rest des Programms kann sich darauf
verlassen, dass möglichst jeder Unsinn abgefangen wird. Und auch in der
Debug-Konsole ausgegeben wird.
Du kannst/sollst da auch die streaming-Funktion (senden an PC)
reinbauen.
Alle Funktionen so weit möglich mit const Parametern, dann kann der
Compiler die meisten selbst auferlegten Beschränkungen rausoptimieren.
Meistens kommt bei dem "data hiding" dabei sogar ein kompakterer Code
raus. Einfach weil der Code in nur einem Modul auftaucht und nicht
überall verteilt Kopien.
Nick
S. R. schrieb:> Du musst ja nicht auf C++24 und kryptographischen Quelltext gehen,> sondern dich erstmal auf "C-with-classes" beschränken.
Reicht das das Studium einer Arduino Lib erstmal? Oder gleich das
geballte 500 Seiten Werk "C++ für Dummies"? Ich habe schon vor zig
Jahren um die Jahrtausendwende beim Bjarne Stroustrup abgeschaltet und
nichts verstanden. Und lieber meine Original Ausgabe von K&R
geschmökert.
> Ich habe schon vor zig> Jahren um die Jahrtausendwende beim Bjarne Stroustrup abgeschaltet
Der Stroustrup ist ganz gut. Zumindest kann man sich da das Konzept der
OOP erarbeiten. Auch als C-Programmierer!
Beim OOP gehts nur um das Konzept und das dann sinngemäß umzusetzen
(wenn sinnvoll).
Und nicht vergessen, C++ hat als Präprozessor für C angefangen.
> und nichts verstanden. Und lieber meine Original Ausgabe von K&R> geschmökert.
K&R find ich absolut fürchterlich. C in a nutshell von Koenig bei
O'Reilly ist zum Nachschlagen und Auffrischen sehr gut. Nehme ich immer
wieder gerne her.
Nick
S. R. schrieb:> Christian J. schrieb:>> Ok, C++... Klassen und gekapselte Daten aber>> das kann ich leider nicht, ging bisher ohne auch.>> Du musst ja nicht auf C++24 und kryptographischen Quelltext gehen,> sondern dich erstmal auf "C-with-classes" beschränken.>> Aus der C-Sicht ist eine Klasse erstmal nichts weiter als eine normale> Struktur mit zusätzlichen Funktionen. Jedes Element (Variable oder> Funktion) kann für externen Zugriff blockiert (private) oder freigegeben> (public) werden.>> Fertig. Das allein sollte dir weiterhelfen.> Und wenn Wilhelm loslegt, hörst du erstmal weg. :-)
Ok, ich halte mich zurück ;-)
Aber: C++24 fällt durchs Raster ... C++23 wird spannend genug!
Christian J. schrieb:>> Du musst ja nicht auf C++24 und kryptographischen Quelltext gehen,>> sondern dich erstmal auf "C-with-classes" beschränken.>> Reicht das das Studium einer Arduino Lib erstmal? Oder gleich> das geballte 500 Seiten Werk "C++ für Dummies"? Ich habe schon> vor zig Jahren um die Jahrtausendwende beim Bjarne Stroustrup> abgeschaltet und nichts verstanden.
Um in deinen kleinen C-Code eine oder zwei Klassen einzubauen, um von
einer globalen Monsterstruktur wegzukommen, braucht man weder das eine
noch das andere.
Aber wenn du dich partout weigerst, auch nur den kleinsten Schritt in
diese Richtung zu gehen, dann ist das Thema ohnehin tot.
> Und lieber meine Original Ausgabe von K&R geschmökert.
Wer nicht will, der hat schon.
Christian J. schrieb:> Moin,>> auch mein Hobby Code soll sauber sein. Bei einem größeren Projekt mit> einer Wetterstation für den F429, die auch noch 3 externe Sensoren> bedient und ein extra Anzeige Display gibt es bei mir eine größere> globale Struktur, in der etliche Wetter-Werte liegen, viele davon auch> noch als Arrays of struct, da 5 Tage stundenweise gespeichert werden.> Deswegen, damit das Ganze mit __packed in einem Rutsch per Funk an einen> PC übertragen werden kann. Der Trümmer ist rund 4k gross, global> external im main.h deklariert und in main.c implementiert, liegt per> Linker Direktive im residenten Batt-gepufferten BKRAM drin, nahezu alle> Routinen greifen darauf zu aus den ca 9 .c Files. Die einen füllen ihn,> andere holen sich für die LCD Grafik alles da raus.
Hört sich nach FORTRAn an: alles in einem common-block.
> Ok, C++... Klassen und gekapselte Daten aber das kann ich leider nicht,
Warum KANNST du das nicht? Du kannst doch auch 16-Bit in ein uint16_t
packen ...
S. R. schrieb:> Um in deinen kleinen C-Code eine oder zwei Klassen> einzubauen, um von einer globalen Monsterstruktur> wegzukommen, braucht man weder das eine noch das> andere.
Mal eine Frage: Woher kommt eigentlich der
ununterdrückbare Drang auf µC.net, einem Fragesteller
eine deutlich kompliziertere Lösung zu empfehlen, als
er braucht?
Soweit ich das beurteilen kann, hat sein Problem mit
Kapselung zu tun. Kapseln kann man aber auch in
ANSI-C; dazu braucht's kein C++.
Egon D. schrieb:> Soweit ich das beurteilen kann, hat sein Problem mit> Kapselung zu tun. Kapseln kann man aber auch in> ANSI-C; dazu braucht's kein C++.
Ich finde die Grundlagen von Klassen jetzt nicht besonders kompliziert.
Insbesondere dann nicht, wenn es sich auf dem Niveau von "class als
struct mit Funktionen" drin bewegt. Nix Mehrfachvererbung, Templates,
Lambdas, STL, zukünftige Coolness oder so.
Aber ja, man kann auch in C ordentlich kapseln und auch komplette OOP
mit (Einfach-)Vererbung hochziehen. Ich habe nie das Gegenteil
behauptet.
Daniel schrieb:> Du kannst in der main getter und setter implentieren und die Struktur in> der main.c definieren.
Angenommen ich habe eine Struct von 100 Parametern mit 5 Elementen.
Brauche ich dann 1000 Getter+Setter oder wie habe ich mir das
vorzustellen?
Peter D. schrieb:> Daniel schrieb:>> Du kannst in der main getter und setter implentieren und die Struktur in>> der main.c definieren.>> Angenommen ich habe eine Struct von 100 Parametern mit 5 Elementen.
Was meinst Du mit Parametern bei einem struct?
> Brauche ich dann 1000 Getter+Setter oder wie habe ich mir das> vorzustellen?
Wenn Du vorher eclipse benutzt hast, bestimmt noch mehr ;-)
Nop schrieb:> Peter D. schrieb:>> Angenommen ich habe eine Struct von 100 Parametern mit 5 Elementen.>> Sowas solltest Du gar nicht erst haben.
Naja, MySQL auf uc ist vermutlich etwas heftig.
Nop schrieb:> Sowas solltest Du gar nicht erst haben.
D.h. ich soll keine Steuerungen mehr bauen?
Die Physiker wollen bei Teilchenquellen und Analysatoren nen Haufen
Spannungen/Ströme setzen, lesen, überwachen und regeln können. 100
Parameter ist da eher eine kleine Anzahl.
> Angenommen ich habe eine Struct von 100 Parametern mit 5 Elementen.
Dann ist das vermutlich ein enum als Parameter und es gibt eine getter
und eine setter-Funktion.
Nick
Anbei mal die Struct.
Nick M. schrieb:> Dann ist das vermutlich ein enum als Parameter und es gibt eine getter> und eine setter-Funktion.
Enum ja, aber alle können direkt zugreifen.
Egon D. schrieb:> Zugriff auf die Daten konsequent NUR über Getter und Setter, NIEMALS> direkt auf die Variablen.Peter D. schrieb:> Angenommen ich habe eine Struct von 100 Parametern mit 5 Elementen.> Brauche ich dann 1000 Getter+Setter oder wie habe ich mir das> vorzustellen?
Auch wenn es in einigen Anfänger-C++-Lehrbüchern so diktiert wird: Für
jede Membervariable grundsätzlich und ohne nachzudenken eine Set- und
Get-Funktion zu schreiben, ist Unfug. Wäre das so gewollt, gäbe es in
C++ weder Structs noch Public-Variablen.
Dieser Meinung ist übrigens auch die C++-Prominenz:
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rh-get
Peter D. schrieb:> Nop schrieb:>> Sowas solltest Du gar nicht erst haben.>> D.h. ich soll keine Steuerungen mehr bauen?
Du sollst Steuerungen nicht mit einem Megastruct bauen.
Peter D. schrieb:> Enum ja, aber alle können direkt zugreifen.
Worauf? Auf die Daten? Das wäre nicht gewünscht. Der enum sagt, auf
welches Datum mit dem getter/setter zugeriffen wird. Abhängig vom Typen.
Nick
Yalu X. schrieb:> Egon D. schrieb:>> Zugriff auf die Daten konsequent NUR über Getter und>> Setter, NIEMALS direkt auf die Variablen.>> Peter D. schrieb:>> Angenommen ich habe eine Struct von 100 Parametern mit>> 5 Elementen. Brauche ich dann 1000 Getter+Setter oder>> wie habe ich mir das vorzustellen?>> Auch wenn [...]
Quotemarder. Pfui!
Egon D. schrieb:> Quotemarder. Pfui!
Wieso Quotemarder?
Habe ich deine Aussage in irgendeiner Form sinnentstellend zitiert?
Wenn ja, musst du mir erklären, was du damit gemeint hast. Für mich ist
ihr Inhalt – nicht zuletzt durch die Hervorhebung der Wörter "NUR" und
"NIEMALS" – völlig unmissverständlich, unabhängig vom Kontext.
Yalu X. schrieb:> Egon D. schrieb:>> Quotemarder. Pfui!>> Wieso Quotemarder?>> Habe ich deine Aussage in irgendeiner Form> sinnentstellend zitiert?
Durchaus, ja. :)
> Wenn ja, musst du mir erklären, was du damit gemeint hast.
Meine Aussage war:
"Wenn man eine Variable kapselt, muss man sie konsequent
kapseln. Das bedeutet, man greift NUR über die Methoden
und NIEMALS direkt auf die Variable zu -- und das geht
sogar auch in einer nicht-OOP-Sprache."
Du argumentierst aber gegen:
"Wann man kapselt, muss man IMMER ALLE Variablen kapseln."
Letzteres ist jedoch überhaupt nicht meine Aussage.
Egon D. schrieb:> Meine Aussage war:> "Wenn man eine Variable kapselt, muss man sie konsequent> kapseln. Das bedeutet, man greift NUR über die Methoden> und NIEMALS direkt auf die Variable zu -- und das geht> sogar auch in einer nicht-OOP-Sprache."
Das hört sich vernünftig an, und das Ganze war wohl ein Missverständnis.
Ich habe deine Teilaussagen ursprünglich so verstanden:
Egon D. schrieb:> Unabhängig von der Sprache - Kapseln:
"Man sollte immer Kapseln, auch in nicht OO-Sprachen."
> Zugriff auf die Daten konsequent NUR über Getter und Setter, NIEMALS> direkt auf die Variablen.
"Da immer gekapselt wird, darf der Zugriff auf (Member-)Variablen nur
über Getter und Setter erfolgen."
Nachdem das Missverständnis nun aufgelöst ist, gehe ich davon aus, wir
beider der Meinung sind, dass Peda für seine 500 Struct-Elemente keine
Get- und Set-Funktionen braucht, oder?
Yalu X. schrieb:> Ich habe deine Teilaussagen ursprünglich so verstanden:>> Egon D. schrieb:>> Unabhängig von der Sprache - Kapseln:>> "Man sollte immer Kapseln, auch in nicht OO-Sprachen."
Verstehe. -- Nein, die Langform ist eher ungefähr so:
"Ich bin nicht qualifiziert, Dir einen Rat zu C oder C++
zu geben, aber Dein konkretes Problem lässt sich durch
sinnvolle Kapselung lösen, und Kapselung ist weitgehend
unabhängig von der konkreten Sprache anwendbar. Die
Regeln dafür sind folgende:..."
> Nachdem das Missverständnis nun aufgelöst ist, gehe ich> davon aus, wir beider der Meinung sind, dass Peda für> seine 500 Struct-Elemente keine Get- und Set-Funktionen> braucht, oder?
Ja natürlich -- zumindest nicht für jedes Element einzeln :)
Wann immer Getter und Setter trivial sind, sind sie auf Überflüssigkeit
zu prüfen. Leider bestehen viele Tools darauf, erstmal sämtliche dieser
Funktionen zu erzeugen und weil Dogmatik zählt, macht die auch keiner
weg.
Aber hey, in Kotlin wird jedes getX/setX-Pärchen als "virtuelle
Membervariable" X betrachtet. Dann sieht es im Code wieder so aus, als
ob man Membervariablen zuweist. Im Bytecode ist es natürlich trotzdem
ein Funktionsaufruf.
Eine Struktur mit 100 Elementen als Klasse zu machen fällt übrigens
meistens unter "Gottobjekt" und ist nach Dogma ebenfalls zu vermeiden.
Aber manchmal braucht man sowas trotzdem.
Christian J. schrieb:> Ok.... ich lass euch dann mal weiter diskutieren. Meine Frage ist> eigentlich beantwortet. Danke! Bin dann mal raus hier...
Was ist denn Dein Fazit?
Christian J. schrieb:> Meine Frage ist> eigentlich beantwortet. Danke! Bin dann mal raus hier...
Solche Antworten fallen unter die Rubrik, hätte man besser sein lassen
sollen.
Wem willst Du damit helfen?
Wilhelm M. schrieb:> Was ist denn Dein Fazit?
Dass man das so kapseln kann wie direkt oben beschrieben. Oder eben C++
verwenden. Zumindest werde ich es bei größeren Sachen machen, nicht aber
wenn ich wie gerade nur 2 Module habe und es läuft und nie erweitert
werden wird.
Mein "Game of Life" hat auch zwei Felder, wäre aber sinnlos die zu
kapseln. Ein Zugriff feld.alt[x][y] und feld.neu[x][y] bleibt immer so,
zudem er bidirektional erfolgt. Da ändert auch ein Getter/Setter nichts
dran.
Christian J. schrieb:> Mein "Game of Life" hat auch zwei Felder, wäre aber sinnlos die zu> kapseln.
Du könntest mehr Performance hinkriegen, wenn Du die kapselst und dann
nur indirekt über Pointer an die Berechnungsfunktion gibst. Dann kannst
Du Dir nämlich das Umkopieren von feld.neu nach feld.alt am Anfang eines
Berechnungszyklus sparen.
Nop schrieb:> Du könntest mehr Performance hinkriegen, wenn Du die kapselst und dann> nur indirekt über Pointer an die Berechnungsfunktion gibst.
In dem Fall allerdings eher nicht. 1 Feld Lesen, "umkreisen" und 2.tes
Feld erzeugen für die Grafik-Funktion, die das Feld abbildet.
Peter D. schrieb:> Nop schrieb:>> Sowas solltest Du gar nicht erst haben.>> D.h. ich soll keine Steuerungen mehr bauen?> Die Physiker wollen bei Teilchenquellen und Analysatoren nen Haufen> Spannungen/Ströme setzen, lesen, überwachen und regeln können. 100> Parameter ist da eher eine kleine Anzahl.
Er meinte glaube, dass du kein einzelnes struct mit 100+ Members haben
sollst.
zB kannste den Stromteil in ein struct packen, dass dan in dem großen
struct wohnt.
Dann kannste dieses "substruct" der Funktion für die Stromeinstellung
geben anstatt ALLES vom struct zu übergeben.
mbedTLS hat das so, da gibts structs in structs in structs in structs in
structs usw.
Oben haste das große struct in der Hand für die eine TLS Verbindung,
dann gibts in diesem ein struct für zB das Zertifikat, dann noch eins
für das Schküsselpaar und eiens für die Sockets. Das Zertifikatstruct
hat dann wieder memberstructs für die certchain etc.
Das sieht aber trotzdem gruselig aus:
https://tls.mbed.org/api/structmbedtls__ssl__context.html
Egon D. schrieb:> Mal eine Frage: Woher kommt eigentlich der> ununterdrückbare Drang auf µC.net, einem Fragesteller> eine deutlich kompliziertere Lösung zu empfehlen, als> er braucht?>> Soweit ich das beurteilen kann, hat sein Problem mit> Kapselung zu tun. Kapseln kann man aber auch in> ANSI-C; dazu braucht's kein C++.
Weil irgendwann mal Irgendjemand was von angeblichen Vorzügen bestimmter
Programmierweisen gelesen (oder in der Vorlesung gehört) hat, sei es
Wiederverwendbarkeit oder eben Kapselung, und seit dem das ständig für
Besser unter allen Umständen hält, er betet es wie ein Mantra runter
ohne jemals selbst nachgedacht zu haben.
Das wirkt halt wie Werbung, Gehirn ausschalten.
Leider leiden unter solchen Leuten diejenigen, die wirklich die
Hntergründe verstehen wollen und es richtig machen wollen.
Eine globale Variable erfordert nämlich zunächst mal einen Zugriff
LD reg,(address)
ist die Variable auf dem Stack muss der Stackframe addiert werden
LD pointerreg,address
ADD pointerreg,SP
LD reg,(pointerreg)
und hat man es malloct geht der Zugriff per Offset über eine pointer
LD pointerreg,address
ADD pointerreg,fieldoffset
LD reg,(pointerreg)
kommt ein getter/setter auf ein self hinzu muss ein call sein
PUSH self
CALL getter
POP pointerreg (meist noch komplizierter compiliert)
ADD pointerreg,fieldoffset
LD returnreg,(pointerreg)
RET
LD reg,returnreg ; vielleicht dasselbe
es sei denn der Compiler inlint den Aufruf.
Weil die meisten gar nicht wissen, welchen Overhead ihre
Fehlentscheidungen so bewirken, sind heute alle Programme, auch bei 1000
mal leistungsfähigerer Hardware, meistens lahmarschiger als noch vor 30
Jahren, und natürlich 100 mal grösser.
JEDE Programmierweise, die (zur vollständigen Bearbeitung der Aufgabe
inkl Fehlerbehandlung) auch nur 1 Befehl mehr auszuführenden code
bewirkt, ist letztlich ein Programmierfehler.
Aber wenn es um die Worte geht, mit denen diese Programmierer ihre
Fehler verteidigen, dann haben sie plötzlich tausend mal mehr Worte und
Elan dafür, als für ein richtiges Programm.
Kommt da auch noch was konstruktives? Weil wenn das Mantra "da beste
Assembler is kein Assembler" ist, dann kann der Threadersteller seinen
F429 kübeln und sich an Wetterfrosch zulegen...
Michael B. schrieb:> JEDE Programmierweise, die (zur vollständigen Bearbeitung der Aufgabe> inkl Fehlerbehandlung) auch nur 1 Befehl mehr auszuführenden code> bewirkt, ist letztlich ein Programmierfehler.
Performance ist halt nicht die einzig relevante Metrik. In dem Maße, wie
die Software komplexer wird, braucht man auch Methoden, diese
Komplexität zu strukturieren, weil das sonst unwartbar wird.
Insbesondere, wenn sich wegen der normalen Job-Fluktuation auch noch
jemand außer dem ursprünglichen Entwickler in überschaubarem Zeitrahmen
in die Codebasis einarbeiten soll. Ebenso, wenn mehrere Entwickler am
selben Projekt arbeiten, das tun sie dann gegen definierte
Schnittstellen.
Unabhängig von der Programmiersprache sind Kapselung und Schichten ein
probates Mittel, weil sie eine bestimmte Aufgabe in einem recht
abgeschlossenen Bereich erledigen, der nach außen durch seine
Schnittstellen interagiert. Will man da was ändern, muß man sich erstmal
eben nur in diesen Bereich reinarbeiten.
Michael B. schrieb:> JEDE Programmierweise, die (zur vollständigen Bearbeitung der Aufgabe> inkl Fehlerbehandlung) auch nur 1 Befehl mehr auszuführenden code> bewirkt, ist letztlich ein Programmierfehler.
Das ist nun echt Quark. Heute geht es mehr darum dass der Mensch den
Code noch versteht und strukturieren kann als dass die Maschine den
Idealcode zu sehen kriegt. Kannst ja versuchen eine optische KI in
Assembler zu programmieren. Die Dinger sind so schnell, dass da ein paar
Zeilen mehr nicht auffallen. Und Getter und Setter werden vom Compiler
vollständig aufgelöst, die existieren gar nicht mehr!
Christian J. schrieb:> Und Getter und Setter werden vom Compiler> vollständig aufgelöst, die existieren gar nicht mehr!
Zumindest beim AVR-GCC werden sie das in der Regel nicht. Und dann wird
rumgerechnet, bis der Arzt kommt, wärend auf die globale Struct bei
bekanntem Index einfach direkt mit LDS zugegriffen wird.
Ist der Index variabel, kann man dem Compiler deutlich auf die Sprünge
helfen, indem man schon die Basisadresse berechnet und
zwischenspeichert.
Peter D. schrieb:> Zumindest beim AVR-GCC werden sie das in der Regel nicht. Und dann wird> rumgerechnet, bis der Arzt kommt, wärend auf die globale Struct bei> bekanntem Index einfach direkt mit LDS zugegriffen wird.
Dann mach mal ein konkretes Beispiel, wo das nicht passiert.
Peter D. schrieb:> Ist der Index variabel, kann man dem Compiler deutlich auf die Sprünge> helfen, indem man schon die Basisadresse berechnet und> zwischenspeichert.
Was muss man denn da berechnen? Da steht doch bloss das & vor und das
ist eine Adresse. Der Linker kennt die. Auch dass der GCC das nicht
auflöst halte ich für ein Gerücht. Was ich gesehen habe bei -Os ist,
dass der alles inline auflöst, was nur aus einer Zeile besteht oder
irgendwie zur Kompilerzeit berechenbar ist.
Wilhelm M. schrieb:> Dann mach mal ein konkretes Beispiel, wo das nicht passiert.
Ohne LTO? Wenn die in einer anderen Datei liegen. Wenn sie als Teil
eines Interfaces extern (in einer Bibliothek) sind, sowieso.
Wenn natürlich Getter/Setter mit "static" dekoriert in der gleichen
Datei wie Datenstruktur und aufrufender Code rumhängen, wird das alles
aufgelöst.
S. R. schrieb:> Ohne LTO?
Vermute ich auch ...
S. R. schrieb:> Wenn sie als Teil> eines Interfaces extern (in einer Bibliothek) sind, sowieso.
Deswegen bevorzuge ich header-only Bibliotheken. Da hat man mit sowas
gar kein Thema.
Christian J. schrieb:> Was muss man denn da berechnen?
Basisadresse + Index * Structgröße.
Danach kann er mit LDD + Displacement auf die einzelnen Elemente
zugreifen.
Legt man diesen Pointer nicht an, dann macht er die Berechnung für jeden
Zugriff neu.