Jetzt muss ich aber bei Verwendung der eigentlichen Nummer dahinter,
Parameter eines Funktionsaufrufes etc., immer und überall einen Cast
machen. Irgendwie nervt mich das langsam, weil es die Lesbarkeit
beeinträchtigt. Auf der einen Seite wichtig und auf der anderen bläht
das die Zeilenlänge auf.
1
(uint8_t)Gleis::GA
Ich hätte es gern ohne Cast in der Verwendung an allen Stellen im
Programm.
1
Gleis::GA
Ist das ohne casten möglich in C++? Irgendwo einmalig verstecken?
Ansonsten fiele mir nur ein eine Struktur anzulegen und alles von Hand
durch zu nummerieren.
Veit D. schrieb:> Hallo,
Was ist das Ziel der Daten?
Also, was willst du damit anstellen?
Wozu die Nummern?
Veit D. schrieb:> NumberOfGleise
Ich befürchte, dass du einem alten C Hack aufgesessen bist.
Also könntest du auch die alte C Notation nutzen, welche zwar keinen
Schutz gegen Böcke implementiert, aber du willst ja gerade
Zweckentfremden, also genau die Böcke schießen.
Die Scoped Enums aus C++11 sind extra eingeführt worden, damit diese
NICHT mehr nach int gecastet werden.
Du willst das Gegenteil, also verwende die einfach nicht.
Veit D. schrieb:> Parameter eines Funktionsaufrufes etc., immer und überall einen Cast> machen.
Da liegt dein Fehler.
Der Parametertyp der Funktion muss den enum-Typ haben. Nicht integer.
MaWin O. schrieb:> Nicht integer.
ein uint8_t wird dann wohl eher mal ein byte oder ein unsigned char,
oder?
MaWin O. schrieb:> Funktion muss den enum-Typ haben
Dann wandert der Typecast in die Klasse, aber da dann ja nur ein
mal...ist sicher einfacher...
Martin S. schrieb:>> Nicht integer.>> ein uint8_t wird dann wohl eher mal ein byte oder ein unsigned char,> oder?
Ich verstehe die Frage nicht.
uint8_t ist ein integer.
Hallo,
ob ich die Verwendung in Kurzform erklären kann, ich versuche es einmal.
Selbstgebaute Platine mit H-Brücke und 8Bit I/O Portexpander usw.
Damit werden Gleisabschnitte auf der Modelleisenbahn ein- und
ausgeschalten.
Sieht dann bspw. so aus.
Die Nummern sind letztlich die Ausgänge 0...7 des verbauten
Portexpanders PCA8574.
Der Rest wird in der Klasse vom Portexpander gemacht.
Typangabe hilft nicht. Casten ist weiterhin erforderlich.
Also aktuell entweder
den Cast in die Klasse verschieben. Nur dann muss ich bei
Datentypänderung die Klasse anpassen. Ob das passieren kann weiß ich
aktuell nicht. Außer vielleicht "auto" funktioniert.
Oder ich erstelle eine Struktur für die "Magic Numbers". Jedenfalls
benötige ich letztlich Integer für die Funktionsparameter
hBridge.gleisOn(n) bzw. hBridge.gleisOff(n).
In allen Fällen habe ich noch den Cast denn man aktuell bei Cpp nicht
mehr machen soll.
Ich möchte das jetzt möglichst sauber haben wenn ich jetzt nochmal daran
rumfummel.
Wenn ich einzelne Funktionen schreibe mit sprechenden Namen, dann wären
darin als Parameter Magic Numbers drin. Gut, die könnte man
kommentieren.
Bspw. spontan
1
gleisA_einschalten(){hBridge.gleisOn(0);}
2
gleisA_auschalten(){hBridge.gleisOff(0);}
Weiß jetzt nicht ob das eine gute Idee ist. Was tun?
Das mit dem namespace Gleis von Ernst ginge eigentlich auch.
Veit D. schrieb:> Sieht dann bspw. so aus.hBridge.gleisOn((uint8_t)Gleis::GA);> hBridge.gleisOn((uint8_t)Gleis::GC);
Dann hat deine Funktion gleisOn() aber als Parametertyp nicht Gleis,
sondern wohl uint8_t. Warum, wenn sie doch ein Gleis haben will?
Wenn es unbedingt uint8_t sein soll, warum verwendest du dann enum
class? Das ist ja gerade dazu da, dass es sich nicht ohne Cast in einen
Integer konvertieren lässt.
Rolf M. schrieb:> Veit D. schrieb:>> Sieht dann bspw. so aus.hBridge.gleisOn((uint8_t)Gleis::GA);>> hBridge.gleisOn((uint8_t)Gleis::GC);>> Dann hat deine Funktion gleisOn() aber als Parametertyp nicht Gleis,> sondern wohl uint8_t.
Genau, sie hat uint8_t als Parametertyp.
> Warum, wenn sie doch ein Gleis haben will?
Dann wandert der Cast in die Klasse. Dann ist es aus den Augen aus dem
Sinn. Ist das eine gute Idee? Irgendwo und irgendwann muss ich aktuell
casten wenn ich dafür enum verwenden.
Edit:
Klasse angehangen, hoffe das hilft, wobei da noch mehr dran hängt
Veit D. schrieb:> Dann wandert der Cast in die Klasse. Dann ist es aus den Augen aus dem> Sinn. Ist das eine gute Idee?
Ja. Wenn die Funktion ein Gleis haben will und dies durch den
gleichnamigen enum repräsentiert wird, dann sollte sie diesen auch als
Parametertyp haben.
> Irgendwo und irgendwann muss ich aktuell casten wenn ich dafür enum> verwenden.
… wenn du enum class verwendest. Wenn du nur enum verwendest, geht es
ohne Cast.
Veit D. schrieb:> hBridge.gleisOn((uint8_t)Gleis::GC);
Ist die H-Brücke für mehr Geleise zuständig, als nur das eine?
Wie macht die das? (meine H-Brücken könnten sowas nicht)
MaWin O. schrieb:> uint8_t ist ein integer.
Ich wollte darauf hinaus das uint8_t kein Integer ist, also nicht im
C-Standard - uint8_t ist ein Alias für einen unsigned char...
Zumal ein "integer" minimal 16 Bit hätte und vorzeichenbehaftet wäre im
Gegensatz zu einem unsigned int...
Aber vielleicht ist das ja in C++ auch alles anders...
Martin S. schrieb:> MaWin O. schrieb:>> uint8_t ist ein integer.>> Ich wollte darauf hinaus das uint8_t kein Integer ist, also nicht im> C-Standard - uint8_t ist ein Alias für einen unsigned char...>> Zumal ein "integer" minimal 16 Bit hätte und vorzeichenbehaftet wäre im> Gegensatz zu einem unsigned int...
Was du meinst, ist ein int, nicht ein "integer". unsigned char ist kein
int, aber ein integer-Typ.
Veit D. schrieb:> Dann wandert der Cast in die Klasse. Dann ist es aus den Augen aus dem> Sinn. Ist das eine gute Idee? Irgendwo und irgendwann muss ich aktuell> casten wenn ich dafür enum verwenden.
Ja, die Transformation Gleis->int gehört in gleisOn.
Gleis ist ein abstraktes Objekt (enum), setPin ein konkreter HW-Pin.
Es gibt 3 Möglichkeiten:
* casten
* enum (ohne class)
* Umwandlungsfunktion, z.B. uint8_t GleisToPin(Gleis g)
Sobald Du mehrere H-Brücken hast oder Lücken, beginnen die Trixereien.
Eigentlich ist eine Umwandlungsfunktion sauberer, da die Reihenfolge der
Enums dann nicht der HW entsprechen müssen und der Code auf andere
Setups passt. Aber natürlich macht das in der Praxis jeder so.
Das Argument von gleisOn und gleisOff ist ein Gleis und sollte damit
logischerweise vom Type Gleis (enum) sein. Das Argument von setPin und
clearPin ist eine Pinnummer, also eine Integer-Zahl. Deswegen sollte der
Cast meiner Meinung nach genau dazwischen stattfinden, also so:
Das hat auch den Vorteil, dass der Cast nur an zwei Stellen und nicht
bei jedem Aufruf von gleisOn oder gleisOff benötigt wird.
Edit: Bruno war schneller und hat im Wesentlichen dasselbe geschrieben.
Bruno V. schrieb:> Ja, die Transformation Gleis->int gehört in gleisOn.>> Gleis ist ein abstraktes Objekt (enum), setPin ein konkreter HW-Pin.
Edit 2:
Das hier finde ich auch gut, vor allem dann, wenn die Konvertierung an
mehr als zwei Stellen im Code erfolgen soll:
Bruno V. schrieb:> * Umwandlungsfunktion, z.B. uint8_t GleisToPin(Gleis g)
Veit D. schrieb:> Ist das ohne casten möglich in C++? Irgendwo einmalig verstecken?> Ansonsten fiele mir nur ein eine Struktur anzulegen und alles von Hand> durch zu nummerieren.
Nur weil es plötzlich in C++ sehr viele neue Dinge gibt, muß man die
nicht krampfhaft nutzen, wenn die nicht passen.
Bleib beim enum ohne class, oder ändere die Logik so, daß die ohne deren
Integerwerte funktioniert. Beides zusammen ist Krampf.
Oliver
Yalu X. schrieb:> Das hier finde ich auch gut, vor allem dann, wenn die Konvertierung an> mehr als zwei Stellen im Code erfolgen soll:
Insbesondere kann man die Funktion ja inline realisieren, dann hat man
erstmal keinerlei Overhead. Falls, wie oben angedeutet, später doch mal
Gleisnummer != Pinnummer wird aus irgendeinem Grund, muss man nur noch
genau diese Funktion anpassen. Ist eine saubere logische Trennung der
einzelnen Funktionen.
Rolf M. schrieb:> Was du meinst
Was ich meine:
Der Datentyp eines uint8_t ist kein integer im C-Standard sondern ein
unsigned char, mehr wollte ich nicht sagen.
Ein integer Datentyp (int type) ist im C-Standard mindestens 16 Bit
lang!
Das ist zumindest das was ich mal gelernt habe. Es ist aber lange her
und vielleicht habe ich das ja auch falsch gelernt, alles denkbar.
Martin S. schrieb:> uint8_t ist kein integer im C-Standard
Es ist ein "integer type".
> sondern ein unsigned char
Nein. Das steht so nirgends, auch wenn beide häufig äquivalent sein
dürften.
Davon abgesehen, auch ein "unsigned char" ist ein "integer type":
"There are five standard signed integer types, designated as signed
char, short int, int, long int, and long long int." (Die "unsigned"
pendants gibt es natürlich auch alle.)
Martin S. schrieb:> Der Datentyp eines uint8_t ist kein integer im C-Standard sondern ein> unsigned char, mehr wollte ich nicht sagen.
Ein unsigned char ist ein integer.
> Ein integer Datentyp (int type) ist im C-Standard mindestens 16 Bit> lang!
Nochmal, du verwechselt da den Typ int mit einem integer allgemein.
Nicht jeder integer ist vom Typ int, denn es gibt mehrere integer-Typen.
int ist einer davon, unsigned char ein anderer. Für int gilt deine
Aussage, aber sie gilt nicht für jeden integer-Typ.
Martin S. schrieb:> Ein integer Datentyp (int type) ist im C-Standard mindestens 16 Bit> lang!
Was Rolf meint: "integer" ist einfach nur eine "Ganzzahl". Egal ob 1
oder 7 Byte, 3 oder 17 Bit, signed oder unsigned.
Prinzipiell sind alle integer Datentypen "gleich", nur der Wertebereich
verschieden.
"int" ist (wie Du schreibst) ein konkreter Typ, wenn auch nicht immer
gleich.
Oliver S. schrieb:> Nur weil es plötzlich in C++ sehr viele neue Dinge gibt, muß man die> nicht krampfhaft nutzen, wenn die nicht passen.
Hier paßts aber ziemlich gut, Herr Fortschrittsverweigerer.
Some O. schrieb:> Hier paßts aber ziemlich gut, Herr Fortschrittsverweigerer.
Nein, es passt nicht, wenn die enums nur etwas aufgemotzte Integer-Werte
sind, und die ganze Anwendung eigentlich auf Integer angewiesen ist.
Dafür ist enum class halt nicht so richtig passend. Die Alternative mit
einem enum in einem namespace wurde ja oben auch schon genannt.
Die eigentliche Frage ist aber doch, warum überhaupt die Integerwerte
benötigt werden.
Sauber wäre eine Implementierung, in der die Anwendung nur und
ausschließlich mit den enum-Werten arbeitet. Solange die Gleise keine
Reihenfolge haben müssen, oder eine Sortierung notwendig ist, braucht es
da überhaupt keine Integer (und selbst wenn, geht das). Da klappt das
dann auch problemlos mit class enum.
Wenn doch irgendwo nahe an der Hardware Integer benötigt werden, dann
gehört die Umwandlung an eine einzige Stelle in den "Treiber", der dann
direkt an den I/Os rumfummelt.
Oliver
Oliver S. schrieb:> Die eigentliche Frage ist aber doch, warum überhaupt die Integerwerte> benötigt werden.
Für die Adressierung der einzelnen Pins des Port-Expanders.
> Wenn doch irgendwo nahe an der Hardware Integer benötigt werden,
... was tatsächlich der Fall ist (s.o.) ...
> dann gehört die Umwandlung an eine einzige Stelle in den "Treiber",> der dann direkt an den I/Os rumfummelt.
... was bereits mehrfach vorgeschlagen wurde.
Yalu X. schrieb:>> dann gehört die Umwandlung an eine einzige Stelle in den "Treiber",>> der dann direkt an den I/Os rumfummelt.>> ... was bereits mehrfach vorgeschlagen wurde.
Wobei es natürlich nichtsdestotrotz hilfreich ist, wenn man sie gleich
so definiert, dass ihre Zahlenwerte „zufällig“ der Hardware entsprechen,
solange das möglich ist – das reduziert die Abstraktionsschicht rein
physisch dann auf Null.
>
Genau mein Humor:
Jemand redet von sprechenden Variablen und bringt dann ein Beispiel
dafür, wie man es genau nicht macht.
Woher soll der Leser deines Codes denn wissen, wofür GA, GB, GC usw.
steht?
Dazu noch ein Mischmasch aus deutsch und englisch:
NumberOfGleise
Das soll wohl "NumberOfTracks" heißen oder "AnzahlGleise" oder sowas in
der Art.
Jörg W. schrieb:> Wobei es natürlich nichtsdestotrotz hilfreich ist, wenn man sie gleich> so definiert, dass ihre Zahlenwerte „zufällig“ der Hardware entsprechen,
Kein Plan überlebt die erste Feindberührung...
Oliver
Reine c enums haben den Nachteil das sie global sichtbar sind.
Weil enums zur Kompilezeit konstant sind kannst du sie in C++ anonym in
eine struct verpacken.
Hallo,
wie Einige gelesen haben, ist die Zuordnung der Gleisabschnitte zur
Hardware (Portexpander) passend gemacht. Sprich die Verdrahtung
entspricht der enum Reihenfolge bzw. umgekehrt. Auf dem Bild unten links
ist die besagte Platine. Schaltplan wegen der H-Brückenfragen vs. Gleise
hängt auch dran.
Ich tendiere aktuell zur Lösung mit namespace enum oder struct enum.
Sodass ich auf den Cast, egal wo er stehen würde ganz verzichten kann.
Dennoch habe ich damit eine Kapselung, sodass man die enum Namen nicht
völlig wild verwenden kann. Betrifft sowieso nur mich der das schreibt
und lesen muss und der sich selbst vor groben Dummheiten schützen
möchte. ;-)
So eine Modelleisenbahn zu programmieren ist schon eine andere Nummer.
Es gibt immer wieder neue Ideen und Änderungen.
Dazu gleich noch eine Frage zum inkludieren. Auf die Gefahr hin das es
wohl keine eindeutige Antwort geben könnte.
Wie macht ihr das gewöhnlich, wenn eine Klasse eine andere Klasse
benötigt und inkludiert werden muss.
Inkludiert ihr direkt in der Klasse die diese benötigt oder schreibt ihr
alle #includes ins Hauptprogramm?
Wobei man hier wiederum das Problem haben kann das man tunlichts auf die
Reihenfolge achten muss.
Die Frage dreht sich eher darum ob man allen Lesern im Hauptprogramm
zeigt, welche Klassen benötigt werden oder ob der Leser sich diese
Information bei Bedarf in den Header etc. selbst zusammensuchen soll?
Oliver S. schrieb:> Some O. schrieb:>> Hier paßts aber ziemlich gut, Herr Fortschrittsverweigerer.>> Nein, es passt nicht, wenn die enums nur etwas aufgemotzte Integer-Werte> sind, und die ganze Anwendung eigentlich auf Integer angewiesen ist.
Du kannst das gerne andes sehen, aber ich halte Typsicherheit und
Lesbarkeit für wichtig. Und eine Enum an dieser Stelle zu nutzen, hilft
beiden Aspekten. Sonst kann man auch Magic Numbers nehmen und mit ein
bissel Unkonzentriertheit bekommt man Magic Smoke.
Veit D. schrieb:> Inkludiert ihr direkt in der Klasse die diese benötigt oder schreibt ihr> alle #includes ins Hauptprogramm?
Natürlich in der Header-Datei der Klasse bzw. der Implementierungsdatei
der Klasse, je nachdem wo der vollständige Typ gebraucht wird. Um Zyklen
aufzulösen, kann man Header mit nur unvollständigen Deklarationen machen
(s.a. <iosfwd> statt <iostream>).
An anderer Stelle (im "Hauptprogramm") darf der Anwender der Klasse
nicht über eine Reihenfolge nachdenken müssen!
Hallo,
okay, Danke euch. Habe nochmal inne gehalten und mich für struct enum
entschieden. Ich denke das ist der passende Weg für mich. Damit kann ich
solche Konstrukte wie bspw.
Das hilft mir aufzuräumen, Typsicherheiten beizubehalten und es lesbarer
zu bekommen. Zog sich ja noch weiter durch das Programm. include Frage
ist auch beantwortet. Danke an alle.
Bevor der Nächste meckert, die Magic Number 1 bzw. -1 kommt auch noch
weg für CW bzw. CCW. Ist ja alles noch "under construction" inkl.
Spieltrieb zwischendurch. :-)
Wenn das struct "Gleis" heisst, ist es unlogisch für Gleis A dann das
enum GA zu nennen. Besser wäre Gleis.A. Das "G" in "GA" ist redundant.
Gleiches bei den Weichen.
Hallo,
du hast völlig recht Wilhelm. Nun kommt jedoch das aber. Kannst du dich
erinnern an meinen Thread zu den #defines? Das Problem ist das im DxCore
Namen wie A1 für Pins schon mittels #define existieren und dann
kollidiert das alles mit meinen Namen, wegen Präprozessor. Deswegen bin
ich den verschmerzbaren Umweg gegangen einen Anfangsbuchstaben zu
wählen. Ich habe mich daran schnell gewöhnt.
Veit D. schrieb:> Hallo,>> du hast völlig recht Wilhelm. Nun kommt jedoch das aber. Kannst du dich> erinnern an meinen Thread zu den #defines? Das Problem ist das im DxCore> Namen wie A1 für Pins schon mittels #define existieren und dann> kollidiert das alles mit meinen Namen, wegen Präprozessor. Deswegen bin> ich den verschmerzbaren Umweg gegangen einen Anfangsbuchstaben zu> wählen. Ich habe mich daran schnell gewöhnt.
Naja, das gehört ja ohnehin zu Deiner enum und ist durch diese
Zugehörigkeit doch schon absolut eindeutig bezeichnet?! Das ist schon
was anderes als die eher stumpfen Textersetzungen eines Präprozessors,
methinks.
Ein T. schrieb:> Naja, das gehört ja ohnehin zu Deiner enum und ist durch diese> Zugehörigkeit doch schon absolut eindeutig bezeichnet?! Das ist schon> was anderes als die eher stumpfen Textersetzungen eines Präprozessors,> methinks.
Nun, wenn irgendwo ein #define mit dem Namen existiert, dann kann dieser
Name nirgendwo mehr für was anderes verwendet werden, auch nicht als
Enum-Wert.
Veit D. schrieb:> Damit kann ich> solche Konstrukte wie bspw.> ...> ControlWeiche weiche [] {> { 1, (uint8_t)Servoname::WBCL, (uint8_t)Sensorboard::SD1,> (uint8_t)Sensorboard::SC1, (uint8_t)Sensorboard::SB1},> ...> ändern in> ...> ControlWeiche controlWeiche [4] {> { 1, servo.WBCL, sensor.SD1, sensor.SC1, sensor.SB1},
Das kannst du auch, wenn du enum class Typen verwendest. ControlWeiche
darf dann keine Integer, sondern deine enum class Typen enthalten. Nur
der Treiber, der dann mit den Daten aus CotrolWeiche die I/Os bedient,
muß wissen, welche Pins angesteuert werden müssen. Der macht dann die
Umsetzung von der enum class Typen in Integer. Alle Ebenen da drüber
brauchen das nicht.
Oliver
Veit D. schrieb:> Habe nochmal inne gehalten und mich für struct enum> entschieden.
Finde ich gut. In C erzwinge ich die strenge Typprüfung oft so (auch
wenn enums selber global sind)
1
enumeGleis{GA,GB,GC,GD,GE,GF,GZ,NumberOfGleise};
2
3
typedefstruct
4
{
5
enumeGleisnr;
6
}GleisType;
7
8
voidgleisOn(GleisTypeg){icGleis.setPin(g.nr);}
9
voidgleisOff(GleisTypeg){icGleis.clearPin(g.nr);}
10
11
intmain(void)
12
{
13
GleisTypeGleisWest={GC};
14
SensorTypeSensorOben={GC};/* <-- nur Warnung */
15
16
gleisOn(GleisWet);
17
gleisOn(SensorOben);/* <-- Compilerfehler, selbst mit Warnungen aus */
18
}
Bei Gleisen mag das wie Overkill aussehen, verhindert aber das ein
Sensor übergeben wird. Vielleicht wird es anschaulicher, wenn man im
Programm Auftrags- und Kundennummer hat und beide u32 sind. Irgendwann
hat jemand in einem Sub-Block nur noch "Nr" und der nächste Maintainer
ruft damit "CheckOrder(Nr)" statt "CheckCustom(Nr)" auf.
Veit D. schrieb:> wie Einige gelesen haben, ist die Zuordnung der Gleisabschnitte zur> Hardware (Portexpander) passend gemacht. Sprich die Verdrahtung> entspricht der enum Reihenfolge bzw. umgekehrt.
Hardware hat die Eigenschaft, sich während der Entwicklung mehrfach zu
ändern oder später erweitert zu werden, da kann einem eine feste
Zuordnung schnell auf die Füße fallen.
Ich versuche daher, die Pinzuweisung variabel zu halten und nur in einem
Header zuzuweisen. Dann kann man auch beim Routen der Platine noch Pins
vertauschen, um Vias zu sparen. Weniger Vias machen das Layout
übersichtlicher bei der Fehlersuche und beim Patchen.
Für Pins, die über Expander bedient werden, lege ich daher
Schattenvariablen im RAM als Bit-Struct an, die ausschließlich verwendet
werden.
Hier mal ein Beispiel:
Definition der Bitstruct:
Der AVR-GCC kann die Bitzugriffe sehr gut optimieren, d.h. hält die
Bytes in Registern, wenn sie mehrfach benötigt werden.
Mit Enums dürfte das aber ein riesen Codemonster werden (bezüglich
Schreibarbeit als auch Binärcode).
update_all(); schreibt und liest die Expander, d.h. man muß nicht für
jedes einzelne IO-Bit Zeit verschwenden. Eingänge bleiben dadurch
während des gesamten Zyklus konsistent. Obendrein dürfen Outputs
mehrfach geändert werden, ohne daß es zu Glitches kommt.
Rolf M. schrieb:> Nun, wenn irgendwo ein #define mit dem Namen existiert, dann kann dieser> Name nirgendwo mehr für was anderes verwendet werden, auch nicht als> Enum-Wert.
True, aber wer würde denn sowas machen? :-)
Veit D. schrieb:> Inkludiert ihr direkt in der Klasse die diese benötigt oder schreibt ihr> alle #includes ins Hauptprogramm?
Immer dort inkludieren, wo es benötigt wird.
Veit D. schrieb:> Inkludiert ihr direkt in der Klasse die diese benötigt oder schreibt ihr> alle #includes ins Hauptprogramm?
Jede Header (*.h*-Datei) enthält alle #includes, die es braucht. Auch
wenn das nicht zwingend ist, sollte es so sein. Natürlich mit
#Include-Guard in welcher Form auch immer.
Jede Klasse (*.c*-Datei) enthält alle #includes, die es braucht. Direkt
oder Indirekt. (Zwingend, logisch)
Wenn ich irgendwo die Klasse "Gleis" verwenden will, dann muss "Gleis.h"
reichen. Wenn auch "Schiene.h" oder "Gleisbett.h" benötigt werden, muss
Gleis.h die mitbringen.
Ausnahme sind manchmal projektspezifische Konfigurationsdateien
("config.h") am Anfang jeder Applikations-*.c*.
Bei Instanzen (GleisA in "GleisA.h", GleisB in "GleisB.h") kann es sein,
dass die nur in einer Datei (main.c) benötigt werden und alle anderen
mit der Oberklasse "Gleis.h" zufrieden sind. Dann ist Gleis.h in
GleisA.h und nicht umgekehrt. Und beide parallel in main.c.
Hallo,
Ein T. schrieb:> Rolf M. schrieb:>> Nun, wenn irgendwo ein #define mit dem Namen existiert, dann kann dieser>> Name nirgendwo mehr für was anderes verwendet werden, auch nicht als>> Enum-Wert.>> True, aber wer würde denn sowas machen? :-)
Es ist ist wie es ist. An dem DxCore fummel ich nicht dran rum. Der wird
nur angewendet. Mehr möchte ich dazu gar nicht schreiben. Das Thema ist
durch.
// -----------------------------------------------------------------
Johann L. schrieb:> Eigentlich brauchst du doch eine Abbildung, die Gleise auf H-Brücken> abbildet? Also sowas wie>>
1
>constexpruint8_tGleisToHBridge(enumeGleisgleis)
2
>{
3
>switch(gleis)
4
>{
5
>caseGA:return123;
6
>caseGB:return42;
7
>...
8
>}
9
>}
10
>
Muss ich mir überlegen. Hier prasseln gefühlt Hunderte weitere Ideen
ein. ;-)
// -----------------------------------------------------------------
Oliver S. schrieb:> Veit D. schrieb:>> Damit kann ich>> solche Konstrukte wie bspw.>> ...>> ControlWeiche weiche [] {>> { 1, (uint8_t)Servoname::WBCL, (uint8_t)Sensorboard::SD1,>> (uint8_t)Sensorboard::SC1, (uint8_t)Sensorboard::SB1},>> ...>> ändern in>> ...>> ControlWeiche controlWeiche [4] {>> { 1, servo.WBCL, sensor.SD1, sensor.SC1, sensor.SB1},>> Das kannst du auch, wenn du enum class Typen verwendest. ControlWeiche> darf dann keine Integer, sondern deine enum class Typen enthalten. Nur> der Treiber, der dann mit den Daten aus CotrolWeiche die I/Os bedient,> muß wissen, welche Pins angesteuert werden müssen. Der macht dann die> Umsetzung von der enum class Typen in Integer. Alle Ebenen da drüber> brauchen das nicht.und auch Bruno V. schrieb:
Den Datentyp strenger zu setzen muss ich mir auch überlegen. Die Idee
ist erstmal nicht falsch. Schützt vor Dummheiten. ;-)
// -----------------------------------------------------------------
Peter D. schrieb:> Veit D. schrieb:>> wie Einige gelesen haben, ist die Zuordnung der Gleisabschnitte zur>> Hardware (Portexpander) passend gemacht. Sprich die Verdrahtung>> entspricht der enum Reihenfolge bzw. umgekehrt.>> Hardware hat die Eigenschaft, sich während der Entwicklung mehrfach zu> ändern oder später erweitert zu werden, da kann einem eine feste> Zuordnung schnell auf die Füße fallen.> Ich versuche daher, die Pinzuweisung variabel zu halten und nur in einem> Header zuzuweisen.
Eigentlich bin ich absolut flexibel. Bin jetzt ganz verunsichert. Falls
ich die Verdrahtung ändere, was wohl nicht passieren wird, muss ich nur
die Namen im enum umsortieren. Denn die Reihenfolge entspricht der
Bitnummer 0...7 vom I/O des PortExpanders. Das hier meine ich, kann ich
jederzeit umsortieren wenn es denn sein müßte. Bei den Sensoren 0...15,
sind noch paar frei.
Johann L. schrieb:> Eigentlich brauchst du doch eine Abbildung, die Gleise auf H-Brücken> abbildet?
Da stellt sich mir die Frage, warum überhaupt erstmal Enums anlegen,
wenn dann doch wieder alles umständlich umgemappt werden muß. Das kostet
nur unnütz Schreibarbeit und Code und verschlechtert die Lesbarkeit
rapide.
Man kann doch gleich sprechende Symbole anlegen, denen sofort die
konkrete Byteadresse und Bitnummer mitgeteilt wird.
Johann L. schrieb:> case GA: return 123;> case GB: return 42;
Sowas mag ich nun überhaupt nicht, magische Nummern, da geht doch der
Überblick komplett verloren. Es sollte schon leicht zu erkennen sein,
welcher IO-Port und welches Bit da angesprochen wird. Ansonsten sucht
man sich dumm und dämlich nach Zuordnungsfehlern.
Hallo,
ich habe das Gefühl aktuell dreht man sich im Kreis. Nicht böse gemeint.
Enums, weil das für mich die kürzeste Form ist sprechende Symbole
abzubilden. Die werden automatisch durchnummeriert in der Reihenfolge
wie ich diese benötige.
ist mehr Tipparbeit und Fehlerträchtig, empfinde ich.
Auch die Bitstruct Idee habe ich mir überlegt. Jedoch ist es auch mehr
Schreibaufwand ähnlich dem obigen struct Gleis. Ich sehe da leider
keinen Vorteil drin. Wenn man sich hierbei vertippt sucht man sicherlich
auch lange Zeit den Fehler. Ich wüßte jetzt ehrlich gesagt nicht was
diese eine Zeile
1
structGleise{enum{GA,GB,GC,GD,GE,GF,GZ,Anzahl};};
toppen könnte. Hier steckt alles drin was benötigt wird. Der "Wert" wird
dem Objekt übergeben was den Portexpander verwaltet und der weiß was zu
tun ist. Hier muss nichts umgemappt werden. Ist genauso direkt
verwendbar wie Bitstrukturen. Vielleicht habe ich einen Tunnelblick,
vielleicht haben andere einen Tunnelblick. Kann ich nicht beurteilen.
Nur eins weiß ich. Mir gefallen die geänderten struct-Enums ohne Cast
und dabei werde ich bleiben. Einfach zu schreiben und sicher anzuwenden.
Nach aller vorheriger Konfigurationsorgie ist die Hauptschleife dafür
allein gesehen praktisch wie leer.