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!
Dirk B. schrieb: > ...(4k auf Mikrocontroller ist blöd)... Was ist denn daran "blöd" auf eine µC der 256k RAM hat?
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.
Naja, es ging nur um das hier... schon 2 Jahre her. Kann man sicher eleganter machen.
1 | struct maxima_t { |
2 | /* --- Board Sensoren --- */
|
3 | float P_Max, |
4 | P_Min, |
5 | T_Innen_Max, |
6 | T_Innen_Min, |
7 | /* -- Funk Sensor -- */
|
8 | T_Ext_Max, |
9 | T_Ext_Min, |
10 | F_Ext_Max, |
11 | F_Ext_Min; |
12 | };
|
13 | |
14 | typedef struct |
15 | {
|
16 | int32_t id; // = 0x1234 nach Initialisierung |
17 | uint16_t ptr; |
18 | float P_Now, // Aktueller Druckwert |
19 | T_Now; // Aktueller Innen-Temperaturwert |
20 | |
21 | uint32_t P_MaxGraph, // Aktuell oberer Druckwert im Bargraphen |
22 | P_MinGraph; |
23 | |
24 | float T_MaxGraph, // Schwankungen um den Messpunkt |
25 | T_MinGraph; |
26 | |
27 | // Maxima der Historie: Innen-Temperatur, Druck
|
28 | float T_MaxInHistory, // Maximaler gemessener INNEN Temperaturwert |
29 | T_MinInHistory, // Minimaler gemessener INNEN Temperaturwert |
30 | T_AvgInHistory; |
31 | |
32 | uint32_t P_MaxInHistory, // Maximaler gemessener Druckwert |
33 | P_MinInHistory; // Minimaler gemessener Druckwert |
34 | float P_AvgInHistory; |
35 | |
36 | float RegrSteigung; // Steigung der Regressionsgeraden |
37 | float Ext_Temperature; |
38 | float Ext_Feuchte; |
39 | float Ext_UBatt; |
40 | gebiet_t gebiet; // Hoch, Tief, Neutral |
41 | uint8_t ZFN; // Zambretti Forecast Letter |
42 | struct data_t History[HISTORY_MAX_IDX+1]; // Historie, stuendliche Eintragungen |
43 | |
44 | } Work_t __attribute__ ((packed)); |
45 | |
46 | |
47 | // __attribute__ ((packed))
|
48 | |
49 | // Flags für die Zeit
|
50 | typedef struct { |
51 | _Bool rtc_1s, |
52 | rtc_sec10, |
53 | rtc_1min, |
54 | rtc_1hr, |
55 | rtc_120min, |
56 | rtc_1day, |
57 | tag_nacht_wechsel, |
58 | night, |
59 | rf_timeout, // 1 = RF Modul meldete sich nicht mehr |
60 | f_SendHistoryToDisplay, |
61 | NRF24L01_IsPresent, |
62 | backlight; |
63 | |
64 | } flags_t; |
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: > Angenommen ich habe eine Struct von 100 Parametern mit 5 Elementen. Sowas solltest Du gar nicht erst haben.
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.
:
Bearbeitet durch User
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.
Ok.... ich lass euch dann mal weiter diskutieren. Meine Frage ist eigentlich beantwortet. Danke! Bin dann mal raus hier...
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.
1 | eeadcval_t* adc_eept = cmd->adc_eept; |
2 | adc_eept->offset = meas[1].input - meas[1].val * adc_eept->gain; |
Manchmal braucht man sogar mehrstufige Structpointer:
1 | cmd->adc_eept->comp = (float)get_adc_raw(cmd->idx) / get_adc_raw(comp_idx); |
Der Getter ist hier notwendig, weil der Parameter-Index noch in den physischen ADC-Pin umgerechnet werden muß.
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.
Hier mal ein Beispiel aus meinem Scheduler. Eimal mit und einmal ohne Struct-Pointer.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.