Ich versuche die Instanz einer Klasse in den EEMEM eines AVR zu legen.
Das .eep wird, bei vorhandensein von Methoden, mit Nullen angelegt.
Eigentlich sollte die Initialisierung im .eep stehen, wie auch bei jedem
Struct denn es ist im Prinzip nichts anderes.
Die Instanz im RAM kann aber per Blockoperation ohne Probleme in das
EEPROM gesichert und ausgelesen werden.
Interessant ist, das ein gleich aufgebautes Struct ein korrektes .eep
erzeugt und ich dieses sogar in die Instanz der Klasse laden kann.
Natürlich ist das nicht besonders schön...
Ich habe ein kurzes "Testprogramm" mit Kommentaren angehängt.
Vielleicht findet jemand meinen Denkfehler, oder steckt da ein Fehler im
Compiler ?
Entschuldige,
PlanOldData ?
Dann wäre es wohl sinnvoller ein Struct in die Klasse zu packen und
darüber den Zugriff ab zu wickeln.
Aber warum sollte das nicht funktionieren ?
Eine Klasse unterscheidet sich von einem Struct doch nur um den this
Zeiger ?
Die Instanz lässt sich bei meinen Versuchen in das EEProm sichern und
auch wieder auslesen, quasi wie bei einem Struct.
Habe auch schon Zähler hochlaufen lassen etc.
Nur die Initialisierung per .eep funktioniert nicht.
Thomas schrieb:> Ich versuche die Instanz einer Klasse in den EEMEM eines AVR zu legen.> Das .eep wird, bei vorhandensein von Methoden, mit Nullen angelegt.
Funktioniert es, wenn du keine Methoden in der Klasse hast?
Unabhängig davon:
1
while(1){}
solltest du ändern, da undefined behaviour in C++.
Genau,
Ohne Methoden stimmt das .eep.
Vermutlich wird dann auch class als struct behandelt.
Schreibe ich Methoden in das struct geht es wieder schief.
Ich vermute das hier die Fallunterscheidung greift.
Aus meiner sicht mache ich doch nichts anderes als variablen zwischen
ram und eemem zu verschieben. Das scheint ja auch zu funktionieren.
Speicherobjekte muss ich doch auch zwischen ram heap und stack kopieren
können.
So wie es aussieht funktioniert das auch.
Nur wird die eemem sektion bei der klasseninstanz nicht verarbeitet.
Thomas W. schrieb:> Entschuldige,>> PlanOldData ?
Ja.
> Dann wäre es wohl sinnvoller ein Struct in die Klasse zu packen und> darüber den Zugriff ab zu wickeln.
Struct und Klasse sind in C++ das gleiche.
> Aber warum sollte das nicht funktionieren ?
Ich vermute, sobald du einen Konstruktor definierst, wird die
Initialisierung anders gemacht.
> Eine Klasse unterscheidet sich von einem Struct doch nur um den this> Zeiger ?
Nein. Der this-Zeiger hat damit überhaupt nichts zu tun. Der einzige
Unterschied zwischen Klasse und Struct sind die default-Zugriffsrechte
und Vererbung. Die sind bei Klassen private und bei Struct public.
Thomas W. schrieb:> PlanOldData ?
plaIn old data
> Dann wäre es wohl sinnvoller ein Struct in die Klasse zu packen und> darüber den Zugriff ab zu wickeln.
Und wie soll das funktionieren? Du kannst nicht einen Teil der Struktur
im EEprom haben und einen Teil im RAM.
> Aber warum sollte das nicht funktionieren ?
Ei weil EEMEM nur die Ablage des Codes regelt, aber nicht der Zugriff.
Wenn du zum Beispiel Code bastelst — wie oben geschehen — der die
Struktur zur Laufzeit initialisiert (ausgeführt in einem impliziten,
statischen Konstruktor — oder wie auch immer C++ sowas nennt, wenn es
denn überhaupt einen Namen dafür hat) dann schreibt dieser Code ins RAM
anstatt den EEprom zu initialisieren.
Jeglicher Schreib- oder Lesezugriff muss via der eeprom_-Funktionen aus
avr/eeprom.h (oder Entsprechungen davon) erfolgen. Du kannst nicht g++
verwenden, um diese Zugriffe für dich generieren zu lassen.
Wenn du sicherstellen willst, dass du nicht versehentlich C++-Features
mit EEMEM oder PROGMEM kombinierst, die nicht kompatibel mit C++ sind,
dann pack alles EEMEM und PROGMEM Zeugs in C-Module, übersetz diese mit
avr-gcc anstatt avr-g++, und schreib Interfaces, welche die Zugriffe
abwickeln.
> Die Instanz lässt sich bei meinen Versuchen in das EEProm sichern und> auch wieder auslesen, quasi wie bei einem Struct.
Gründe deine Programmierung auf Wissen über die Sprache, nicht auf
"Versuche", die mal "funktioniert" haben.
Aus dem Sprachstandard kann man auf ein Progamm schließen, aber aus
einem in einer konkreten Situation funktionierenden Binärcode lässt sich
NICHT schließen, dass man gültige Konstrukte benutzt hat.
Ich kenne keinen anderen Denkfehler, der in der Programmierung auch nur
ansatzweise so häufig gemacht wird wie dieser nicht-gültige
Umkehrschluss.
mh schrieb:> Unabhängig davon:while (1) {}> solltest du ändern, da undefined behaviour in C++.
Koenntest du das mal naeher erklaeren, eventuell mit einer Verlinkung
auf die passende Stelle im C++ Standard?
Kaj G. schrieb:> mh schrieb:>> Unabhängig davon:while (1) {}>> solltest du ändern, da undefined behaviour in C++.> Koenntest du das mal naeher erklaeren, eventuell mit einer Verlinkung> auf die passende Stelle im C++ Standard?
1
Progress guarantee
2
3
In a valid C++ program, every thread eventually does one of the following:
4
5
* terminate
6
* makes a call to an I/O library function
7
* performs an access through a volatile glvalue
8
* performs an atomic operation or a synchronization operation
9
10
No thread of execution can execute forever without performing any of
Kaj G. schrieb:> mh schrieb:>> Unabhängig davon:while (1) {}>> solltest du ändern, da undefined behaviour in C++.> Koenntest du das mal naeher erklaeren, eventuell mit einer Verlinkung> auf die passende Stelle im C++ Standard?
Aus dem C++17 draft (N4687)
1
4.7.2 Forward progress [intro.progress]
2
The implementation may assume that any thread will eventually do one of the following:
3
(1.1) — terminate,
4
(1.2) — make a call to a library I/O function,
5
(1.3) — perform an access through a volatile glvalue, or
6
(1.4) — perform a synchronization operation or an atomic operation.
7
[ Note: This is intended to allow compiler transformations such as removal of empty loops, even when
8
termination cannot be proven. — end note ]
"may assume" = alles andere ist UB. Das gleiche oder zumindest etwas
sehr ähnliches sollte auch in c++11 und c++14 stehen.
C11 sagt übrigens etwas sehr ähnliches, macht aber eine Ausnahme für
"while(1)" und co.
Aus dem C11 draft (N1570)
1
6.8.5 Iteration statements
2
...
3
6) An iteration statement whose controlling expression is not a constant expression, that performs no input/output operations, does not access volatile objects, and performs no synchronization or atomic operations in its body, controlling expression, or (in the case of a for statement) its expression-3, may be assumed by the implementation to terminate.
Johann L. schrieb:> Thomas W. schrieb:>> Aber warum sollte das nicht funktionieren ?>> Ei weil EEMEM nur die Ablage des Codes regelt, aber nicht der Zugriff.> Wenn du zum Beispiel Code bastelst — wie oben geschehen — der die> Struktur zur Laufzeit initialisiert (ausgeführt in einem impliziten,> statischen Konstruktor — oder wie auch immer C++ sowas nennt, wenn es> denn überhaupt einen Namen dafür hat) dann schreibt dieser Code ins RAM> anstatt den EEprom zu initialisieren.
Ich vermute da habe ich Dir mein Problem nicht richtig erklärt :
1. Eine Instanz wird im RAM angelegt :
1
DATABLOCKdataRAM;
2. Eine Instanz wird im EEMEM angelegt, natürlich lässt sich die nicht
"benutzen"...
1
DATABLOCKEEMEMdataEEP;
3. Jetzt muss ich doch den Inhalt von dataRAM <> dataEEP in beide
Richtungen kopieren können.
Dazu muss ich doch nur deren "Speicherobjekte" im Block schreiben oder
lesen.
Das muss doch im Sprachstandard von C++ erlaubt sein?
Die Sprache kann mir doch nicht den direkten Zugriff auf Speicherobjekte
"verbieten"?
Die ganzen LIBs wie Boost etc. müssen unter der Haube doch auch solche
Konstrukte verwenden.
...und das funktioniert in meinem Test auch.
Vielleicht wäre es mit Copy Constructor schöner anzusehen aber mir ging
es nur darum das Problem möglichst einfach zu umreissen.
Das einzige Problem was ich jetzt habe ist das bei einem blanken Struct
eine korrekte .eep für die Vorinitialisierung des EEMEM erzeugt wird.
Bei einer Klasseninstanz geht es schief, da stehen nur Nuller aber es
wird wenigstens der richtige Speicherbereich im EEMEM reserviert.
Ich denke nicht dass das .eep verhalten im C++ Standard festgehalten ist
?
>> Die Instanz lässt sich bei meinen Versuchen in das EEProm sichern und>> auch wieder auslesen, quasi wie bei einem Struct.>> Gründe deine Programmierung auf Wissen über die Sprache, nicht auf> "Versuche", die mal "funktioniert" haben.
Ich lege kein "Versuch und Fehler" Taktik an den Tag.
Es ist nur der Versuch herauszufinden was der Compiler macht, denn auch
er hat Spielraum.
Besonders bei einer Implementierung wie auf dem AVR.
Ich habe auch erfolgreich versucht aus dem Struct, was ja ein valides
.eep erzeugt, in das RAM Objekt zu lesen.
Aber das wäre eindeutig so ein Ding was unter "Dirty hack" fällt und ich
aus den Gründen die Du in Deinem letzten Absatz beschreibst nicht
machen würde...
C++ kennt die AVR-Spielchen mit mehreren Adressräumen nicht. Der GCC
hilft ein bischen, indem er über Attribute die Adressräume zu trennen
erlaubt und die AVR-binutils diese dann in getrennte Files ablegen.
Bei Flash gibt es noch den Trick, daß die Lesefunktionen auf speziellen
Assemblerbefehlen beruhen, die wie auch immer
(Compiler-Bachend/AVRlibc/...) im Befehlstrom landen.
Für das initialisieren einer EEProm-Klasse müßte das Backend schreibend
auf den EeProm-Bereich zugreifen (das wird bei Flash auch nicht
unterstützt), was rein mit Mitteln des Instruction-Set nicht möglich
ist. Dazu müßte es Library-Unterstützung geben, die aber, anders als
"multipliziere 2 int's", nicht nur von der CPU-Architektur abhängig
wäre, sonder von explizitem Chip. All diese Chip-spezifischen Dinge sind
in der AVRLibc versteckt, z.B. EEPron-Zugriffe.
Was der Compiler macht, ist die Adresse in dem alternativen Adressraum
auf Anfrage (&) rauszurücken.
Ich würde die EEProm-Struktur wie gehabt anlegen (mit Initialwerten, die
in .eep wandern) und eine "Proxy"-Klasse verwenden, die die Adresse der
EEProm-Klasse im Konstruktor übergeben bekommt. Damit kann sie dann
Lesen/Schreiben und bei Bedarf auch in einem lokalen Cache halten. Wenn
man die EEProm-Daten tatsächlich als Struktur ablegt, was auch für ein
bekanntes Layout EEProm sorgt, wenn man das direkt beackern will, dann
ist der "eine Adresse" Overhead verkraftbar. Oder man legt die
EEProm-Daten gleich an fixe Adressen, dann darf die EEPromCache-Klasse
auch ein Template mit Parametern "Typ" und "Adresse" sein.
Thomas W. schrieb:> Das muss doch im Sprachstandard von C++ erlaubt sein?> Die Sprache kann mir doch nicht den direkten Zugriff auf Speicherobjekte> "verbieten"?> Die ganzen LIBs wie Boost etc. müssen unter der Haube doch auch solche> Konstrukte verwenden.
Das muss nicht erlaubt und du musst keinen direkten Zugriff haben. Was
passiert mit dem Destruktor wenn du einfach so ein Objekt überkopierst?
Was ist mit geschützten (private) Datamembern?
Thomas W. schrieb:> Ich lege kein "Versuch und Fehler" Taktik an den Tag.> Es ist nur der Versuch herauszufinden was der Compiler macht, denn auch> er hat Spielraum.
Es bleibt "Versuch und Fehler" solange du Versuchst herauszufinden was
der Compiler macht. Du musst herausfinden was der Standard sagt.
Carl,
ich vermute jetzt hat es bei mir klick gemacht, danke !!!
In meiner Naiven Sichtweise ist eine Klasseinstanz nur eine Ansammlung
von Variablen auf die halt über "Sicherungsmechanismen" zugegriffen
wird.
Das entspricht der Erfahrung mit dem Struct das ich in die Instanz laden
konnte. (Sehr schlecht, sollte man unter keinen Umständen machen)
Der vom Compiler gebaute Standardkonstruktor initialisiert bei der
Erzeugung alle Variablen wie ich sie in der Klassendefinition vorbelegt
habe.
Das klappt im RAM aber nicht im EEPROM, soweit klar.
Aber vom Prinzip her wäre es doch ein Const Objekt und der "Konstruktor"
könnte in die .eep schreiben... ;)
(Was derzeit nur bei einem POD gemacht wird)
Bitte nicht falsch verstehen,
ich will die Instanz nicht im EEPROM verwenden, nur speichern.
Welche Regel sollte mir das verbieten ?
Wenn die Vorinitialisierung per .eep nicht unterstützt wird, so muss
doch mein Speichern und Laden "vom Standard gedeckt" sein.
mh schrieb:> Das muss nicht erlaubt und du musst keinen direkten Zugriff haben. Was> passiert mit dem Destruktor wenn du einfach so ein Objekt überkopierst?> Was ist mit geschützten (private) Datamembern?
Ich denke das ich mich dann direkt darum kümmern muss.
Es geht mir hier Hauptsächlich um Kapselung und den Konstruktor.
Aber auf einem AVR sind bei mir 99% meiner Destruktoren leer.
Der ganze Käse muss Initialisert werden aber ein loslassen von Resourcen
gibt es da nicht.
...und wenn ich in C++17 keine Instanzen mehr Kopieren darf,
dann will ich das nicht haben.
Wofür legt man denn einen CopyConstructor an ?
Dort beschreibe ich wie ein Objekt von einem Speicherbereich in den
anderen kopiert wird.
Das macht am ende immer eine Speicherroutine wie memcpy oder z.b.
eeprom_read_block, ausserdem kann ich dann noch die ID hochzählen.
Oder stimmt das nicht ?
In den grossen Libs wird das doch auch ständig gemacht !?
Dem C++ ist es doch egal wo die Daten in der Maschine gespeichert sind.
Ich wette man darf ein Objekt (dessen Daten) auch in eine Datei
speichern ?
Ich will nicht als Beratungsressistent erscheinen, will es nur
verstehen.
> Es bleibt "Versuch und Fehler" solange du Versuchst herauszufinden was> der Compiler macht. Du musst herausfinden was der Standard sagt.
Bis jetzt ist es Forschung, wenn ich es im Projekt einsetzte dann ist
es... ;)
Aber zeig mir mal die Stelle im C++ Standard die das kopieren eines
Objekt aus einem anderen Speicher(bereich) verbietet.
Ich bin doch derjenige der den CopyConstructor schreibt bzw. absichtlich
=0 setzt ?!?
Man will ja typischerweise auch nicht bei jedem(!) Zugriff der Firmware,
dass alles ins EEProm zurückgeschrieben oder neu gelesen wird. Also habe
ich dafür ein Template, was parametriert wird mit dem DT, dessen Daten
ins EEProm persistiert werden sollen, mit dem konkreten µC, etc. Dieses
Template agiert im wesentlichen als Cache, und schreibt auch die Daten
wenn nötig in definierten, zeitlichen Abständen ins EEProm zurück. Damit
bin ich alle Sorgen los.
Die Initialisierung des EEProms mache ich entweder durch einmaliges
Starten der Firmware in einem besonderen Modus (könnte ja sein, das an
der Peripherie auch mal einmalig was gemacht werden muss), oder ich
"verlasse" mich darauf, dass im Auslieferungszustand der µC 0xff drin
steht, und schreibe anschließend eine magic number bspw. an den Anfang
des EEproms. Dies ist auch ein generation-stamp, damit man
unterschiedliche Versionen verwalten kann ...
Das ich kein .eep bekomme, damit kann ich mich abfinden.
Vielleicht hat noch jemand einen Grund der gegen das Kopieren in / aus
dem EEMEM spricht.
Ich sehe gerade keinen.
@ Wilhelm M. :
Wie sähe das denn aus ?
Mein naiver Ansatz war Snapshots mit einer ID über das ganze eeprom zu
verteilen.
Nach jedem Copy wird die ID erhöht, so finde ich den letzten Datensatz.
Geschrieben wird nur auf Anfrage von aussen oder bei Abfall der
Spannung.
Ich wollte nicht jeden einzelnen Wert aktualisieren.
Eigentlich soll es im EEMEM zwei Datenbereiche geben.
(Parametrierung und Telemetrie)
PS zu mh :
Es gibt in C++ auch verschiedene Möglichkeiten das Private aufzuweichen.
Mit fallen da protected, friend und static ein.
(und static ist ne ganz üble Nummer)
Thomas W. schrieb:> PS zu mh :> Es gibt in C++ auch verschiedene Möglichkeiten das Private aufzuweichen.> Mit fallen da protected, friend und static ein.
Wozu sollte das nötig sein?
> (und static ist ne ganz üble Nummer)
Was hat static mit Sichtbarkeit zu tun?
Thomas W. schrieb:> Johann L. schrieb:>> Thomas W. schrieb:>>>> Aber warum sollte das nicht funktionieren ?>>>> Ei weil EEMEM nur die Ablage des Codes regelt, aber nicht der Zugriff.>> Wenn du zum Beispiel Code bastelst — wie oben geschehen — der die>> Struktur zur Laufzeit initialisiert (ausgeführt in einem impliziten,>> statischen Konstruktor — oder wie auch immer C++ sowas nennt, wenn es>> denn überhaupt einen Namen dafür hat) dann schreibt dieser Code ins RAM>> anstatt den EEprom zu initialisieren.>> Ich vermute da habe ich Dir mein Problem nicht richtig erklärt :>> 1. Eine Instanz wird im RAM angelegt :>
1
>DATABLOCKdataRAM;
2
>
>> 2. Eine Instanz wird im EEMEM angelegt, natürlich lässt sich die nicht> "benutzen"...>
1
>DATABLOCKEEMEMdataEEP;
2
>
Es wird also ein Constructor "ausgeführt".
Wann wird dieser "ausgeführt"? D.h. wie sorgt die abstrakte Maschine
dafür, dass dataEEP beim Betreten von main mit den geforderten
Initalwerten belegt ist? Wir wissen es nicht. Es kann zur Load-Time
sein oder zur Laufzeit. Ich bin wahrlich kein C++ Experte, aber ich
kann mir schwerlich vorstellen, dass C++ diese Konzepte überhaupt kennt
oder gar eine Aussage bzw. Zusicherung darüber macht. Selbst wenn alle
Komponenten physisch mit 0 initialisiert werden darf eine Implementation
das zur Laufzeit initialisieren — ich lass mich aber gerne eines
besseren belehren.
> 3. Jetzt muss ich doch den Inhalt von dataRAM <> dataEEP in beide> Richtungen kopieren können.
Nur falls std::is_trivially_copyable erfüllt ist.
> Das muss doch im Sprachstandard von C++ erlaubt sein?
Es geht hier doch nicht nur um C++ sondern auch um attribute "section"
(hier konkret EEMEM) und dass dieses Attribut den Code in einen Bereich
mit Eigenschaften legt, die sich nicht in C++ formulieren lasse.
> Die Sprache kann mir doch nicht den direkten Zugriff auf Speicherobjekte> "verbieten"?
EEMEM gehört nicht zu C++ :-)
> Die ganzen LIBs wie Boost etc. müssen unter der Haube doch auch solche> Konstrukte verwenden.
Nein, Boost unterstützt bestimmt nicht sowas wie attribute((section))
und bastelt dir entsprechende Zugriffe oder garantiert dir, dass
Speicher ausschließlich zur Load-Time angefasst wird, etc.
> ...und das funktioniert in meinem Test auch.
Wie gesagt: Das ist keine Garantie dafür, dass es korrekt ist. Ob
das funktioniert kann z.B: vom Optimierungen abhängen: Wenn der Compiler
sieht, dass er ertwas nicht in einem statischen Constructor abhandeln
muss, dann dar er dass zur Load-Time machen. Aber er MUSS NICHT.
Noch mal: "Ich hab XXX ausprobiert und es ging" ist KEIN Argument :-)
> Copy Constructor schöner anzusehen
Vergiss (schönes) C++. Bei Sachen wie EEMEM lässt es dich im Regen
stehen.
> Das einzige Problem was ich jetzt habe ist das bei einem blanken Struct> eine korrekte .eep für die Vorinitialisierung des EEMEM erzeugt wird.> Ich denke nicht dass das .eep verhalten im C++ Standard festgehalten ist> ?
Korrekt.
Thomas W. schrieb:> Johann L. schrieb:>> Nimm ein POD.>>>> Mit einer Klasse (die mehr ist als POD) kann sowas nicht funktionieren:>>>>
>>>> Und POD ist nur eine notwendige Bedingung, hinreichend ist POD nicht.>> Ahhh, damit gibst Du mir das Werkzeug zum Testen in die Hand.
Nein. Das wäre der Fall, wenn ich "hinreichend" geschrieben hätte.
https://de.wikipedia.org/wiki/Notwendige_und_hinreichende_Bedingung
Zudem ist is_pod ab C++20 deprecated. Zwar kann man es ersetzen durch
is_trivial && is_standard_layout, aber das genügt eben nicht.
Ich finde auch keine Trait, die wirklich passen würde und strikt genug
wäre. Etwa könnte ein Struct eine nicht-statische Methode haben und
würde immer noch POD sein. Und auch wenn is_trivially_copyable erfüllt
ist, funktionieren Zuweisungen mit = eben NICHT. Und wenn es ein
Initializer ist, steht der C++ Implementation frei, diesen zu Laufzeit
abzuhandeln [*]. Was hier nicht nur dazu führt, dass der struct nicht
initialisiert wird, sondern auch dazu, dass in den RAM / SFR-Bereich
Schrott geschrieben wird :-)
Wenn also ein Datum im Static Storage physisch mit 0 initialisiert wird,
ist dann die Implementation dazu verpflichtet, diese zur Load-Time
abzuhandeln? Ich denke nicht [*], d.h. selbst wenn dein .eep wie
gewünscht erzeugt wird, ist dein Programm Schrott :-)
[*] Mit C++ kenne mich aber wie gesagt nicht genug aus und lasse mich da
gerne eines Besseren belehren.
Thomas W. schrieb:> ich will die Instanz nicht im EEPROM verwenden, nur speichern.
Warum willst du denn die Instanz selber speichern? Reicht es nicht nur
die Werte der Attribute zu speichern?
Wie waere es mit serialisierung?
https://isocpp.org/wiki/faq/serialization
1
What’s this “serialization” thing all about?
2
3
It lets you take an object or group of objects, put them on a disk or
4
send them through a wire or wireless transport mechanism, then later,
5
perhaps on another computer, reverse the process: resurrect the original
6
object(s). The basic mechanisms are to flatten object(s) into a
7
one-dimensional stream of bits, and to turn that stream of bits back
8
into the original object(s).
9
10
Like the Transporter on Star Trek, it’s all about taking something
11
complicated and turning it into a flat sequence of 1s and 0s, then
12
taking that sequence of 1s and 0s (possibly at another place, possibly
13
at another time) and reconstructing the original complicated “something.”
Wenn man das so macht wie ich oben beschrieben habe, fällt auch die POD
Forderung weg.
Entweder gibt man seinem EEProm-Typ einen Typumwandlungs-Ctor von etwa
std::array<std::byte, sizeof(DT)>: dieses Array muss natürlich schon im
RAM liegen, d.h. man muss es vorher durch plattformspezifische
Funktionen wie etwa eeprom_read_block(...) füllen, oder man benutzt ein
placement-new, oder, da man in C++ ja über uchar* <-> type* ein Objekt
"füllen" kann ohne UB zu bekommen, schreibt man die Repräsentation des
Objektes irgendwo ins RAM und castet (Vorsicht, dass man keine
trap-representation dabei generiert).
Thomas W. schrieb:> Aber auf einem AVR sind bei mir 99% meiner Destruktoren leer.
Warum schreibst du dann den Destroktor?
Thomas W. schrieb:> ...und wenn ich in C++17 keine Instanzen mehr Kopieren darf,> dann will ich das nicht haben.>> Wofür legt man denn einen CopyConstructor an ?> Dort beschreibe ich wie ein Objekt von einem Speicherbereich in den> anderen kopiert wird.
Ja genau dafür ist ein CopyConstructor da, also schreibe ihn.
Thomas W. schrieb:> In den grossen Libs wird das doch auch ständig gemacht !?
Hast du ein Beispiel wo das genau so gemacht wird?
Thomas W. schrieb:> Bis jetzt ist es Forschung, wenn ich es im Projekt einsetzte dann ist> es... ;)>> Aber zeig mir mal die Stelle im C++ Standard die das kopieren eines> Objekt aus einem anderen Speicher(bereich) verbietet.
Wenn es deine Forschung ist, solltest auch du im Standard nachlesen, ob
es erlaubt ist.
> Ich bin doch derjenige der den CopyConstructor schreibt bzw. absichtlich> =0 setzt ?!?
Du hast (in deinem Beispiel) keinen CopyConstructor geschrieben und auch
nicht =0 gesetzt (was auch immer das bedeuten mag)!?
Thomas W. schrieb:> PS zu mh :> Es gibt in C++ auch verschiedene Möglichkeiten das Private aufzuweichen.> Mit fallen da protected, friend und static ein.> (und static ist ne ganz üble Nummer)
Kannst du das etwas genauer erklären?
Nicht weil du so nett gefragt hast, sonder weil es mich selbst
interessiert, habe ich 5 Minuten investiert, um im Standard (C++17 Draft
N4687) zu lesen.
1
6.8 Object lifetime [basic.life]
2
...
3
5) A program may end the lifetime of any object by reusing the storage which the object occupies or by explicitly calling the destructor for an object of a class type with a non-trivial destructor. For an object of a class type with a non-trivial destructor, the program is not required to call the destructor explicitly before the storage which the object occupies is reused or released; however, if there is no explicit call to the destructor or if a delete-expression (8.3.5) is not used to release the storage, the destructor shall not be implicitly called and any program that depends on the side effects produced by the destructor has undefined behavior.
Auf den ersten Blick ist es also erlaubt, das ganze Objekt zu kopieren
(mit der expliziten Erlaubnis sich in den Fuss zu schießen). Andere
Stellen des Standards sind sicherlich auch relevant für die Frage, aber
ich keinen Grund gefunden der gegen das Kopieren spricht.
Thomas W. schrieb:>> Bitte nicht falsch verstehen,> ich will die Instanz nicht im EEPROM verwenden, nur speichern.> Welche Regel sollte mir das verbieten ?
Bitte richtig verstehen, ich sage (mit meinen Worten und eventuell
vereinfacht) genau das gleiche, was Johan und Wilhelm auch sagen. C++
kennt die AVR-Hardware-Besonderheiten nicht und wird sie auch nicht
kennenlernen, da "modernere" HW versucht, alle Datenarten in einen
Adressraum zu mappen.
Mach einen "cachenden Proxy", der mit der EEProm-Adresse deiner Struktur
initialisiert. Oder falls die Adresse constexpr ist, was eine
EEProm-Adresse eventuell nicht ist, wegen Link-Step, benutze ein
Template, dann steckt die Adresse im Typ und braucht keinen
Speicherplatz im RAM. Notfalls macht man das EEProm Layout von Hand,
dann ist die Strukturadresse constexpr ;-)
Ich verstehe dein Problem sehr gut, weil ich genau an der selben Stelle
nach einer eleganten Lösung suche. Allerdings ohne Termindruck, also
niedrige Prio.
Carl D. schrieb:> Thomas W. schrieb:>>>> Bitte nicht falsch verstehen,>> ich will die Instanz nicht im EEPROM verwenden, nur speichern.>> Welche Regel sollte mir das verbieten ?>> Bitte richtig verstehen, ich sage (mit meinen Worten und eventuell> vereinfacht) genau das gleiche, was Johan und Wilhelm auch sagen. C++> kennt die AVR-Hardware-Besonderheiten nicht und wird sie auch nicht> kennenlernen, da "modernere" HW versucht, alle Datenarten in einen> Adressraum zu mappen.
Richtig. Wobei das nichts mit moderner HW zu tun hat, sondern damit,
dass C++ HW-agnostisch ist. So etwas kann also nicht Bestandteil des
Sprachkerns sein.
Mein Ansatz ist wie gesagt recht simpel: ich modelliere das (gesamte)
EEProm als einen heterogenen Container: entweder als eine
Aggregat-Klasse mit unterschiedlichen Elementen für unterschiedliche
Aufgaben der Firmware, oder gleich als std::tuple<>. Dann gibt es ein
generisches Cache-template, was mit diesem Typ parametriert wird. Das
kümmert sich dann um die Serialisierung: und dies ist dann
plattform-spezifisch (bei avr kann man dann eeprom-update-block(), ...
benutzen).
Entschuldigt meine Späte Antwort ich hatte viel zu lesen.
Ich vermute ich weiss jetzt wer der Johann ist... 8)
>Aus dem Sprachstandard kann man auf ein Progamm schließen, aber aus>einem in einer konkreten Situation funktionierenden Binärcode lässt sich>NICHT schließen, dass man gültige Konstrukte benutzt hat.>>Ich kenne keinen anderen Denkfehler, der in der Programmierung auch nur>ansatzweise so häufig gemacht wird wie dieser nicht-gültige>Umkehrschluss.
Das muss(te) ich mir erst verinnerlichen, da ich, das bisschen, was ich
über C++ weiss nicht aus dem Standard gelernt habe ging ich da auch
blauäugig ran.
Ist schon interessant wie sich (m)ein Weltbild verändern kann.
Dank euch verstehe ich jetzt ein bisschen mehr, danke!
> Die ganzen LIBs wie Boost etc. müssen unter der Haube doch auch solche> Konstrukte verwenden.>Nein, Boost unterstützt bestimmt nicht sowas wie attribute((section))>und bastelt dir entsprechende Zugriffe oder garantiert dir, dass>Speicher ausschließlich zur Load-Time angefasst wird, etc.
Ich habe den Source durchgeackert. (grep etc) Ich konnte bei Boost jede
Menge memcpy auf POD Daten finden aber nicht einmal auf eine Klasse !
>[*] Mit C++ kenne mich aber wie gesagt nicht genug aus und lasse mich da>gerne eines Besseren belehren.
Das glaube ich Dir nicht;)
mh
>Nein! Es ist UB, weil es eine Endlosschleife ohne Seiteneffekte ist.
Also wenn ich das richtig verstehe würde es schon reichen wenn die
(leere-)Schleife Terminieren würde.
Zb vergleich gegen eine Volatile Variable.
Oder im Rumpf ein Effekt nach aussen auftritt.
> In den grossen Libs wird das doch auch ständig gemacht !?>Hast du ein Beispiel wo das genau so gemacht wird?
Hab nachgeschaut, Boost nutzt es nur für POD.
Es ist schade das es keine libC++ gibt.
(Ja, ich weiss, selber schreiben, aber ich habe nicht die Fähigkeiten
dafür...)
>> Ich bin doch derjenige der den CopyConstructor schreibt bzw. absichtlich>> =0 setzt ?!?>Du hast (in deinem Beispiel) keinen CopyConstructor geschrieben und auch>nicht =0 gesetzt (was auch immer das bedeuten mag)!?
Naja, wenn man ihn pure virtual macht (=0) kann man ihn nicht nutzen.
Der Compiler müsste abbrechen.
>> PS zu mh :>> Es gibt in C++ auch verschiedene Möglichkeiten das Private aufzuweichen.>> Mit fallen da protected, friend und static ein.>> (und static ist ne ganz üble Nummer)>Kannst du das etwas genauer erklären?
In einer Klassendefinition führt static dazu das die Variable zwischen
allen Instanzen der Klasse geteilt wird.
Inkrementiert man im CTor und dekrementiert im DTor, könnte man
mitzählen wieviele Instanzen der klasse existieren.
Ich behaupte wenn eine static Variable im private Teil steht weicht dass
das Private auf.
Den eine andere Instanz bekommt Zugriff auf das Datum.
Friend erlaubt einer anderen Methode den Zugriff auf die Variablen einer
Instanz als würde sie in der Klasse stehen.
Also ist wieder das privat unterwandert.
Ich nutze das um aus einer ISR an die Variablen einer Klasseninstanz zu
kommen.
Protected ist vielleicht nicht das beste Beispiel.
>Nicht weil du so nett gefragt hast, sonder weil es mich selbst>interessiert, habe ich 5 Minuten investiert, um im Standard (C++17 Draft>N4687) zu lesen.
Entschuldige!!!
>> Aber auf einem AVR sind bei mir 99% meiner Destruktoren leer.>Warum schreibst du dann den Destroktor?
Hmmm, der DTor muss sein wenn man virtual Methoden nutzt. Jedenfalls war
das früher so.
Damit kann man sehr schöne Konstrukte bauen.
Ich leite von einer Basisklasse, welche eine einfach verkettete Liste
bildet, Klassen ab.
Deren Instanzen kann ich nun in die Liste einhängen und ein Ereignis
auslösen. Jetzt rufen sich alle Überschriebenen Methoden der Reihe nach
auf.
Carl,
So habe ich es jetzt auch gemacht.
Zwei arrays vom Typ Struct, vorinitialisiert, erzeugen das .eep.
Eine Klasse kümmert sich um das richtige lesen und Speichern.
Der Getter reicht die Daten als "const" durch.
Die Handvoll Setter manipulieren und lösen das schreiben aus.
Leider keine super hübsche Variante aber sicher, schnell, einfach.
Wilhelm,
ich wollte nicht nur config Daten sondern auch "Telemetriedaten"
erfassen.
Die sollten schon im Block geschrieben werden.
Meine Absicht ist über ein array von structs zu rotieren, sonst könnte
ich zu viele Schreibzugriffe bekommen. (spätestens bei der Telemetrie)
Wobei Dein Ansatz einzelne Variablen zu sichern natürlich Super ist.
Um das "schön" umzusetzen fehlt mir im Moment aber die Kenntnis.
Ich müsste meinen Horizont halt mal erweitern.
Das geht im Moment aber nicht so gut...
Ich sehe gerade schlimmere Lücken als Templates. ;)
>> Es gibt in C++ auch verschiedene Möglichkeiten das Private aufzuweichen.>> Mit fallen da protected, friend und static ein.>>Wozu sollte das nötig sein?>>> (und static ist ne ganz üble Nummer)>>Was hat static mit Sichtbarkeit zu tun?
In einer Klassendefinition führt static dazu das die Variable zwischen
allen Instanzen der Klasse geteilt wird.
Inkrementiert man im CTor und dekrementiert im DTor, könnte man
mitzählen wieviele Instanzen der klasse existieren.
Ich behaupte wenn eine static Variable im private Teil steht weicht dass
das Private auf.
Den eine andere Instanz bekommt Zugriff auf das Datum.
>Entweder gibt man seinem EEProm-Typ einen Typumwandlungs-Ctor von etwa>std::array<std::byte, sizeof(DT)>: dieses Array muss natürlich schon im>RAM liegen, d.h. man muss es vorher durch plattformspezifische>Funktionen wie etwa eeprom_read_block(...) füllen, oder man benutzt ein
<placement-new, oder, da man in C++ ja über uchar* <-> type* ein Objekt
>"füllen" kann ohne UB zu bekommen, schreibt man die Repräsentation des>Objektes irgendwo ins RAM und castet (Vorsicht, dass man keine>trap-representation dabei generiert).
Uiuiui, in einem Jahr... Liegt nicht im Bereich meiner Fähigkeiten.
Kaj,
ja da ist so eine Ding. Serialisierung kenne ich von Funkstrecken ;)
Aber leider wird mir das zu Kompliziert um es in kurzer Zeit zu
implementieren.
Ein wichtiger "Merksatz" für mich wäre :
* Instanzen von Klassen sind nur im Speicher Speicherobjekte,
* für den Programmierer sind sie Black Boxen mit Seiteneffekten.
Thomas W. schrieb:> Entschuldigt meine Späte Antwort ich hatte viel zu lesen.
Fürs Weiterbilden muss man sich nicht entschuldigen. Vor allem wenn
dabei Weltbilder verändert werden.
Thomas W. schrieb:> Also wenn ich das richtig verstehe würde es schon reichen wenn die> (leere-)Schleife Terminieren würde.> Zb vergleich gegen eine Volatile Variable.> Oder im Rumpf ein Effekt nach aussen auftritt.
Ja, es reicht, wenn es in der Schleife (Rumpf oder Bedingung) einen
Seiteneffeckt gibt, oder wenn es keine Endlosschleife ist.
Thomas W. schrieb:> Naja, wenn man ihn pure virtual macht (=0) kann man ihn nicht nutzen.> Der Compiler müsste abbrechen.
Dafür müsste der Construktor allerdings virtual sein, was nicht erlaubt
ist. Du solltest dir =delete anschauen.
http://en.cppreference.com/w/cpp/language/copy_constructorThomas W. schrieb:> Hmmm, der DTor muss sein wenn man virtual Methoden nutzt. Jedenfalls war> das früher so.
Das ist nicht ganz richtig. Der Destruktor muss virtual sein, wenn man
ein Objekt "polymorph" zerstört, also über einen Pointer oder eine
Referenz, wenn dynamischer und statischer Typ nicht übereinstimmen. Auf
ein "sollte virtual sein" gehe ich jetzt nicht ein.