Hallo,
ich frage mich derzeit ob es möglich ist und wenn ja wie, dass man
einzelne Objekte in einem Array zusammenfassen und automatisch
gegenseitig aktuell halten kann.
Im Programm ist es für mich günstiger ich arbeite direkt mit einzelnen
Objektnamen. Zum speichern und lesen hätte ich jedoch gern alles in
einem Array zusammengefasst, weil das einfacher ist und funktioniert.
Aktuell fällt mir dazu nur ein 2 Funktionen zu schreiben. Daten von den
Einzelobjekten in das Array kopieren und dann Array speichern. Bzw.
Array auslesen, Array Daten in die Einzelobjekte kopieren und damit
arbeiten.
Frage wäre eben ob man die Einzelobjekte irgendwie als Referenz bei der
Array Initialisierung mitgeben kann. Vielleicht kennt jemand eine
Möglichkeit.
Testcode, damit man sieht das sumObj nur Kopien erstellt.
Willst Du wirklich Referenzen (hier: Zeiger oder C++-Referenzen) in
einem Array halten, oder möchtest Du lediglich eine Möglichkeit, über
eine variablen Anzahl von Objekten eines beliebigen Typs iterieren?
Hallo,
@ Alex: das funktioniert leider nicht so einfach
@ Wilhelm: ich gehe davon aus das ich C++ Referenzen benötige
Bei der weiteren Lösungssuche bin ich auf diese Seite gestoßen
https://www.nextptr.com/question/qa1444367653/array-of-reference_wrapper-an-alternate-array-of-references
Demnach wird ein Konstruktor benötigt. Damit erstmal sachte angefangen
und probiert. Das funktioniert erstmal wie gewünscht. Variable und Array
sind jeweils automatisch aktuell wenn man eins von beiden ändert.
Ist das der Weg den ich für meine Struktur usw. weiter verfolgen kann?
Wilhelm M. schrieb:> Willst Du wirklich Referenzen (hier: Zeiger oder C++-Referenzen) in> einem Array halten, oder möchtest Du lediglich eine Möglichkeit, über> eine variablen Anzahl von Objekten eines beliebigen Typs iterieren?
Nochmal über die Frage nachgedacht. Das Array benötige ich zum speichern
und auslesen. Die Daten im Array werden in einem FRAM gespeichert. Das
Array übergebe ich Funktionen die sich dann mit dem FRAM bzw. der FRAM
Klasse usw. beschäftigen. Das mit dem FRAM speichern/lesen funktioniert
alles. Die Array Übergabe vereinfacht alles.
Über das Array iterieren tue ich vorm speichern nach dem lesen nicht. Da
möchte ich mit den einzelnen Objekten arbeiten ohne über einen Index
nachdenken zu müssen.
Beantwortet das deine Frage?
Hallo,
jetzt tue ich mich schwer, vorausgesetzt der Weg ist überhaupt richtig.
Die Übergabe von Objekten an ein Array funktioniert jedoch nicht mehr.
Warum? Ist doch gleich der Übergabe der Variablen m, n, b. Nur eben als
struct Objekte.
1
could not convert 'obj0' from 'Daten' to 'DatenRef'|
Alexander schrieb:> Daten* sumObj[] = {&obj0, &obj1};Veit D. schrieb:> @ Alex: das funktioniert leider nicht so einfach
Dasselbe wie Alexander hätte ich auch vorgeschlagen, allerdings etwas
C++isher mit einem std::array statt dem C-Array und mit Referenzen statt
Zeigern.
Statt eines Arrays von Referenzen könnte man auch ein Array von
Strukturen nehmen (wie in deinem ersten Beispiel) und dafür die
Einzelvariablen zu Referenzen machen. Das hätte den Vorteil, dass die
Strukturen zusammenhängend im Speicher liegen, was evtl. das Kopieren
vom und ins FRAM etwas vereinfacht.
Um dein Problem besser zu verstehen, hilft es vielleicht, wenn du
schreibst, warum der Vorschlag von Alexander für dich nicht
funktioniert.
Veit D. schrieb:> jetzt tue ich mich schwer, vorausgesetzt der Weg ist überhaupt richtig.> Die Übergabe von Objekten an ein Array funktioniert jedoch nicht mehr.
Echt. Na, dann fehlen doch ein paar Grundlagen oder es ist schon zu
spät.
> Warum? Ist doch gleich der Übergabe der Variablen m, n, b. Nur eben als> struct Objekte.
Nein.
Jetzt bräuchtest Du
1
structDatenRef{
2
DatenRef(Daten&d):v{d}{}
3
Daten&v;
4
};
Vorher hattest Du einen dreistelligen ctor mit uint16_t, jetzt brauchst
Du einen einstelligen mit Daten.
Veit D. schrieb:> Wilhelm M. schrieb:>> Willst Du wirklich Referenzen (hier: Zeiger oder C++-Referenzen) in>> einem Array halten, oder möchtest Du lediglich eine Möglichkeit, über>> eine variablen Anzahl von Objekten eines beliebigen Typs iterieren?>> Nochmal über die Frage nachgedacht. Das Array benötige ich zum speichern> und auslesen. Die Daten im Array werden in einem FRAM gespeichert. Das> Array übergebe ich Funktionen die sich dann mit dem FRAM bzw. der FRAM> Klasse usw. beschäftigen. Das mit dem FRAM speichern/lesen funktioniert> alles. Die Array Übergabe vereinfacht alles.
Das hört sich so an, als würdest Du eine Klasse benötigen, die den
Zugriff auf das FRAM kapselt. Was für DT sollen im FRAM gespeichert
werden? Homogen ein DT? Dann kannst Du dieser Klasse einen operator[]
geben, der die Objekte aus dem FRAM liefert. GGf. haben die DT der
Objekte im FRAM einen geeigneten ctor, und Deine FRAM-Klasse konstruiert
die Objekte aus ein paar Bytes aus dem FRAM.
Dein Weg mit (abstrakten) Referenzen in einem Array könnte man
verfolgen, wenn diese Referenzen keine C++-Referenzen oder C++-Zeiger
sind, sondern z.B. einfach Indizes für Objekte im FRAM. Die Klasse zur
Abstraktion des FRAM weiß dann damit umzugehen und popelt die Objekte
aus dem FRAM.
BTW: C++-Referenzen sind sog. non-nullable, non-reseatable, no-object
Referenzen. Sie müssen also immer auf ein und dasselbe Objekt verweisen
mit dem sie initialisiert worden sind. Diese (gute) Einschränkung wird
Dein Vorhaben aber wahrscheinlich unsinnig machen.
Reference-Wrapper beseitigen je nach Implementierung das "no-object" und
/ oder "non-reseatable" aus der obigen Liste. Das "reseatable" geht
nur, wenn sie intern mit Zeiger arbeiten.
In der stdlibc++ gibt es
https://en.cppreference.com/w/cpp/utility/functional/reference_wrapper
als template. Dann brauchst Du Deine DatenRef-Klasse nicht.
Das ist einfach eine Wrapper-Klasse um Zeiger, die sich dann so
"anfühlen" wie direkte Referenzen, d.h. im Gegensatz zu Zeigern entfällt
beim Zugriff der Indirektionsoperator "*".
Yalu X. schrieb:> Dasselbe wie Alexander hätte ich auch vorgeschlagen, allerdings etwas> C++isher mit einem std::array statt dem C-Array und mit Referenzen statt> Zeigern.
Da C++-Referenzen keine Objekte sind, lassen sie sich nicht in einem
Array speichern.
Wilhelm M. schrieb:> Yalu X. schrieb:>> Dasselbe wie Alexander hätte ich auch vorgeschlagen, allerdings etwas>> C++isher mit einem std::array statt dem C-Array und mit Referenzen statt>> Zeigern.>> Da C++-Referenzen keine Objekte sind, lassen sie sich nicht in einem> Array speichern.
Stimmt, das war mir nicht bewusst. Dann eben doch ein Array von Zeigern
oder ein Array von std::reference_wrapper<Daten>. Das ist dann halt der
Zugriff auf die Array-Elemente etwas unelegant, weil man immer noch
einen * bzw. ein get() für die Dereferenzierung braucht.
Andersherum sollte es aber auch mit ungewrappten Referenzen gehen:
Yalu X. schrieb:> Statt eines Arrays von Referenzen könnte man auch ein Array von> Strukturen nehmen (wie in deinem ersten Beispiel) und dafür die> Einzelvariablen zu Referenzen machen.
Veit D. schrieb:> Meine FRAM Klasse kann alles speichern und lesen was ich ihr übergebe.
Dann übergebe ihr doch einfach die Objekte, fertig. Stichwort:
variadische Funktion(template)
Veit D. schrieb:> Meine FRAM Klasse kann alles speichern und lesen was ich ihr übergebe.> Die Übergabe eines Arrays hat wie gesagt den Vorteil das damit alles> bequem am Stück geschrieben und gelesen werden kann.
Dann also evtl. so:
Yalu X. schrieb:> Statt eines Arrays von Referenzen könnte man auch ein Array von> Strukturen nehmen (wie in deinem ersten Beispiel) und dafür die> Einzelvariablen zu Referenzen machen.
Yalu X. schrieb:> Dann also evtl. so:
Das ist jetzt von hinten durch die Brust ins Auge.
Wenn ich das richtig verstanden habe, fühlst dich zu einem Array
gezwungen, weil du mehrere Objekte Dr fram Klasse übergeben willst?
Dann mache eine variadische Elementfunktion.
Oder eine Std:: Array mit Zeigern, wobei dies nullable Referenzen sind,
was ggf.nicht willst.
Wilhelm M. schrieb:> Yalu X. schrieb:>> Dann also evtl. so:>> Das ist jetzt von hinten durch die Brust ins Auge.>> Wenn ich das richtig verstanden habe, fühlst dich zu einem Array> gezwungen, weil du mehrere Objekte Dr fram Klasse übergeben willst?
Der Wunsch nach einem Array kommt vom TE. Ich habe versucht, seinen Code
mit relativ wenig Änderungen in etwas Funktionierendes überzuführen.
> Dann mache eine variadische Elementfunktion.
Wir wissen nicht wie viele Instanzen von Daten in der realen Anwendung
benötigt werden. Die Argumentliste der variadischen Funktion könnte u.U.
sehr lang werden. Schon ab etwa 4 Argumenten würde ich das als ziemlich
hässlich empfinden.
> Oder eine Std:: Array mit Zeigern,
Das wurde ja bereits in der ersten Antwort vorgeschlagen, aber der TE
hat das - leider ohne Nennung von Gründen – abgelehnt. Wahrscheinlich
hängt es damit zusammen, dass die Daten
Veit D. schrieb:> bequem am Stück geschrieben und gelesen werden
können sollen.
In mir keimt der Verdacht, dass das Ganze wieder einmal ein XY-Problem
ist.
Yalu X. schrieb:> Der Wunsch nach einem Array kommt vom TE.
Ja, natürlich. Und an ihn war auch meine Antwort eigentlich gerichtet.
Aber ob jetzt für die Übergabe an fram eine Array erzeugt wird oder die
Objekte direkt in der argumentliste stehen, dürfte egal sein.
Yalu X. schrieb:>> Oder eine Std:: Array mit Zeigern,>> Das wurde ja bereits in der ersten Antwort vorgeschlagen, aber der TE> hat das - leider ohne Nennung von Gründen – abgelehnt. Wahrscheinlich> hängt es damit zusammen, dass die Daten>> Veit D. schrieb:>> bequem am Stück geschrieben und gelesen werden>> können sollen.>> In mir keimt der Verdacht, dass das Ganze wieder einmal ein XY-Problem> ist.
Was soll das werden? Man hat nichts gewonnen wenn man sich danach mit
der Dereferenzierung rumschlagen muss. Das wird in Summe nur anders und
noch Fehler anfälliger.
Hallo,
das geht in die richtige Richtung was ich haben möchte. Die Handhabung
der Benutzung soll einfach sein. Jetzt bekomme ich noch eine
Fehlermeldung das DatenRef keinen Member "left" hat. Wie greift man
darauf zu?
Wegen der Frage wieviel Objekte das werden. bzw. variadischer Funktion.
Mindestens 8 können auch bis 16 werden.
Hallo,
jetzt habe ich das Beispiel von Yalu getestet und siehe da es
funktioniert. :-) Es ist alles automatisch syncron. Ganz stark.
Der Trick besteht demnach im umdrehen der Initialisierungen.
Erst das array und dann die Einzelobjekte.
Und siehe da man benötigt keine Dereferenzierungen und den ganzen Kram.
Vielen Dank dafür!
Hallo,
die Übergabe von 'sumObj' funktioniert auch wie erwartet weiterhin mit
meinem FRAM. Das ist sehr schön.
Vielleicht noch eine Erklärung warum ich auf ein Array bestanden habe.
Variadische Funktion (Template) und damit alle Objekte einzeln übergeben
wäre zwar auch möglich, aber das erzeugt wiederum mehr Overhead auf dem
I2C Bus. Wenn ich ein Array schreiben oder lesen lassen kann, habe ich
nur einmalig Zugriffsvorbreitungen auf das FRAM und danach werden egal
wieviele Bytes hintereinander geschrieben oder gelesen. Das ist sehr
effektiv.
Veit D. schrieb:> Jetzt bekomme ich noch eine> Fehlermeldung das DatenRef keinen Member "left" hat. Wie greift man> darauf zu?Veit D. schrieb:> cout << sumObjRef[0].left << endl;
Auch hier ist doch auch die Fehlermeldung eindeutig: der Elementtyp des
Arrays sumObjRef ist DatenRef. Der Type hat kein Element left, sondern
ein Element v des Typs Daten. Und erst Daten hat das Element v.
Es muss also heißen:
1
sumObjRef[0].v.left
Würdest Du std::reference_wrapper<> einsetzen, wäre diese zusätzliche
Indirektion nicht notwendig.
Veit D. schrieb:> Erst das array und dann die Einzelobjekte.
Vorsicht: keine Einzelobjekte, sondern Referenzen (Namen) dafür. Mir
scheint, Du hast das Konzept der C++-Referenzen nicht verstanden.
Warum willst Du denn dann, wenn Du das Array hast, noch die einzelnen
Referenzen noch haben? Warum nimmst Du nicht "sprechende" Indexnamen
stattdessen, etwa als enum?
Veit D. schrieb:> Hallo,>> die Übergabe von 'sumObj' funktioniert auch wie erwartet weiterhin mit> meinem FRAM. Das ist sehr schön.>> Vielleicht noch eine Erklärung warum ich auf ein Array bestanden habe.> Variadische Funktion (Template) und damit alle Objekte einzeln übergeben> wäre zwar auch möglich, aber das erzeugt wiederum mehr Overhead auf dem> I2C Bus.
Nun, das ist kein Widerspruch: das Array könnte lokal in FRAM erzeugt
werden. Aber so würde ich es auch nicht machen.
Sondern: falls Du Dich mit Faltungsausdrücken auskennst, dann brauchst
Du auch intern in FRAM kein Array. Ähnlich einer Iteration über die
Bytes eines Arrays, kannst Du mit einer Faltung über die Elemente eines
Parameterpacks iterieren. Damit hast Du dieselbe Effektivität eines
contiguous-write auf dem I2C.
Weiterhin hast Du beim Array die Einschränkung, dass das ein homogener
Container ist, bei der variadischen Funktion nicht.
Im übrigen könntest Du alle Deine Objekte in eine Klasse packen, dass
ist dann ggf. so ähnlich wie ein heterogener Container.
1
structFRamData{
2
DataAd0;
3
DataBd1;
4
DataAd2;
5
...
6
};
und dann
1
FRamDatadata;
2
3
FRam.save(data);
Andere Frage: es scheint so zu sein, dass Du die Daten aus dem FRAM in
den Objekten pufferst statt direkt auch das FRAM zuzugreifen?
Daher stimme ich Yalu X. zu, dass wir hier ein Problem haben, was
eigentlich anders gelöst werden sollte ...
Danke.
Das sieht auch sehr praktisch aus und einfach handhabbar. Beginnt die
enum Durchnummerierung intern nicht default bei 0? Ich dachte das wäre
garantiert?
1
enumNames{A=0,B,numberOfObjects};
2
3
array<Daten,numberOfObjects>sumObj{{
4
{100,200,300,"obj.0"},
5
{101,201,301,"obj.1"}
6
}};
7
8
FRamfram;
9
10
sumObj[A]=...;
11
12
fram.save(sumObj);
Mit Faltungsausdrücken kenne ich mich nicht aus. Ich weiß nur es gehört
zu variadischen Funktionen.
Ist 'homogen' nicht etwas Gutes? Sollte doch keine Einschränkung sein.
> Andere Frage: es scheint so zu sein, dass Du die Daten aus dem FRAM in> den Objekten pufferst statt direkt auch das FRAM zuzugreifen?> Daher stimme ich Yalu X. zu, dass wir hier ein Problem haben, was> eigentlich anders gelöst werden sollte ...
Aktuelle Überlegung zur Handhabung mit den Daten ist folgende.
Das sind Daten von Servos von meiner Modelleisenbahn.
Die Daten sollen nach Controller Reset bzw. Kaltart aus dem FRAM gelesen
und danach aktuell gehalten werden. Es kommt mir einfacher vor wenn ich
zu bestimmten Zeitpunkten alle Daten ins FRAM schreibe und damit aktuell
halte.
Ich weiß nicht wie eine andere Lösung dafür aussehen könnte die genauso
einfach handhabbar ist. Was stört euch denn am puffern im RAM?
Veit D. schrieb:> Was stört euch denn am puffern im RAM?
Wie häufig ändern sich denn die Daten?
Und: Nutzt sich das FRAM beim Schreiben bzw. Lesen irgendwie ab?
Oder ist der einzige Nachteil die Zugriffsgeschwindigkeit?
Veit D. schrieb:> Mit Faltungsausdrücken kenne ich mich nicht aus. Ich weiß nur es gehört> zu variadischen Funktionen.
Etwas allgemeiner: zu Parameterpacks.
>> Ist 'homogen' nicht etwas Gutes? Sollte doch keine Einschränkung sein.
Ja, homogen macht es einfacher. Heterogene Container wie std::tuple<>
sind in ihrer Handhabung es gewöhnungsbedürftig, weil man hier mit einem
Visitor-Pattern arbeitet.
> Die Daten sollen nach Controller Reset bzw. Kaltart aus dem FRAM gelesen> und danach aktuell gehalten werden. Es kommt mir einfacher vor wenn ich> zu bestimmten Zeitpunkten alle Daten ins FRAM schreibe und damit aktuell> halte.
Objektorientiert wäre jetzt eher ein Beobachter-Pattern.
> Ich weiß nicht wie eine andere Lösung dafür aussehen könnte die genauso> einfach handhabbar ist. Was stört euch denn am puffern im RAM?
Mich stört daran gar nichts, wenn Du mit dem RAM Verbrauch leben kannst.
Ich würde eher die Objekte, die im FRAM gesichert werden sollen, in eine
Klasse packen. Und diese Klasse wiederum wird vom FRAM-Controller
verwendet. Wenn die Objekte modifiziert werden, sind sie "dirty", und
der FRAM-Controller kann sie in regelmäßigen Abständen sichern. Das ist
das Prinzip eines Observers/Observables, das kann man mit statischer
(templates) oder dynamischer Assoziation (klassische OO) lösen.
Hallo,
Rick schrieb:> Veit D. schrieb:>> Was stört euch denn am puffern im RAM?> Wie häufig ändern sich denn die Daten?> Und: Nutzt sich das FRAM beim Schreiben bzw. Lesen irgendwie ab?> Oder ist der einzige Nachteil die Zugriffsgeschwindigkeit?
Das ist alles nicht das Problem. Ich möchte unnötige Buszugriffe
vermeiden. Die Hauptaktivität auf dem I2C Bus sind Sensorabfragen.
Hierbei habe ich zeitlich keine Probleme aber ich möchte mir auch keine
zusätzlichen Probleme schaffen.
Wenn ich jedem Objekt noch einen Parameter für seine FRAM Zelladdresse
mitgebe, die man zur Compilerzeit berechnen lässt, dann sollte eine Idee
keimen. 2 Einzelzugriffe hintereinander (lesen, schreiben) sind kürzer
wie wenn das gesamte Array geschrieben wird. Wenn es noch effektiver
sein soll könnte die Zellberechnung auch schnell aufwendig werden, wenn
man gezielt auf einen Member eines Objekts zugreifen möchte.
Die Frage ist was es sonst noch für Ideen gibt um effektiv ohne großen
Aufwand mit einzelnen Objekten direkt aus/zum dem FRAM zu arbeiten.
> Nutzt sich das FRAM beim Schreiben bzw. Lesen irgendwie ab?
Das ist unbedeutend, rein praktisch gesehen nicht. Deswegen nehme ich
FRAM.
> Oder ist der einzige Nachteil die Zugriffsgeschwindigkeit?
Es gibt keinen Nachteil. Im Vergleich zum EEprom sogar schneller, weil
es keine Zwangspausen gibt.
Denoch dauert jeder Buszugriff seine Zeit welche sich summieren kann.
Für eine saubere Steuerung muss man sich schon paar Gedanken machen bzw.
Überlegungen anstellen. Die wie man sieht auch wieder über den Haufen
geworfen werden können. Bsp. für die Weichensteuerung gibt es 2
Möglichkeiten. Entweder es werden alle Weichen gestellt bevor ein Zug
losfährt. Oder die Weichen stellen sich automatisch. Bei automatisch
muss man Zeiten beachten. Weil die Weiche muss gestellt sein bevor der
Zug "auf der Weiche" ist.
Edit:
@ Wilhelm:
RAM hat der AVRxDB genügend. Eine FRAM Controller Klasse klingt auch
gut. Ich habe jetzt die Qual der Wahl.
Danke euch für die Ideen. Ich muss neu überlegen was ich wie machen
werde.
Veit D. schrieb:> RAM hat der AVRxDB genügend.
Software ist wie ein ideales Gas, sie füllt auch die letzten Ecken des
Speichers ;-)
> Eine FRAM Controller Klasse klingt auch> gut.
Ich dachte, Du machst das in OO bzw. OB? Deswegen war es für mich keine
Frage, dass Du eine FRAM-Controller Klasse benötigst.
Hallo,
bei uns gehört es scheinbar zum festen Programm das immer wieder kleine
Missverständnisse entstehen. :-)
Ja ne ist schon klar. Ich wollte damit nur höflich zum Ausdruck bringen
das ich das mache. Ich benötige noch andere Klassen die dann alle am
Ende in einer Klasse "Steuerung" o.ä. enden und miteinander agieren. Das
grundlegende Konzept steht. Einzelhardwaretest sind durch. Jetzt werden
die Gedanken um das Konzept immer feiner und "nebenbei" wird
programmiert. Alles wird gut.
Hallo,
ich bin dabei paar Optionen auszutüfteln. Sowas macht Spass. Bei
möglichen 16 Objekten je 18 Bytes sind es in Summe 256. 256 Bytes
schreiben wenn sich zum Zeitpunkt immer nur zwei Bytes ändern ist nun
auch nicht so sinnvoll, denke ich, auch wenn prinzipiell möglich.
Wenn ich für jedes Objekt noch die Startadresse berechne, kann ich
bequem jederzeit einzelne Objekte aktualisieren. Wenn ich bei der
Initialisierung der Berechnungsfunktion die Indexnummer als Parameter
händisch eintrage funktioniert das.
Weil das vielleicht fehlerträchtig sein könnte, wollte ich das ohne
Parameter machen. Siehe unteres Bsp. Weil die Index Inkrementierung
jedoch nicht sauber konstant ist, kommt mindestens eine Warnung. Für die
Art der Berechnung benötigt man jedoch soweit ich das erfasse einen sich
ändernden Zwischenspeicher der letzten Adresse. Sieht jemand eine
elegante Compilezeit fähige Lösung?
Denn wenn ich statt der Indexnummer die vorher berechnete Adresse
verwende,
ist das auch nicht besser als die Indexnummer. Eher verwirrender zu
lesen.
Also meine Frage wäre gibt es eine Möglichkeit sauber zur Comilezeit die
jeweilige Anfangsadresse des Objekts zuberechnen ohne Parameter?
Hallo,
> Wo ist denn Dein Array hin?
Durch die Bemerkung "warum nicht direkt mit dem FRAM arbeiten" war ich
angestachelt in die Richtung zu denken. Aktuell ist das array damit
erstmal weg. Mittels der Adresse je Objekt liegen die Daten weiterhin
hintereinander im FRAM.
> https://en.cppreference.com/w/cpp/types/offsetof
Danke.
Veit D. schrieb:> Hallo,>>> Wo ist denn Dein Array hin?>> Durch die Bemerkung "warum nicht direkt mit dem FRAM arbeiten" war ich> angestachelt in die Richtung zu denken. Aktuell ist das array damit> erstmal weg. Mittels der Adresse je Objekt liegen die Daten weiterhin> hintereinander im FRAM.>>> https://en.cppreference.com/w/cpp/types/offsetof>> Danke.
Warum sollen die einzelnen Objekte wissen, an welcher Stelle sie im FRAM
liegen? Das ist nicht ihre Verantwortung. Sondern das ist die
Verantwortung des FRAM-Controllers: SRP.
Daher: der FRAM-Controller scannt periodisch, welche der Objekte "dirty"
sind. Nur die schreibt er weg. Dabei berechnet er den Index während des
Scans. Übrigens muss der Scan auch nicht "am Stück" durchgeführt werden,
sondern in Form einer "Coroutine" bzw. einer FSM im FRAM-Controller, so
dass er keine Laufzeit-Hickups verursacht.
Hallo,
ich überlege mir das alles nochmal. Nur aktuell sehe ich keinen Grund
für eine extra array Verwaltung namens FRAM-Controller. Wenn der blind
scannen muss um irgendwas zu ändern, dann wird das
a) zeitlich größerer Aufwand
b) muss noch ein extra Puffer her damit er zwischen alten und geänderten
Wert beim Scan unterscheiden kann
Ich kann mit der eigenen Objektadresse jedes Objekt sich selbst
verwalten lassen. Nach jeder Änderung schreibt es den einen Wert in das
FRAM. Das geht dann auch sehr schnell und erlaubt mir das sofortige
speichern. Selbstverwaltung ist doch eigentlich eine geile Sache. ;-)
Veit D. schrieb:> ich frage mich derzeit ob es möglich ist und wenn ja wie, dass man> einzelne Objekte in einem Array zusammenfassen und automatisch> gegenseitig aktuell halten kann.
Ich benutze für sowas structs. Structs kann man beliebig kaskadieren.
Man kann somit beliebig tief auf Elemente zugreifen, trotzdem bleibt das
immer ein einziger Speicherbereich.
Für den Zugriff auf Arrayelemente benutze ich keine magischen Zahlen
0,1,2,..n, sondern Enums.
Hier mal ein Beispiel in C:
1
enum
2
{
3
CON_HVRAMP,
4
CON_DEBUG,
5
CON_REPLYWAIT,
6
CON_ERRTIME,
7
CON_VFILLIM,
8
CON_IFILLIM,
9
CON_IFIL,
10
CON_IFILSTB,
11
CON_IFILRAMP,
12
CON_IEMISS,
13
CON_AVG_CNT,
14
CON_REG_TIME,
15
CON_REG_P,
16
CON_REG_I,
17
CON_REG_D,
18
CON_IEMDEGAS,
19
CON_VAL_SIZE
20
};
21
22
/* associated EEPROM usage
23
Parameter .gain .offset .comp .lower .upper .val
24
ADC X X X - - -
25
DAC X X - X X X
26
Control - - - X X X
27
*/
28
29
typedefstruct// DAC parameter
30
{
31
floatgain;
32
floatoffset;
33
floatlower;
34
floatupper;
35
floatval;
36
}eedacval_t;
37
38
typedefstruct// ADC parameter
39
{
40
floatgain;
41
floatoffset;
42
floatcomp;// compensation of internal resistor current
43
}eeadcval_t;
44
45
typedefstruct// Control parameter
46
{
47
floatlower;
48
floatupper;
49
floatval;
50
}eeconval_t;
51
52
typedefstruct// all parameters including calibration
53
{
54
uint16_tversion;
55
eeadcval_tadc[ADC_VAL_SIZE];
56
eedacval_tdac[DAC_VAL_SIZE];
57
eeconval_tcon[CON_VAL_SIZE];
58
uint16_tcrc;
59
}eeppar_t;
60
61
typedefstruct// copy of calibration
62
{
63
uint16_tversion;
64
eeadcval_tcopy[MAX_CHANNELS];// sum of used ADC + DAC + reserve for future use
65
charcal_signature[CAL_SIG_SIZE];
66
uint16_tcrc;
67
}eepcal_t;
68
69
typedefstruct
70
{
71
eepcal_tcal;
72
eeppar_tpar;
73
}eepdata_t;
74
75
externeepdata_tEepdata;
Ein Zugriff auf ein konkretes Element kann dann so aussehen:
Hallo,
Danke Peter, so war und ist es gedacht. So oder so ähnlich. ;-)
Ich bin derzeit noch an einer anderen Aufgabe dran, in einer
"Steuerungsklasse" die Sensoren und Weichen miteinander agieren zu
lassen. Wenn das fertig ist kommt das Thema FRAM dazu und dann wird sich
zeigen welche Strategie optimal ist die Werte zu speichern und wie ich
sie speichere. Was ich vermeiden möchte ist, dass immer alle Werte
gespeichert werden, obwohl sich nur ein Wert zum Zeitpunkt geändert hat.
Was ich auch vermeiden möchte ist das erst alle Daten aus dem FRAM
gelesen werden müssen um festzustellen welcher Wert sich geändert hat.
Welcher Wert sich geändert hat weiß das Programm ja quasi selbst. Wenn
ich das mit einer "FRAM Control" Klasse mache wie Wilhelm vorgeschlagen
hat, dann muss ich dieser nur mitteilen was sich geändert hat, dann
berechnet diese die Adresse im FRAM und überschreibt den Wert. So meine
aktuelle Theorie. Das wird sich zum Zeitpunkt wenn ich soweit bin
herauskristallisieren. :-)
Veit D. schrieb:> Was ich auch vermeiden möchte ist das erst alle Daten aus dem FRAM> gelesen werden müssen um festzustellen welcher Wert sich geändert hat.
Müssen sie nicht. Ein FRAM hat keinerlei Schreibzeit. Am schnellsten
geht daher zu schreiben, ohne erst zu vergleichen.
Anders beim EEPROM, da braucht der AVR 8ms je Byte. Je nach Größe des
Blocks kann die Mainloop deutlich verzögert werden. Besonders beim
ersten Einschalten, wenn im EEPROM alles noch auch 0xFF steht. Ich
schreibe daher im Hintergrund. Je Mainloop Durchlauf wird geprüft, ob
überhaupt was zu schreiben ist, das letze Schreiben beendet ist und dann
ein Byte verglichen und geschrieben.
1
struct
2
{
3
uint8_t*src;
4
uint16_tdst;
5
uint16_tlen;
6
}eew;
7
8
/* Attention: using EEPROM interrupt instead polling may cause longer delay of the main loop */
Hallo,
genau, direktes Schreiben beim FRAM war auch meine Überlegung. Der
letzte Teil der Unterhaltung drehte sich um das Wie. Ob ich das direkt
in die Weichen-Klasse einbaue, dort wo mit den Werten gearbeitet wird.
Oder ob ich eine weitere FRAM-Control Klasse schreibe die die Werte von
der Weichen-Klasse bekommt. Wenn es allein bei den Weichen-Parametern
bliebe könnte ich das in die Weichen-Klasse direkt einbauen. Wenn ich
aber später noch andere Werte speichern möchte wird das schnell Essig.
Also wird es doch auf eine extra FRAM-Control Klasse hinauslaufen. Mach
ich alles zu seiner Zeit. :-)
Veit D. schrieb:> Ich möchte unnötige Buszugriffe> vermeiden. Die Hauptaktivität auf dem I2C Bus sind Sensorabfragen.
Ja, Hardwareressourcen sind typisch nicht reentrant, d.h. man muß sich
was einfallen lassen, wenn sie von mehreren Instanzen benötigt werden.
Das Problem sind auch nicht unnötige Zugriffe, sondern die Zeit der
Blockierung anderer Tasks. Eine Möglichkeit ist das Anlegen mehrerer
Puffer, die dann der I2C-Interrupt Round-Robin abarbeitet.
Für Konfigurationsdaten nehme ich aber auch gerne einen exklusiven I2C
mit 2 normalen IO-Pins als I2C-Master in Software. Dann kann man ähnlich
zu meinem EEPROM-Beispiel kleine Häppchen in der Mainloop übertragen,
ohne daß nennenswert Zeit belegt wird. Da der Bus ja exklusiv ist, muß
auch zwischendurch ein Paket nicht abgeschlossen sein. Man kann bequem
einzelne Bytes oder sogar nur Bits übertragen.
Manche MCs haben aber auch 2 oder mehrere I2C-Busse.
Veit D. schrieb:> Für eine saubere Steuerung muss man sich schon paar Gedanken machen bzw.> Überlegungen anstellen.
Für Steuerungen aller Art hat sich bei mir ein Mapping im RAM bewährt.
Alle IOs (Sensoren, Tasten, Aktoren, LEDs) bekommen ein Bit in einem
Bitarray im RAM.
Die gesamte Steuerlogik arbeitet nur im RAM, wie eine SPS. Und eine
extra Task sorgt dann für ein Update der Peripherie in beide Richtungen.
Innerhalb der Statemaschine sind mehrere Wechsel erlaubt, da sie ja nach
außen keine Glitches erzeugen.
Hier mal eine Task aus einer größeren Statemaschine:
1
voidtlc_relays(void)// switch TLC relays
2
{
3
REL_SIMS=0;
4
if(LED_PLC_SIMS)
5
REL_SIMS=1;
6
if(LED_STC)// HFC/PLC -> STC
7
{
8
if(REL_DBM)
9
{
10
REL_DBM=0;
11
SWtimer_add(rel_stc_on,DELAYED);
12
}
13
}
14
else// STC -> HFC/PLC
15
{
16
if(REL_STC)
17
{
18
REL_STC=0;
19
SWtimer_add(rel_dbm_on,DELAYED);
20
}
21
}
22
}
Die Timeraufrufe dienen dazu, daß sich ausschließende Relais nie
gleichzeitig gezogen sind und es einen Kurzschluß gibt. Das jeweils
andere Relais ist also nach der Wartezeit sicher abgefallen.
Das Programm funkioniert, sodass alle Servos entsprechend reagieren und
langsam ihre Position ändern.
Die Klasse 'ControlWeiche' und 'ControlSignal' enthalten je eine
Elementfunktion 'isChangedPosition' mit bool Rückgabewert. Die
informiert mit einmalig 'true' bei Ereignis, wenn das Servo in der neuen
Endposition ist. Damit kann mittels Trigger der neue Positionswert
'current' im FRAM gespeichert werden.
Jetzt habe ich mir folgendes überlegt. Jede Instanz von 'ControlWeiche'
und 'ControlSignal' hat ja schon durch die enum Servoname Nummerierung
den Indexwert automatisch enthalten. Ich brauch doch jetzt nur noch aus
der jeweiligen Instanz dem FRAM-Controller die Indexnummer übergeben.
Das macht uint8_t getObjIndex()
Damit weiß der FRAM-Controller 'icFRAM' das ein Wert dieser Instanz
geändert werden soll. Den Adresse und Offset zum Element kann der
FRAM-Controller selbst berechnen.
Die Hauptschleife sollte dann folgendes machen
Macht das Sinn? Irgendwie schon finde ich. Damit ist das xy Problem aus
der Welt. Referenznamen der Objekte entfallen. Die Instanzen benötigen
keine FRAM Adressen mit. Die Werte die zur Laufzeit benötigt werden
kommen vom einzigen Puffer im RAM, dem struct Array 'ServoDaten
servoObj'. Ändert sich eine Servoposition, ändert sich das struct
Element 'current' was dann im FRAM aktualisiert wird.
Dann fehlt mir nur noch eine Kalibrierfunktion für die Servos und das
nach Controllerreset die Daten vom FRAM eingelesen werden vor der ersten
Verwendung.
Sollte doch alles der richtige Weg sein?
Veit D. schrieb:> Sprichst erneut die eigenständige FRAM Verwaltung an mit> scannen, vergleichen und schreiben?
Genau das, was Du in der o.g. for-Schleife machst: das ist ja scannen,
und wenn nötig: schreiben.
Hallo,
ich denke da liegt ein Missverständnis vor.
w.getObjIndex() liefert die Indexnummer des Weichen Array. Das was ich
in den enums festgelegt habe.
Damit wird im FRAM Controller die Speicheradresse der zu ändernden
Variable im FRAM der gesamten Struktur berechnet. Damit kann direkt ohne
Scan oder Vergleich geschrieben werden. Zu dem Zeitpunkt ist bekannt das
ein Update stattfinden muss.
Aktuell sieht das so. Noch sind das Tests. getAddr() gibt mir testweise
die korrekte Addresse im FRAM zurück. Zur Kontrolle bevor ich wirklich
auf den FRAM schreibe.
Bsp.
Die Servoposition von Weiche WCDR hat sich geändert.
Dann liefert
w.isChangedPosition()
den Trigger
und der FRAM Controller 'icFRAM' bekommt mitgeteilt das er
die Instanz mit ihrem Index 2 und davon den Member 'current' mit Offset
4 schreiben soll. Die gesamte Struktur beginnt im FRAM bei Adresse 0.
Eine Instanz der Struktur 'ServoDaten' hat eine Einzellänge von 18
Bytes. Die Formel
getAddr = initObjAddr + (objStartIndex*lengthStructure) + memberOffset
0 + (2*18) + 4 = 40
Bedeutet der zu ändernde Member 'current' der Weiche WCDR liegt auf
Addresse 40 im FRAM. Resultat ist kürzeste Nutzung des I2C Busses, weil
nur das direkt geändert wird.
Wenn ich den Aufwand weglasse und zyklisch scanne, muss die gesamte
Struktur von derzeit 144 Bytes jedesmal gescannt werden. Ziemlich lange
Blockierung des I2C Busses finde ich.
Wenn ich den Scan auf den Member 'current' einschränke kann ich die
Servos für ihre Endlagen nicht einstellen. 'left' und 'right' müssen
jederzeit bei Bedarf geändert werden können. Das ginge wiederum in einer
Kalibrierroutine recht einfach mit
Hallo,
ich habe mir das alles nochmal überlegt mit der unabhängigen Verwaltung
eines FRAM Controllers ohne externe Methodenaufrufe. Es gibt ja nur 2
Möglichkeiten wie der Controller selbst feststellen kann ob sich Werte
geändert haben die er aktualisieren muss.
a)
Er liest zyklisch alle Daten der Struktur aus dem FRAM und vergleicht
sie mit der Struktur die schon im RAM liegt.
Das ist sehr Zeitaufwändig. Würde jedoch einen Puffer sparen.
b)
Der FRAM Controller hält noch einen Puffer für sich bereit in der Größe
der Struktur der den Inhalt des FRAM abbildet.
Wenn der FRAM-Controller die aktuellen Daten der Struktur mit denen
seines Puffers vergleicht, kann er sehr schnell feststellen ob er
irgendwas aktualisieren muss. Ginge sehr schnell kostet aber zusätzlich
RAM je nach Strukturgröße.
Wenn ich das mit meiner Vorgehensweise vergleiche womit ich mittels
Methode dem FRAM Controller direkt sage ändere bitte genau diesen Wert,
damit habe keine unnötige Busbelastung durch scannen und keine
Notwendigkeit eines extra Puffers. Das ist für meine Modelleisenbahn
perfekt. Nach meiner aktuellen Bewertung der Lage belasse ich das
erstmal so.
Ich danke Euch für die Denkanstöße. Haben ja letztlich zur aktuellen
Lösung ohne Umwege geführt. Am Anfang hatte ich zu kompliziert gedacht.
Genau dafür ist ein Forum da. :-)
Hallo,
vielleicht wird es doch nochmal anders. ;-) Nach einer Fehlersuche
stellte sich heraus das bei einer Objektübergabe als Template Parameter
nicht nur die Objektlänge erhalten bleibt sondern die gesamte Struktur
mit allen ihren Eigenschaften. Weil das direkt eingesetzt wird. Wie geil
ist das denn. Wird mir jetzt erst so richtig bewusst was das für eine
Magie hat. :-) Besser spät als nie. Damit lässt sich arbeiten. :-)
Veit D. schrieb:> Nach einer Fehlersuche> stellte sich heraus das bei einer Objektübergabe als Template Parameter> nicht nur die Objektlänge erhalten bleibt sondern die gesamte Struktur> mit allen ihren Eigenschaften.
Was meinst Du da denn mit?
Also: es gibt drei Arten von template-Parametern:
- type-parameter (also: Datentypen)
- non-type-template-parameter (NTTP)
- template-template-parameter: (also: templates)
Als NTTP functionieren (grob gesagt) Werte (constexpr) und
lvalue-Referenzen für Objekte mit static-storage. Bei den Datentypen der
Werte müssen es dann auch noch sog. structural-types sein (die
wesentliche Einschränkung ist hier, dass die Member dieser
structural-types public sein müssen: unschön).
Hallo,
im Nachhinein fiel mir ein das ich das schon mit Template Funktionen
gemacht hatte. Naja.
> Was meinst Du da denn mit?
Ich hatte erhofft das wäre ersichtlich. Ich übergebe dem FramControl
Objekt 'icFRAM' das Objekt 'servoObj'. Der Parameter ist 'auto &obj'.
Wobei ich den Namen icFRAM anders wählen sollte. ;-)
Veit D. schrieb:>> Was meinst Du da denn mit?>> Ich hatte erhofft das wäre ersichtlich. Ich übergebe dem FramControl> Objekt 'icFRAM' das Objekt 'servoObj'.
Ja klar. Bin ja nicht blind.
Ich meinte diesen Satz:
> nicht nur die Objektlänge erhalten bleibt sondern die gesamte Struktur> mit allen ihren Eigenschaften.
Hallo,
damit war gemeint das ich der Klasse 'FRAM-Control' von
'ServoDaten servoObj [8]'
auf alle Elemente zugreifen kann. Index und Struktur Member. Das hatte
ich die ganze Zeit völlig außer Acht gelassen. Das ermöglicht mir
spezifischere Methoden zu schreiben die mit weniger Parameter auskommen.
In C++ Sprache "Elementfunktionen".
Veit D. schrieb:> Hallo,>> damit war gemeint das ich der Klasse 'FRAM-Control' von> 'ServoDaten servoObj [8]'> auf alle Elemente zugreifen kann.
Nun, Du hast dich eine lvalue-Referenz auf Dein Array. Also: die
lvalue-Referenz ist wie "ein neuer Name" (eben eine Referenz) für das
Array. Sollte also wenig verwunderlich sein.
> Index und Struktur Member.
Was für einen Index? Oder meinst Du die Dimension.
Dir sollte bewusst sein, das der NTTP "auto& obj" auf alles matched.
Wilhelm M. schrieb:>> Index und Struktur Member.> Was für einen Index? Oder meinst Du die Dimension.
Die Frage ist seltsam. Hier habe ich einen Index 0...7.
1
ServoDatenservoObj[8]{
2
{1311,1611,1501,"Weiche.BCL"},
3
{1322,1622,1502,"Weiche.CDL"},
4
{1333,1633,1503,"Weiche.CDR"},
5
{1344,1644,1504,"Weiche.DER"},
6
{1355,1655,1505,"Signal.CL "},
7
{1366,1666,1506,"Signal.CR "},
8
{1377,1677,1507,"Signal.DL "},
9
{1388,1688,1508,"Signal.DR "}
10
};
> Dir sollte bewusst sein, das der NTTP "auto& obj" auf alles matched.
Ja da muss man aufpassen. Aber das passiert ja hier nur einmal beim
initialisieren der FRAM-Controller Instanz. Von daher habe ich keine
Bedenken.
Veit D. schrieb:> Wilhelm M. schrieb:>>>> Index und Struktur Member.>> Was für einen Index? Oder meinst Du die Dimension.> Die Frage ist seltsam. Hier habe ich einen Index 0...7.>
1
>ServoDatenservoObj[8]{
2
>{1311,1611,1501,"Weiche.BCL"},
3
>{1322,1622,1502,"Weiche.CDL"},
4
>{1333,1633,1503,"Weiche.CDR"},
5
>{1344,1644,1504,"Weiche.DER"},
6
>{1355,1655,1505,"Signal.CL "},
7
>{1366,1666,1506,"Signal.CR "},
8
>{1377,1677,1507,"Signal.DL "},
9
>{1388,1688,1508,"Signal.DR "}
10
>};
11
>
Dann meinst Du wohl die Dimension (hier: 8).
>>> Dir sollte bewusst sein, das der NTTP "auto& obj" auf alles matched.> Ja da muss man aufpassen. Aber das passiert ja hier nur einmal beim> initialisieren der FRAM-Controller Instanz. Von daher habe ich keine> Bedenken.
Du kannst Deinen FRAM-Controller für Array-Typen spezialisieren. Dabei
kannst Du auch gleich die Dimension des Arrays ableiten (ohne sie so
C-like berechnen zu müssen).
Hallo,
wichtiger für mich als die Dimension ist zu wissen die Anzahl der Bytes,
also die Länge des gesamten Objektes. Dafür benötige sowieso sizeof.
Übrigens ist offsetof ein C Makro. ;-)
Egal. Ich habe versucht den Member für ein Array zu spezialisieren. Es
wird immer angemeckert das ein Argument nicht stimmt. Bezieht sich hier
auf den 3. Code 'NEU' Zeile 5.
1
FRamControl.h:32:102: error: template argument 1 is invalid
Veit D. schrieb:> Wobei das im Trockentest funktioniert.
Leider nein, da machst Du etwas ganz anderes.
> int main()> {> Test<int[1]> a;> Test<int[2]> b;> }> [/c]
Hier instanziierst Du das template Test<> für den Datentyp(!) (kein
NNTP!) "int[1]" bzw. "int[2]". Und für diesen Array-Typ hast Du auch
eine Spezialisierung.
In Deinem FrameControl<> möchtest Du aber einen NTTP (einen Wert oder
eine lvalue-referenz) eines bestimmten Typs als Ausnahme
(Spezialisierung) formulieren.
In Deinem Code ist das, was Du schreibst, auch keine Spezialisierung,
sondern einfach syntaktisch falsch.
Daher:
In der Spezialisierung muss "obj" (die Array-Referenz) von einem
(generischen) Array-Typ (hier: T[N]) sein.
Ich würde mir das Thema template-parameter (s.a. mein Beitrag oben)
nochmal genauer "reinziehen" und auch: was sind Typen, was sind Werte.
Hallo,
das ist aber auch mit den Templates ein durcheinander was wann wofür wo
stehen muss. Man oh man. Das ist mir noch um zu viele Ecken gedacht. Man
muss denken wie ein Compiler. Was könnte er wie machen. Das wird
anstrengend. ;-)
Ich frage mich schon länger wofür benötigt der Compiler das allgemeine
Template, wenn das spezialisierte direkt dafür zugeschnitten wurde
sodass er es verwenden kann? Dann müßte doch bei fehlen des allgemeinen
Templates und falschen Datentyp auch eine Meldung seitens des Compiler
kommen das er keine passende Klasse gefunden hat.
Lasse ich das allgemeine Template weg, kann er nicht kompilieren, weil
ihm an den Argumenten was stört. Er meckert nicht wegen falschen
Datentyp, sondern wegen der Anzahl der Argumente.
Veit D. schrieb:> Hallo,>> das ist aber auch mit den Templates ein durcheinander was wann wofür wo> stehen muss. Man oh man. Das ist mir noch um zu viele Ecken gedacht. Man> muss denken wie ein Compiler. Was könnte er wie machen. Das wird> anstrengend. ;-)
Naja, die meisten Programmierer denken eben nur in Werten. Das man auch
mit Datentypen "rechnen" kann (oder sagen wir: Abbildungen (Funktionen)
durchführen kann), ist den meisten tatsächlich zu viel Voodoo. Dabei ist
es eigentlich ganz einfach ;-) Genauso einfach wie Laufzeitfunktionen
...
> Ich frage mich schon länger wofür benötigt der Compiler das allgemeine> Template, wenn das spezialisierte direkt dafür zugeschnitten wurde> sodass er es verwenden kann?
Ein Spezialisierung ist immer eine "Ausnahme" vom allg. Template.
Deswegen braucht es immer das allg. template, zu dem dann einige
Ausnahmen formuliert werden. Ggf. können mehrere Spezialisierung
anwendbar sein, davon wir dann die, die am meisten spezialisiert ist,
ausgewählt.
> Dann müßte doch bei fehlen des allgemeinen> Templates und falschen Datentyp auch eine Meldung seitens des Compiler> kommen das er keine passende Klasse gefunden hat.
Nö. Das allg. template macht ja in Deinem speziellen Fall keine
Einschränlungen bzgl. obj&, weil Du es mit auto deklariert hast.
> Lasse ich das allgemeine Template weg, kann er nicht kompilieren, weil> ihm an den Argumenten was stört. Er meckert nicht wegen falschen> Datentyp, sondern wegen der Anzahl der Argumente.
Ja, das allg. template und die Spezialisierung müssen die gleiche Anzahl
von Parametern haben.
> Im übrigen funktioniert das mit deinem Syntax, allgemeines und> spezialisiertem Template.
Ja, freut mich.
Hallo,
wenn du das so schreibst und erklärst verstehe ich das sogar. :-)
Vielen Dank.
Gibt es ein gutes Buch in deutsch welches das Thema Template
Programmierung näher erklärt?
Veit D. schrieb:> Gibt es ein gutes Buch in deutsch welches das Thema Template> Programmierung näher erklärt?
Die Anforderung ("Gut" && "Buch" && "deutsch") erscheint mir fast
unerfüllbar ;-)
Als Einstieg: http://www.tmplbook.com/
Nicht vergessen: template <typename DatenTyp> heisst auf deutsch:
Vorlage für einen DatenTYP. Man stellt class/struct einen allgemeinen
DatenTYP zur Verfügung, der später konkret spezialisiert wird.
A. B. schrieb:> Spezialisierungen schreibe ich untereinander formatiert, template> <typename T> struct Signed {using Result_t = T> };> template <> struct Signed <unsigned int> {using> Result_t = int };> template <> struct Signed <unsigned char> {using> Result_t = char };> template <> struct Signed <unsigned long> {using> Result_t = long };> template <> struct Signed <unsigned short> {using> Result_t = short };> template <> struct Signed <unsigned long long> {using> Result_t = long long };
Würde nun vermuten, dass das für eine Plattform ist, für die es keine
Implementierung der stdlibc++ gibt (andernfalls: std::make_signed<>).
Ich hätte mich da an die übliche Praxis gehalten und den Ergebnistyp der
Meta-Funcktionen "type" genannt. Aber das ist ja nicht wirklich
wichtig.
Wichtig und nicht gut finde ich, dass die Abbildung
Signed<XYZ> -> XYZ
gemacht wird. Das ist mehr als irreführend.
Mindestens lässt man das allg. Template ohne Definition oder leer (gibt
halt schlechte Fehlermeldungen) oder man setzt ein requirement (ähnlich)
wie std::is_integer<> dazu (der Mathematiker würde sagen, man schränkt
die Definitionsmenge der Funktion "Signed<>" auf die ganzzahligen Typen
ein :-)).
Wilhelm M. schrieb:> Würde nun vermuten, dass das für eine Plattform ist, für die es keine> Implementierung der stdlibc++ gibt (andernfalls: std::make_signed<>).
Das Beispiel stammt aus McuCPP: /mcucpp/template_utils.h von 2013
(modifiziert using .. ) und soll nur zeigen, wie ein template<> struct
und die dazugehörigen Spezialisierungen mit ein paar zusätzlichen
Leerzeichen gut lesbar werden.
Gut lesbar bedeutet auch einfacher verständlich und besser
nachvollziehbar, insbesondere für template <> Neulinge.
Ein anderes Beispiel:
A. B. schrieb:> Wilhelm M. schrieb:>> Würde nun vermuten, dass das für eine Plattform ist, für die es keine>> Implementierung der stdlibc++ gibt (andernfalls: std::make_signed<>).>> Das Beispiel stammt aus McuCPP: /mcucpp/template_utils.h von 2013> (modifiziert using .. ) und soll nur zeigen, wie ein template<> struct> und die dazugehörigen Spezialisierungen mit ein paar zusätzlichen> Leerzeichen gut lesbar werden.
Schön wäre, wenn es gut lesbar und zugleich richtig und sinnvoll ist.
Die Plattformfrage hast Du nun nicht beantwortet.
> Gut lesbar bedeutet auch einfacher verständlich und besser> nachvollziehbar, insbesondere für template <> Neulinge.
Im Beispiel des TO waren es allerdings 5 template-Paramater, da wird
Deine vorgeschlagene Formatierung schon etwas unhandlich.
> Ein anderes Beispiel:>
Warum hast Du denn jetzt make_unsigned<>, wenn Du schon Signed<> hast?
Und hier ist der Ergebnistyp (wie üblich) type genannt.
Wobei make_unsigned<> nun korrekt ist, weil das allg. template nun zwar
deklariert, aber nicht definiert ist. Wobei man den Definitionsbereich
durchaus auf die vorzeichenlosen Zahlen ausdehnen könnte, dann ist die
Funktion ggf. allgemeiner verwendbar.
Nicht wirklich .. und jedes template kann bei Bedarf erklärend
kommentiert werden.
Es gibt Fälle, da ist diese Schreibweise hier im Forum eingebettet nicht
darstellbar. Auf dem eigenen Rechner mit HD-Monitor ist bei 110CH/Zeile
aber noch lange nicht Schluss. ( Siehe Anhang )
Dieses Layout dient ausschliesslich dem Verständnis. Beide oben
genannten cpp-Plattformen sind nicht dokumentiert und aus dem Bedürfnis
den Sourcecode zu verstehen, ist diese Schreibweise entstanden. Ist nur
ein Hilfsmittel.
A. B. schrieb:> Wilhelm M. schrieb:>> Die Plattformfrage hast Du nun nicht beantwortet.>> McuCPP sollte eigentlich bekannt sein. Google hilft:> https://github.com/KonstantinChizhov/Mcucpp/blob/dev/mcucpp/template_utils.h
Es ging nicht um die Frage, ob ich McuCpp kennen, sondern darum, auf
welcher Plattform Du das einsetzt?
>>Warum hast Du denn jetzt make_unsigned<>, wenn Du schon Signed<> hast?>> Weil ich .. "etwas Abwechslung in die Disussion bringen wollte."
Bingo!
> Das zweite Beispiel sollte auch bekannt sein:> https://sourceforge.net/p/wmucpp/code/ci/master/tree/include0/std/type_traits
Dann solltest Du das wenigstens auch vollständig in den Beitrag
kopieren. Hättest Du das gemacht, dann hätte sich mein Kommentar für
vorzeichenlosen Ganzzahlen erübrigt ;-)
Hallo,
wie bekomme ich es hin das es trotz 2 Spezialisierungen für array und
non array Objekte nutzbar ist?
Ich verwende in 3 Methoden den typename N für den Index. Den gibt es ja
bei non array Objekten nicht. Wie unterscheide ich das in den Methoden?
Überladung funktioniert ja nicht.
Veit D. schrieb:> wie bekomme ich es hin das es trotz 2 Spezialisierungen für array und> non array Objekte nutzbar ist?
Genau für diese beiden Fälle hast Du doch Spezialisierungen. Irgendwie
kann ich Deinen Einwand nicht verstehen. Wobei Du in dem Code die
non-Array Variante nur deklariert hast, ein template-Definition fehlt.
Abgesehen davon sollte Dein Code gar nicht compilieren, weil Du eine
Semikolon zuviel entfernt hast.
Also: was willst Du machen, und wo ist die Fehlermeldung dazu. Im Moment
sehe ich nur Code, der funktionieren sollte (bis auf das Semikolon).
Edit: sehe gerade, dass Du ja ein allg. Template und eine
"vermeintliche" Spezialisierung. Eigentlich solltest Du da eine Warnung
bekommen wegen ambiguos template-instantiation. Also, was da steht ist
vollkommen inkonsistent.
Hallo,
ähm, jetzt fehlt noch die angesprochene Definition für non array Objekte
und um Codedopplungen zu vermeiden muss ein "Mutterklasse" her die dann
vererbt wird? Korrekt?
Veit D. schrieb:> Hallo,>> ähm, jetzt fehlt noch die angesprochene Definition für non array Objekte
rischtisch
> und um Codedopplungen zu vermeiden muss ein "Mutterklasse" her die dann> vererbt wird?
Ja, kann man machen, wenn es sinnvoll ist. Muss man nicht.
Hallo,
ehrlich gesagt komme ich nicht mehr mit.
Mache ich das Semikolon dort hin kompiliert nichts mehr, egal welche
Version.
Wegen Codedopplung. Wenn die Klassendefinition fehlt muss man den Code
doch wiederholen oder vererben. Ich verstehe die Aussage nicht "kann,
muss nicht".
Was ich glaube verstanden zu haben ist, dass meine "non array
Spezialisierung" nonsens ist, weil das das allgemeine Template
übernimmt. Nur fehlt dafür die komplette Definition. Würdest du den Code
in jeder Klasse doppelt schreiben?
Veit D. schrieb:> Hallo,>> ehrlich gesagt komme ich nicht mehr mit.> Mache ich das Semikolon dort hin kompiliert nichts mehr, egal welche> Version.
Wie ich schon unter "edit" schrieb: es ist eine "vermeintliche"
Spezialisierung, die aber dasselbe aussagt wir das allg. template mit
"auto& obj". Damit wird eine mögliche Instanziierung mehrdeutig.
> Wegen Codedopplung. Wenn die Klassendefinition fehlt muss man den Code> doch wiederholen oder vererben. Ich verstehe die Aussage nicht "kann,> muss nicht".
Wie schon gesagt: eine Spezialisierung stellt eine "Ausnahme" vom
allgemeinen Fall da. Wie unterschiedlich dann die Implementierung
ausfällt, kann man so allg. ja nicht beantworten.
> Was ich glaube verstanden zu haben ist, dass meine "non array> Spezialisierung" nonsens ist, weil das das allgemeine Template> übernimmt.
Genau.
>Nur fehlt dafür die komplette Definition. Würdest du den Code> in jeder Klasse doppelt schreiben?
Wahrscheinlich nicht, kann ich aber so nicht beurteilen.
Kommt wie gesagt darauf an: ein Mixin (Basisklasse) wäre denkbar, oder
einfach nur andere template-Klassen, die eine sinnvolle Teilfunktion
übernehmen.
Kleinere Variationen im Code kannst Du auch mit constexpr-if machen.
Und warum muss Du überhaupt eine Unterscheidung in ein Skalar und Array
machen. Man kann doch das Skalar als ein Array[1] auffassen.
Hallo,
ich weiß nicht wie man ohne 2. Klassendefinition und ohne Vererbung zwei
Deklarationen (allg. Template und array Spezialisierung) in eine Klassen
Definition bringen soll?
Wenn das irgendwie funktioniert und man dann constexpr-if anwenden
könnte, muss man dafür Type Traits verwenden und auf Datentyp Array
abfragen?
Wilhelm M. schrieb:> Und warum muss Du überhaupt eine Unterscheidung in ein Skalar und Array> machen. Man kann doch das Skalar als ein Array[1] auffassen.
Du meinst ich soll alles so lassen wie es ist und mich damit begnügen?
1
Datenobj0[]{1311,1611,1501,"Weiche.A"};
Das kann nicht die Lösung sein. Es könnten auch einfache Variablen sein.
Hallo,
aktuell habe ich eine Idee gegen Codedopplung. Ich könnte alle Methoden
in zwei namespaces auslagern und das was ich in der jeweiligen Klasse
benötige verwenden. Mal sehen ob das klappt ...
Hallo,
es ist anders geworden. Habe nun 2 komplette Definitionen. Nur gefallen
mir die Codedopplungen irgendwie nicht. Wenn ich mir vorstelle man hätte
noch mehr Spezialisierungen mutiert das zum Wahnsinn. Kann man die
Codedopplung wirklich nicht vermeiden?
Veit D. schrieb:> es ist anders geworden. Habe nun 2 komplette Definitionen. Nur gefallen> mir die Codedopplungen irgendwie nicht. Wenn ich mir vorstelle man hätte> noch mehr Spezialisierungen mutiert das zum Wahnsinn. Kann man die> Codedopplung wirklich nicht vermeiden?
1. Frage: Was soll dieses Template abstrahieren, wenn doch alles zu
MyFRAM_I2C deligiert wird?
2. Frage: Welchen Unterschied macht das Template für Skalare und
Objekte?
Summa summarum: mir erscheint es komplett überflüssig, Du hast doch
alles schon in MyFRAM_I2C drin.
Hallo,
MyFRAM_I2C
stellt das Interface dar und setzt auf Wire.h auf. Es kümmert sich um
die Berechnung der Device Adresse und die Speicherzellenadressen.
Beinhaltet einen Monitor für das gesamte FRAM. Kümmert sich um das lesen
und schreiben von Daten auch wenn diese größer sind wie der eigentliche
TWI Buffer. Hier geht es mehr darum wie kommen und gehen die Daten zum
und vom FRAM.
MyFramControl
kümmert sich nur um das Objekt welches als Parameter angegeben wurde.
Sieht man schön an read und write. Man kann einfach read() oder write()
aufrufen ohne eine Orgie von Parameter mitgeben zu müssen, weil diese
sowieso gleich bleiben.
Das Ganze funktioniert so und ist gleichzeitig noch Spielwiese. Deswegen
gibt es bspw. eine Methode readTo(). Dafür ist auch die Spezialisierung
gedacht. Man kann mit readTo einen Datensatz in ein anderes Objekt
auslesen welches den gleichen Datentyp haben muss, sonst geht das ja
schief. Habe ich genutzt zum überprüfen ob die Daten richtig geschrieben
und gelesen werden. Die update() Methode ist auch noch in MyFramControl
damit ich Member einer Struktur gezielt überschreiben kann.
Die Frage war, kann man die Codedopplung der MyFramControl Definitionen
vermeiden?