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?
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?
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.
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'|
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.
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.
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 "*".
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.
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:
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.
Danke euch, ich muss das erstmal neu (ein)sortieren ...
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.
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.
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 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
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
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.
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.
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!
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.
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.
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?
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 ...
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?
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?
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.
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.
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.
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,
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.
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.
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.
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. ;-)
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:
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. :-)
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 */
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. :-)
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.
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.
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?
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 + (objStartIndexlengthStructure) + memberOffset
0 + (218) + 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
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. :-)
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. :-)
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).
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. ;-)
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".
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.
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).
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
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.
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.
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.
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?
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.
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 :-)).
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.
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.
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.
Im Beispiel des TO waren es allerdings 5 template-Paramater,da wird
Deine vorgeschlagene Formatierung schon etwas unhandlich.
1
template<uint8_taddr,//
2
TwoWire&line,//
3
uint16_tmemorySize,//
4
typenameN,//
5
uint32_tinitObjAddr>//
6
//*****************************
7
classFramControl{
8
9
private:
10
MyFRAM_I2C<addr,line,memorySize>fram;
11
12
uint32_tobjStartIndex{0};
13
uint32_tmemberOffset{0};
14
constsize_tlengthStructure{sizeof(N)};
15
...
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.
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 ;-)
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.
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.
ä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?
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?
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.
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?
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 ...
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?
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?
Frage: Was soll dieses Template abstrahieren, wenn doch alles zu MyFRAM_I2C deligiert wird?
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.
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?