GCC
Ich will einige im Controller fortlaufende Nummerierungskonstanten aus
den entsprechenden Modulheadern herausnehmen und in eine "MagicIds.h"
packen.
Z.B. standen vorher die Konstanten
1
#ifndef INCL_MAGICIDS_H
2
#define INCL_MAGICIDS_H
3
4
// Magic EEPROM page IDs
5
#define EE_PAGE_DRIVEPARAMS 0
6
#define EE_PAGE_LIDARPARAMS 1
7
#define EE_PAGE_PHYSICSPARAMS 2
8
...
9
#endif
über die einzelnen Header verteilt.
Die habe ich nun zusammengepackt, damit ich nicht jedes mal neu im
gesamten Projekt gucken muss, wenn ich eine neue EEPROM-Seite
implementieren will.
Jetzt kribbelt es mich allerdings irgendwie in den Fingern beim Einfügen
der include-Direktiven in den einzelnen Header-Dateien.
Include ich jetzt korrekterweise die MagicIds.h aus den Headerdateien
(Drive.h/ Lidar.h/ Physics.h) oder aus den verwendenden c-Files?
Funktionieren würde beides, jedoch bin ich mir noch nicht ganz über
Vor-/Nachteile im Klaren.
Horst S. schrieb:> Include ich jetzt korrekterweise die MagicIds.h aus den Headerdateien> (Drive.h/ Lidar.h/ Physics.h) oder aus den verwendenden c-Files?
Verwenden die Headerdateien Drive.h Lidar.h Physics.h vielleicht
Konstanten, die nun in MagicIds.h definiert werden?
Wenn ja, gehört der Include definitv in die Header-Dateien. Sonst
müsstest Du ja auch darauf achten, dass Du in *.c die richtige
Reihenfolge inhältst (erst MagicIds.h und dann z.B. erst Drive.h).
Wenn nein, dann gehört der Include in *.c.
Merke: Include immer dort, wo Du das Include auch brauchst.
Include-Guard nicht vergessen!
Frank M. schrieb:> Verwenden die Headerdateien Drive.h Lidar.h Physics.h vielleicht> Konstanten, die nun in MagicIds.h definiert werden?
Tun sie nicht, aber das Argument mit der Reihenfolge ist gut (hab ich
z.B. mal wieder komplett ausgeblendet gehabt)
Es wird sogar so sein, dass ich bei den externen Transferfunktionen
(Parameterblöcke über Bluetooth lesen/schreiben) in einem Modul jetzt
nur noch die MagicIDs.h benötige, da hatte ich vorher sämtliche
Referenzen wegen der über die einzelnen Header verteilten Konstanten
drin.
Irgendwie trotzdem hässlich: Zuerst versucht man krampfhaft, den Scope
auf die Module über die Header dicht zu halten und anschließend wird die
Sache sperrig und achlecht wartbar, dann landet man doch wieder in so
'nem globalen Murks. Vielleicht ist das die eigentliche Ursache meines
Fingerkribbelns.
Horst S. schrieb:> Tun sie nicht, aber das Argument mit der Reihenfolge ist gut (hab ich> z.B. mal wieder komplett ausgeblendet gehabt)>> Es wird sogar so sein, dass ich bei den externen Transferfunktionen> (Parameterblöcke über Bluetooth lesen/schreiben) in einem Modul jetzt> nur noch die MagicIDs.h benötige, da hatte ich vorher sämtliche> Referenzen wegen der über die einzelnen Header verteilten Konstanten> drin.
Beide Argumente sprechen für das Include in *.c.
> Irgendwie trotzdem hässlich: Zuerst versucht man krampfhaft, den Scope> auf die Module über die Header dicht zu halten und anschließend wird die> Sache sperrig und achlecht wartbar, dann landet man doch wieder in so> 'nem globalen Murks.
Ja, das kenne ich. Da kann ich Dir nur empfehlen: So wenig global wie
möglich. Vieles lässt sich auch static innerhalb eines Moduls abfackeln.
Dann können die ausschließlich dafür benötigten Konstanten oder
Datenstrukturen raus aus den Headern und rein in das xxx.c.
Die Header sollten nur das Interface zum Modul deklarieren und mehr
nicht.
Die Headerdateien werden das eingebunden, wo sie gebraucht werden.
Brauchst du sie für Typdefinitionen oder Deklarationen in der .h Datei,
musst du sie da einbinden.
Wenn du die Infos nur in Code brauchst, dann werden sie in der .c
eingebunden.
Horst S. schrieb:> Frank M. schrieb:>> Merke: Include immer dort, wo Du das Include auch brauchst.>> Kann ich als Faustregel mit leben!!!
Na hoffentlich.
ich sehe das ganz anders. Mit automatisch inkludierten Rattenschwänzen
von Headerdateien lebt es sich zwar recht bequem, aber häufig genug
inludiert man ungewollt Zeugs, was einem später schwer auffindbare Bugs
beschert.
Deshalb bin ich gar kein Freund von sowas.
W.S.
W.S. schrieb:> ich sehe das ganz anders. Mit automatisch inkludierten Rattenschwänzen> von Headerdateien lebt es sich zwar recht bequem, aber häufig genug> inludiert man ungewollt Zeugs, was einem später schwer auffindbare Bugs> beschert.
Beispiel für solch einen Bug?
Wenn ich schreibe:
Include immer dort, wo Du das Include auch brauchst.
wieso meinst Du dann, dass man "ungewollt Zeugs" includiert? Man
includiert doch nur das, was man braucht. Mehr kann man aus der
Faustregel nicht herauslesen. Wenn da bei Dir weiteres "ungewolltes
Zeugs" in Deinen Header-Dateien herumschwirrt, dann sind Deine
Header-Dateien eventuell falsch strukturiert.
Horst S. schrieb:> Was spricht gegen folgende Lösung?> [...]> Würde das Chaos der fortlaufenden Nummerierung lösen UND den> Grundgedanken erhalten: Binde EINEN Header für EIN Modul ein.
Diese "Lösung" enthält mir zuviele wenn und aber, nämlich die vielen
#ifdefs. Ich verstehe nämlich nicht, warum Du diese drinhast. Traust Du
Deiner eigenen Software nicht ("ich könnte das irgendwo schon mal
definiert haben") oder hast Du dafür einen organisatorischen Grund?
Das sieht mir jedenfalls arg fehlerträchtig aus. Wenn ich etwas über den
Preprozessor versehentlich zweimal mit #define definiert habe, will ich
auch die Fehlermeldung dazu sehen, damit ich diesen Missstand beheben
kann!
> Würde das Chaos der fortlaufenden Nummerierung lösen
Verstehe ich nicht. Muss da bedingt fortlaufend numeriert werden?
Schon mal enum dazu probiert?
Mein Fazit:
Wenn MagicIds.h lediglich headerübergreifende Konstanten enthält,
include es in den C-Quellen und nicht über komplizierte Konstruktionen
in den Headern.
Das Problem ist, wie immer, mit diesen "Halden":
Ich weiß morgen nicht mehr, was ich hinzufüge UND ich weiß morgen nicht
mehr, was ich gelöscht habe. Wenn ich morgen Lidar.c/.h aus dem Projekt
entferne, hätte ich gerne eine vollständige Änderungs- / Fehlerliste vom
Compiler.
Heute Nacht kam auf jeden Fall das Kribbeln wieder zurück, als ich mir
vorstellte, dass in den Transferfunktionen Daten für ein nicht
vorhandenes Lidar-Modul weiter unterstützt wird, weil ich die MagicIds.h
nicht angepasst habe. Das wäre für mich eine Leiche.
Insofern fand ich die Lösung oben jetzt gar nicht mal abwegíg.
Horst S. schrieb:> Heute Nacht kam auf jeden Fall das Kribbeln wieder zurück, als ich mir> vorstellte, dass in den Transferfunktionen Daten für ein nicht> vorhandenes Lidar-Modul weiter unterstützt wird, weil ich die MagicIds.h> nicht angepasst habe. Das wäre für mich eine Leiche.
Ich stecke da zuwenig in Deinem Source drin. Müssen die zwingend
fortlaufend nummeriert sein oder reicht es, wenn sie alle voneinander
unterschiedlich sind? Wenn letzteres, benutze Nummernkreise.
Das
#ifdef INCL_DRIVE_H
...
#endif
ist ja noch in Ordnung, aber das:
#ifndef EE_PAGE_DRIVEPARAMS
#define EE_PAGE_DRIVEPARAMS 0
#endif
halte ich für gefährlich. Warum nicht einfach:
#define EE_PAGE_DRIVEPARAMS 0
Durch das #ifdef erlaubst Du mehrdeutige Mehrfachdefinitionen. Zum
Beispiel könnte woanders noch stehen:
#define EE_PAGE_DRIVEPARAMS 4711
Der Compiler wird Dich wegen dem #ifdef dann nicht davor warnen, genau
das zu tun. Und schon hast Du zwei unterschiedliche Werte für ein- und
dieselbe Konstante. Viel Spaß bei der Fehlersuche!
Versuche immer, soviele Fehler wie möglich vom Compiler zu bekommen.
Dieser #ifdef-Nonsense erinnert mich auch immer wieder an:
#ifndef F_CPU
#define F_CPU 8000000UL
#endif
bei AVR-Projekten. In Makefile (oder noch schlimmer: in xxx.h) wird dann
12000000 als F_CPU gesetzt und die Verwirrung ist dann für viele
Anfänger perfekt, die den Source nutzen wollen.
"Gilt jetzt 8MHz oder was anderes"?
Richtig wäre:
#ifndef F_CPU
#error F_CPU not defined
#endif
Aber auf jeden Fall keine Wenn-Dann-Lösung.
Neee, Dein #ifdef (ohne n) ist mein #ifndef (mit n), also ein
IF NOT DEFINED. (Hoffe, das ist Dein Problem).
Ich habe etwas gegrübelt, ob ich es brauche. Korrekterweise brauche ich
es, wenn zuerst Drive.h und anschließend Lidar.h includiert wird.
Beim includieren von Lidar.h wird die Direktive für Drive.h positiv
bewertet und EE_PAGE_DRIVEPARAMS erneut zugewiesen. Das ist zwar wieder
der gleiche Wert, aber diese Unsauberkeit durch ein #ifndef zu
unterdrücken, frisst kein Brot.
So zumindest mein Gedanke.
Horst S. schrieb:> Neee, Dein #ifdef (ohne n) ist mein #ifndef (mit n), also ein> IF NOT DEFINED. (Hoffe, das ist Dein Problem).
Ist doch egal, ob die Abfrage mit oder ohne NOT ist, das Problem bleibt
dasselbe, siehe unten.
> Ich habe etwas gegrübelt, ob ich es brauche. Korrekterweise brauche ich> es, wenn zuerst Drive.h und anschließend Lidar.h includiert wird.
Sorry, ich kenne Deinen Source nicht. Heisst das, wenn Du die
Reihenfolge von Drive.h und Lidar.h rumdrehst, brauchst du sie nicht?
Das ist suboptimal und fehlerträchtig.
> Beim includieren von Lidar.h wird die Direktive für Drive.h positiv> bewertet und EE_PAGE_DRIVEPARAMS erneut zugewiesen.
Das ist schlecht. Ändert sich mal eine Konstante, musst Du immer an zwei
Stellen ändern. Vergisst Du eine, dann gibts Ärger.
> Das ist zwar wieder> der gleiche Wert, aber diese Unsauberkeit durch ein #ifndef zu> unterdrücken, frisst kein Brot.
Das ist noch viel schlechter! Wenn der Wert gleich ist, meckert der
Preprozessor sowieso nicht bei einer Mehrfachdefinition. Wenn die Werte
aber ungleich sind, dann meckert er - zu Recht! Und genau diesen Fall
gilt es zu detektieren!
Durch Dein #ifndef unterdrückst Du genau diesen Mechanismus, der Dir
eigentlich den Arsch retten würde.
> So zumindest mein Gedanke.
Vergiss ihn.
Quatsch!
Überlege Folgendes:
- xxx.c includiert Lidar.h und Drive.h, benötigt aber die Konstanten aus
MagicIDs.h
- Lidar.h includiert MagicIds.h. Beim includieren nach o.g. Pattern wird
EE_PAGE_LIDARPARAMS definiert.
- Anschließend wird durch den Verweis auf Drive.h auch
EE_PAGE_DRIVEPARAMS definiert. Trotzdem die Konstante INCL_LIDAR_H
bereits definiert ist (durch die Referenz auf Lidar.h), wird die
Konstante EE_PAGE_LIDARPARAMS NICHT ERNEUT definiert, weil sie schon da
ist (der Frist-Kein-Brot-Mechanismus)
Das Spiel kannst Du auch umdrehen und zuerst Drive.h und anschließend
Lidar.h einbinden. Die Reihenfolge ist egal. Eine Konstante wird
a) nur dann definiert, wenn der entsprechende Header eingebunden ist
b) nicht noch einmal definiert, wenn sie bereits definiert wurde. Sollte
mich irgendwann mal der zweifellos dämliche Gedanke überkommen, sie
anderswo erneut zu definieren, ist sicher die Stelle im Modulheader vor
der #Include "MagicIds.h"-Anweisung der richtige Ort zum Überschreiben
der Konstante in MagicIds.h.
Was ist an diesem Pattern fehlerträchtig? Das Include der MagicIds.h aus
den einzelnen Modulheadern heraus stellt sicher, dass die Bedingung zum
definieren der zugehörigen Konstanten VORHER erfüllt wurde.
Die andere Geschichte (die über Nacht gekribbelt hat):
Wenn ich statt Lidar.h/ Drive.h aus xxx.c auf MagicIds.h verweise, was
passiert, wenn ich Lidar.c/ .h aus dem Projekt entferne?
Bekomme ich dann einen Compilerfehler? NEIN! Ich muss daran denken, in
MagicIds.h die entsprechende Konstante herauszunehmen.
Kann ich vielleicht dann noch über die Anweisung
#ifdef INCL_LIDAR_H
...
#endif
in MagicIds.h abfangen, dass ohne LIDAR.H auch die Konstante
EE_PAGE_LIDARPARAMS nicht definiert wird?
Nein, ich müsste durch #include "Lidar.h" erst sicherstellen, dass
"INCL_LIDAR_H" auch vorher definiert wurde. Dann müsste ich also
andersherum alle Modulheader in MagicIds.h einbinden und das, so finde
ich, ist richtig krank.
Insofern: Der "Faustregel" "Include immer dort, wo Du das Include auch
brauchst." als "Faustregel", stimme ich zu. Hier allerdings würde ich
eine Ausnahme machen, einfach, weils Sinn macht.
Horst S. schrieb:> Quatsch!>> Überlege Folgendes:> - xxx.c includiert Lidar.h und Drive.h, benötigt aber die Konstanten aus> MagicIDs.h> - Lidar.h includiert MagicIds.h. Beim includieren nach o.g. Pattern wird> EE_PAGE_LIDARPARAMS definiert.> - Anschließend wird durch den Verweis auf Drive.h auch> EE_PAGE_DRIVEPARAMS definiert. Trotzdem die Konstante INCL_LIDAR_H> bereits definiert ist (durch die Referenz auf Lidar.h), wird die> Konstante EE_PAGE_LIDARPARAMS NICHT ERNEUT definiert, weil sie schon da> ist (der Frist-Kein-Brot-Mechanismus)
Also erstens: du reagierst unangemessen, wenn du auf all die
Einlassungen, die von durchaus großer Erfahrung zeugen, mit "Quatsch!"
reagierst.
Zweitens:
Du programmierst unsauber. Das sieht man bei deiner hoffnungslos
konfusen Erklärung, die ich grad oben zitiert habe.
Mach es besser.
Das macht man, indem man die verschiedenen Funktionsteile sauber
voneinander kapselt, so daß sie sich nicht ins Gehege kommen können. Ja,
das geht. Und so wie ich das sehe, ist dein gesamtes Konstrukt mit EE
PageNums und Magics nicht wirklich gut und tragfähig. Es sieht mir eher
so aus, als ob du mit einem sehr einfachen Programm gestartet hast und
dann im Laufe der Entwicklung immer noch hie und da einen Rucksack
dazugeschrieben hast. So wie das Haus der Weasleys bei Harry Potter. Da
fehlt das Gesamtkonzept.
Also:
1. Schreib in Headerdateien nur das hinein, was die übergeordneten
Programmteile zum Benutzen des zugehörigen Moduls wirklich brauchen -
und nicht mehr.
2. Mache keine Import-Export-Geschäfte. Also nicht sowas:
#include "lala.h" // dort ist juhu definiert
#ifndef XYINCLUDED
#define XYINCLUDED
#define Blabla (juhu+1) // aus lala.h
#endif
Wenn du von einer 'grundlegenderen' Datei (scheußliches Wort) etwas
importierst, dann nur für den internen Gebrauch innerhalb des Moduls.
Jeder andere Modul soll sich diese Definition gefälligst selber holen.
Bei dir kommt aus deinem Import-Export-Geschäft (--->
EE_PAGE_LIDARPARAMS) eben etwas extrem fehleranfälliges bei heraus.
W.S.
Horst S. schrieb:> Quatsch!>> Überlege Folgendes:> - xxx.c includiert Lidar.h und Drive.h, benötigt aber die Konstanten aus> MagicIDs.h
Erstens: warum würde xxx.c so'ne magicId brauchen? Definitiv nicht um
auf EEPROM Daten vom Lidar oder Drive zu zugreifen; dafür sollten die
Lidar und Drive-Module Funktionen bereitstellen.
Falls xxx.c selber eine EEPROM Page hat, soll xxx.c auch MagicIDs.h
includieren und sich da nicht darauf verlassen, dass andere .h-Dateien
das schon machen. (Das ist übrigens genau der Rattenschwanz an "was man
sich reinzieht" worauf sich W.S. weiter oben bezog)
> - Lidar.h includiert MagicIds.h. Beim includieren nach o.g. Pattern wird> EE_PAGE_LIDARPARAMS definiert.
Lidar.h soll aber MagicIDs.h nur includieren wenn er die Konstanten aus
MagicIDs.h selber braucht. Wenn in Lidar.h nirgendwo Text
EE_PAGE_LIDARPARAMS auftaucht, soll Lidatr.h auch nicht MagicIDs.h
inkludieren! Faustregel!
> Die andere Geschichte (die über Nacht gekribbelt hat):> Wenn ich statt Lidar.h/ Drive.h aus xxx.c auf MagicIds.h verweise, was> passiert, wenn ich Lidar.c/ .h aus dem Projekt entferne?
Nix. Ausser alle eine Reihe an Fehlermeldungen vom Linker, dass Verweise
auf Funktionen aus Lidar.c nicht "resolved" werden können :-)
> Bekomme ich dann einen Compilerfehler? NEIN! Ich muss daran denken, in> MagicIds.h die entsprechende Konstante herauszunehmen.
Genau. Das ist nämlich Projektverwaltung und keine Compileraufgabe.
Ich würde das über das Makefile organsieren, z.B mit einer USE_LIDAR
Define/Parameter. In MagicIDs.h würde ich dann die EE Pages in einem
enum packen, statt einer Reihe aus #defines. Im MagicIDs.h kommt dann
1
typedefenum{
2
#if defined(USE_DRIVE)
3
EE_PAGE_DRIVEPARAMS,
4
#endif
5
#if defined(USE_LIDAR)
6
EE_PAGE_LIDARPARAMS,
7
#endif
8
...
9
}EE_PAGE_MAGIC_ID;
Und so werden nur die wirklich gebrauchte EE-Pages definiert und kriegen
automagisch die "richtige" Nummer.
So, vielleicht erst einmal, "Quatsch", korrigiere ich zu "Quatsch, Du
musst mich komplett Missverstanden haben". Mag an meiner Schreibe
liegen, dass dieses Missverständnis überhaupt aufgetreten ist.
Ich habe weder ein Reihenfolgen- ein Scope-, noch ein
Verständnisproblem. Präcompilerdirektiven verstehe ich als nutzbares
Werkzeug und ich habe überhaupt keine Angst vor Fehlern, die sich
diesbezüglich wie von Zauberhand in meinen Code einschleichen könnten.
Jungs, in welchem Graben habt Ihr gesessen, dass Ihr das für Teufelszeug
haltet? Nee, ehrlich und wirklich ganz freundlich gefragt: Ich hatte da
noch nie Probleme mit. Mir ist NICHT klar, was da schief gehen könnte.
Das führt bei mir auch nicht zu include-Schlangenwürsten. Es ist eine
gezielte, dokumentierte und einmalige Ausnahme vom Faustregelkonzept.
Diese Ausnahme ändert funktional NICHTS in Bezug auf Scope oder
Modularität. Sie ändert nur die SICHT auf die Konstanten so, dass ich
sie ALS LISTE LESEN UND BEARBEITEN kann.
WO ZUM HENKER IST DAS PROBLEM? ICH SEH'S NICHT!
(Ich weiß, ich bin das Problem. Sonst ist es mein Sohn, aber heute bin
ich es)
Erstens brauchst du nicht schreien. Kleinbuchstaben lsen sich genau so
gut, wenn nicht sogar besser.
Zweitens, wenn du eh nicht auf Ratschläge hören möchtest, warum postest
du dann deine Fragen hier? Dann bleib in deinem Graben und löse deinen
Sch.... selber.
Das mit dem Enum z.B.
Wir bauen eine industrielle Ampel:
typedef enum
{
red,
green
} Ampel_t;
wo bauen wir morgen das neue Feature "gelb" ein? Und was passiert dann,
wenn wir bereits den Ampelwert über eine Schnittstelle geschickt haben?
Kommt das Grün da noch richtig beim Empfänger an?
Wir können auch explizit schreiben:
typedef enum
{
red = 0,
green = 1
} Ampel_t;
Keine Ahnung, aber kannst Du mir erklären, warum der Großteil der
Entwickler das yellow zwischen red und green packt und bei einer
expliziten Deklaration auch noch green bewusst auf 2 setzt oder die
explizite Zuordnung einfach löscht?
Das ist meine persönliche und erfahrungsbasierte Sichtweise. Seitdem
nehme ich für solche Kontrakte wieder Konstanten.
Horst S. schrieb:> Das mit dem Enum z.B.>> Wir bauen eine industrielle Ampel:> typedef enum> {> red,> green> } Ampel_t;>>> wo bauen wir morgen das neue Feature "gelb" ein?
Am besten nach dem Grün, weil dann das hier definierte Interface so
wenig wie möglich geändert wird.
> Und was passiert dann,> wenn wir bereits den Ampelwert über eine Schnittstelle geschickt haben?> Kommt das Grün da noch richtig beim Empfänger an?
Klar kommt das Grün richtig an, weil sich beim hinzufügen vom Gelb das
Interface geändert hat, und der Empfänger dafür natürlich entsprechend
angepasst wurde.
Falls der Empfänger am andere Ende der Welt ist und das "Rot", "Grün"
und "Gelb" über irgendeine Leitung verschickt werden soll, darf auch in
keinem Fall einfach die enum verschickt werden, sondern soll im
Übertragungsprotokoll spezifiziert werden wie die Werte in Bits zu
kodieren sind.
> Keine Ahnung, aber kannst Du mir erklären, warum der Großteil der> Entwickler das yellow zwischen red und green packt und bei einer> expliziten Deklaration auch noch green bewusst auf 2 setzt oder die> explizite Zuordnung einfach löscht?
Weil sie dafür nicht von einem vorgestzen Architekten auf die Finger
geklopft werden. :-)
> Das ist meine persönliche und erfahrungsbasierte Sichtweise. Seitdem> nehme ich für solche Kontrakte wieder Konstanten.
Das ist dein gutes Recht. Die enum habe ich aber wegen deiner Frage
nach eine Lösung zur Behandlung nicht vorhandender Modulen eingebracht.
Wenn das Lidar Modul nicht im Projekt miteingebunden ist, wirst du so
Compilerfehler bekommen wenn irgendein Modul trotzdem auf die Konstante
EE_PAGE_LIDARPARAMS versucht zuzugreifen.
Mich würde es nicht stören wenn EE_LIDAR_PARAMS einfach immer definiert
ist, egal ob ich Lidar habe oder nicht.
Ich habs noch mal auseinandergefummelt und ein Testprojekt geschrieben.
Drive.h
1
#ifndef INCL_DRIVE_H
2
#define INCL_DRIVE_H
3
#include"MagicIDs.h"
4
5
externuint8_tDrive_Init();
6
#endif
Keine Ahnung, ob hier jeder diesen "zirkularen Referenzblocker", also
die #ifndef <Headername>-Klausel verwendet. Ich baue ihn eigentlich
immer in einen Modulheader ein. Dabei wird automatisch eine Konstante
erzeugt, deren Name sich an den Dateinamen anlehnt.
Analog dazu Lidar.h
1
#ifndef INCL_LIDAR_H
2
#define INCL_LIDAR_H
3
#include"MagicIDs.h"
4
5
externuint8_tLidar_Init();
6
#endif
Ist das gleiche in grün.
Merke: Im beiden Modulheadern wird auf MagicIDs verwiesen.
MagicIDs:
1
//***************************
2
//Public: MAGIC COMMAND IDS
3
//***************************
4
#ifdef INCL_LIDAR_H
5
#define EE_CMD_READLIDARPARAMS 1
6
#define EE_CMD_WRITELIDARPARAMS 2
7
#endif
8
9
#ifdef INCL_DRIVE_H
10
#define EE_CMD_READDRIVEPARAMS 3
11
#define EE_CMD_WRITEDRIVEPARAMS 4
12
#endif
13
14
15
//******************************
16
//Private MAGIC EEPROM PAGE IDS
17
//******************************
18
#ifdef PRIVATEACCESS
19
#ifdef INCL_LIDAR_H
20
#define EE_PAGE_LIDARPARAMS 1
21
#endif
22
23
#ifdef INCL_DRIVE_H
24
#define EE_PAGE_DRIVEPARAMS 2
25
#endif
26
#endif
27
28
#undef PRIVATEACCESS
MagicIds.h beinhaltet zwei Listen, die sich im Scope unterscheiden.
Während die EEPROM-Seiten wirklich nur im zugehörigen Modul bekannt sein
sollen, sind die CommandIDs auch in anderen Modulen zugänglich.
Merke:
Hier tauchen die Konstanten aus den Modulheadern wieder auf.
Wenn ich jetzt aus meinem Hauptprogramm auf die öffentlichen Konstanten
zugreifen will, muss ich die entsprechenden Header referenzieren:
PatternTest.c
1
#include<avr/io.h>
2
3
#include"Drive.h"
4
#include"Lidar.h"
5
//#include "MagicIDs.h"
6
7
8
intmain(void)
9
{
10
uint8_tx=0;
11
//Constant from lidar
12
x=EE_CMD_READLIDARPARAMS*2;
13
14
//Constant from Drive
15
x+=EE_CMD_READDRIVEPARAMS;
16
}
Vergesse ich hier das Einbinden des entsprechenden Headers, muckt der
Compiler bei der entsprechenden Codezeile.
Entferne ich das Modul Lidar.c nebst Lidar.h (geht auch mit Drive.c/h)
kräht der Compiler zuerst in der include -Zeile und anschließend auch im
Code.
Auch das (irrtümliche) direkte Einbinden von MagicIDs.h wird nicht zum
Erfolg führen.
Der Zugriff auf eine Konstante, bei der ich den Scope nur auf das Modul
beschränken will, z.B. in Drive.c:
1
#include<avr/io.h>
2
3
#define PRIVATEACCESS
4
#include"Drive.h"
5
#include"Lidar.h" // optional
6
7
8
uint8_tDrive_Init()
9
{
10
returnEE_PAGE_DRIVEPARAMS;
11
//return EE_PAGE_LIDARPARAMS;
12
}
"#define PRIVATEACCESS" vor dem include entriegelt diesen Zugriff. Alle
Bedingungen aus MagicIDs sind erfüllt:
- PRIVATEACCESS ist definiert
- Durch "#include Drive.h" wird "INCL_DRIVE_H" definiert.
Also wird auch EE_PAGE_DRIVEPARAMS definiert.
Versuche ich jetzt auf die private Lidarkonstante zuzugreifen
(entkommentiere ich also die Zeile "//return EE_PAGE_LIDARPARAMS;" im
Code), schlägt der Compiler wieder Alarm. Warum?
Ich habe zwar Lidar.h referenziert und damit INCL_LIDAR_H definiert.
Auch habe ich PRIVATEACCESS definiert. Allerdings wird PRIVATEACCESS am
Ende von MagicIds.h über "#undef PRIVATEACCESS" zurückgesetzt.
Wenn ich von hier aus die privaten Konstanten von Lidar nutzen wollte,
könnte ich den Zugriff über ein erneutes definieren von PRIVATEACCESS
direkt über "include "Lidar.h"" erzwingen.
Es geht also mit beiden Scopes (und noch etwas mehr).
Ist das schön? - naja
Geht das schöner? - Das sagt ihr mir jetzt?!?
Auf jeden Fall hänge ich mal zum Rumspielen die Dateien nebst
AVRStudio-Projekt(4.18) an.
zu schützen ist standard und völlig ok.
Der Rest deines Post lässt mich aber klt am Rücken werden. *Schauder!*
* Nirgendwo in Drive.h oder Lidar.h wird auf den Definitionen in
MagicIDs.h verwiesen, also brauchen die das #include da nicht. Das
#include gehört in den .c-Dateien.
* Dieser PRIVATEACCESS ist einfach grausam und gar nicht so privat wie
die Name es erscheinen lässt. Jede C-Datei die PRIVATEACCESS definiert
und dann z.B. Drive.h includiert kann dann auf EE_PAGE_DRIVEPARAMS
zugreifen.
Ich würde es eher so angehen, dass kein Modul diese EE_PAGE_BLAHPARAMS
braucht, ausser das Modul selber. Siehe angehänges Beispiel.
Die EEPROM Pages werden in eepages.h definiert. Der Aufwand ein Modul
hinzuzufügen oder zu entfernen ist nicht mehr als
- ein #include hinzufügen oder entfernen
- ein #define hinzufügen oder entfernen
- ggf ein weiteres #define umdefinieren.
EDIT: Beispielcode lässt sich vom Cmdline compilieren mit
Eric B. schrieb:> Jede C-Datei die PRIVATEACCESS definiert> und dann z.B. Drive.h includiert kann dann auf EE_PAGE_DRIVEPARAMS> zugreifen.
Wenn ich in Lidar.c ...
#define PRIVATEACCESS
#include "Lidar.h"
#include "Drive.h"
oder ...
#include "Drive.h"
#define PRIVATEACCESS
#include "Lidar.h"
oder im Header Lidar.h...
#ifndef INCL_LIDAR_H
#define INCL_LIDAR_H
#include "MagicIDs.h"
#include "Drive.h"
extern uint8_t Lidar_Init();
...
#endif
...schreibe, wird der Zugriff geblockt. Gibt's sonst noch Möglichkeiten,
die ich nicht gesehen/berücksichtigt habe?
Wenn da noch was fehlt oder eine Lücke ist, sag's, dann ist's ein Fehler
im Pattern. Das sollte nicht sein, dann ist das Pattern falsch.
Wenn Du allerdings sagst, man kann das PRIVATEACCESS auch woanders
hinsetzen, da hast Du zweifelsfrei recht. Dann ist's aber ein Fehler in
der "Anwendung des Patterns". Das ist ein Unterschied!
Ich versuch's mal auf den Punkt zu bringen. Was kann passieren?
- dass ich das PRIVATEACCESS oder ein include gegen Regeln des Patterns
an die falsche Stelle setze.
- dass ich beim Kopieren von Codezeilen vergesse, einen Konstantennamen
anzupassen (z.B. bei Dir in
"eeprom_read(EE_PAGE_LIDARPARAMS, (char *)param_ptr,
sizeof(lidar_param_t));)
).
Bei Deiner Lösung (Scope auf alle Konstanten in der EEPAGES.h offen für
alle Module) muss ich allerdings nur einen Fehler machen, bei meiner
Lösung zwei.
Ich sage nicht, dass das ideal ist, nur fällt mir nix Besseres ein.
Horst S. schrieb:> - dass ich beim Kopieren von Codezeilen vergesse, einen Konstantennamen> anzupassen (z.B. bei Dir in> "eeprom_read(EE_PAGE_LIDARPARAMS, (char *)param_ptr,> sizeof(lidar_param_t));)> ).
Das wäre dann aber auch "ein Fehler in der Anwendung des Patterns". ;-)
Grundsätzlich soll gelten: wenn eine Konstante in nur einer Datei
benutzt werden darf, soll sie auch da und nirgendwo anders definiert
werden.
Wenn aber mehrere solche Konstanten voneinander abhängig sind, macht es
Sinn sie in einer Datei zusammen zu fügen und dann diese Datei zu
includieren (MagicIDs.h / eepages.h)
Sich dann aber einen Krampf machen um zu probieren die einzelne
Konstanten doch wieder in Scope zu beschränken, ist m.E. sinnlos und
fehleranfällig. Da hilft nur ein bisschen Diziplin.