Wenn man den gleichen Quelltext für unterschiedliche Hardware verwenden
möchte, kann man überflüssige Teile mit sowas wie
1
#if CONFIG_HAS_DISPLAY
ausblenden. Wenn dann in einer Quelldatei nichts mehr übrig bleibt (wg.
minimal-Hardware), ist das angeblich verboten:
1
ISOCforbidsanemptytranslationunit
Aber: Sobald auch nur ein include übrig bleibt, gibt es keine Mecker!
Hat bitte jemand eine Idee, was dahinter steckt und wie man das
richtig/schön/vernünftig machen sollte?
Du kannst einen #else Zweig mit einer Dummy Funktion machen:
#if CONFIG_HAS_DISPLAY
#else // no Display
void DisplayDummyFunc(void); // Declare function to avoid "no
prototype" error
void DisplayDummyFunc(void) {
}
#endif
eagle user schrieb:> Sobald auch nur ein include übrig bleibt, gibt es keine Mecker!
Dann nutz' das doch aus und includiere zumindest die Standard-Header
außerhalb des #if-Blocks?
Danke euch. Alle Vorschläge haben etwas für sich, aber alle sind auch
irgendwie unnatürlich. Ich lasse es erstmal beim include vor dem #if.
Aber es wäre halt auch interessant, was sich das Komitee am grünen Tisch
dabei gedacht hat.
eagle user schrieb:> Ich lasse es erstmal beim include vor dem #if.
heisst das, dass die include-Datei auch leer sein kann? Oder enthält sie
z.B. eine static-variable?
Wir verwenden
1
#else
2
staticint_dummy;
3
#endif
durch static gibt es keinen Namenskonflikt. Die 2 oder 4 byte spielen
dabei bei uns keine Rolle.
eagle user schrieb:> Wenn man den gleichen Quelltext für unterschiedliche Hardware verwenden> möchte,...
Ich kann derartige Konstrukte nicht ausstehen. Man sollte stattdessen
säuberlich trennen zwischen hardwareunabhängigen und hardwareabhängigen
Firmwareteilen. Letztere sind dann änderungsbedürftig bei anderer HW und
das sollte dann eben auch mit einer unabhängigen anderen .c erfolgen,
wobei die zugehörige .h für alle HW-Varianten gleich bleibt.
W.S.
In meinem Programm sind es 4 Header mit Prototypen, typedefs usw.; es
wird aber kein Code erzeugt. Aber ein einziges typedef reicht auch schon
damit Ruhe ist.
W.S. schrieb:
> eagle user schrieb:>> Wenn man den gleichen Quelltext für unterschiedliche Hardware verwenden>> möchte,...>> Ich kann derartige Konstrukte nicht ausstehen.
Ich auch nicht. Eigentlich möchte ich die Konfiguration dynamisch
machen, also komplett ohne #if -- ein Image für alle Hardware-Varianten.
Aber das kostet Platz...
> Man sollte stattdessen säuberlich trennen zwischen hardwareunabhängigen> und hardwareabhängigen Firmwareteilen. Letztere sind dann> änderungsbedürftig bei anderer HW und das sollte dann eben auch mit> einer unabhängigen anderen .c erfolgen,
und genau deswegen war die eine .c dann leer und produzierte
diese komische Warnung.
eagle user schrieb:> und genau deswegen war die eine .c dann leer und produzierte> diese komische Warnung.
Das geht GARNICHT - vorausgesetzt du hast nicht in eben dieser .c mit
#ifdef's um dich geworfen wie ne Supernova mit Materie.
also nochmal:
1. eine .h für eine hardwareabhängige Quelle, die das Interface zu dem
Rest der Welt macht.
2. je nach HW eben mehrere unterschiedliche .c (aber mit gleicher .h),
die dann eben jeweils nur für genau eine HW zutreffend ist - und die
eben keinerlei #ifdef's enthält. Dann hast du auch niemals ne leere .c
in deinem Projekt.
Ich geb dir mal ein schlimmes Beispiel:
Kennst du den wp34s ? Ist eigentlich ein richtig netter Taschenrechner
auf Basis eines BWL'ler-Taschenrechners von HP. Hat nen ARM7TDMI drin
und das Projekt wurde mit dem Gcc übersetzt.
Ich hatte aus Neugier mal versucht, auf die Schnelle die Quellen mit dem
Keil zu übersetzen - totaler Schiffbruch.
Nicht nur ein irrer Sack von #define's in jeder blutigen Quelle und auch
zusätzliche, die im Gcc-Makefile drinstecken, sondern auch solche
Spitzentechnologie wie void Funktionen, die tatsächlich einen
Rückgabewert zurückliefern. Also sowas
void blabla(...)
{ ...
if(..) return abc;
else return xyz;
}
Soweit ich das gesehen hatte, werden die Rückgabewerte zwar nicht
benutzt, aber natürlich wird man bei sowas vom Keil aus Prinzip
abgewatscht. Ich frag mich bloß, wie die Leute den Gcc dazu gebracht
haben, sowas überhaupt durchgehen zu lassen.
Kurzum, der Tachenrechner funktioniert - aber ich verkneife mir, in das
Geknäuel von Haarsträubendem bei den Quellen nochmal reinzugucken. So
wichtig ist mir das Ganze nun auch nicht.
Das Problem ist ganz offensichtlich: Die Schreiber dieses Quellcodes
haben an keiner Stelle eine saubere Trennung hingekriegt. Mach du lieber
nicht den gleichen Fehler, sonst kriegst du graue Haare.
W.S.
> durch static gibt es keinen Namenskonflikt.
Aber auch dies kann zu einer Warnung führen weil static _dummy nicht
verwendet wird. Warum also nicht einfach extern?
> extern int _avoid_empty_module_warning;
Andre R. schrieb:
> Das ist aber nicht C11 sondern schon ganz lange drin, z.B.> http://computer-programming-forum.com/47-c-languag...> gcc auf pedantic gestellt?
Natürlich pedantic, jede Warnung hilft, keine wird ignoriert.
[C11] steht nur im Betreff weil ich "std=c11" verwende.
Danke für den lesenswerten Link!
W.S. schrieb:
> eagle user schrieb:> > und genau deswegen war die eine .c dann leer und produzierte> > diese komische Warnung.>> Das geht GARNICHT - vorausgesetzt du hast nicht in eben dieser .c mit> #ifdef's um dich geworfen wie ne Supernova mit Materie.
Es war genau 1 (ein) #if um den kompletten Quelltext herum.
> also nochmal:> 1. eine .h für eine hardwareabhängige Quelle, die das Interface zu dem> Rest der Welt macht.> 2. je nach HW eben mehrere unterschiedliche .c (aber mit gleicher .h),> die dann eben jeweils nur für genau eine HW zutreffend ist - und die> eben keinerlei #ifdef's enthält. Dann hast du auch niemals ne leere .c> in deinem Projekt.
Genauso schaut's hier aus, ausser, dass das "if" in der .c steht und
nicht im Makefile. Oder wie soll ich je nach Konfiguration die eine oder
die andere .c einbinden? "if" im Makefile finde ich noch
unübersichtlicher als in der Quelle.
eagle user schrieb:> Genauso schaut's hier aus, ausser, dass das "if" in der .c steht und> nicht im Makefile. Oder wie soll ich je nach Konfiguration die eine oder> die andere .c einbinden? "if" im Makefile finde ich noch> unübersichtlicher als in der Quelle.
Ich nicht. Ich finde es sehr unübersichtlich, wenn Quelldateien zwar
mitcompiliert werden, aber ihr gesamter Inhalt wegge'#if't wurde.
Fühlt sich ungefähr so an, wie wenn ich etwas bestelle, und weil es
nicht mehr lieferbar ist, wird mir ein leeres Paket geschickt anstelle
von gar nichts.
Im Makefile am Anfang eine Zeile, in der man auswählt, für welche
Hardware übersetzt werden soll. Oder ggf. auch in ein separates
Makefile.config oder so auslagern und das im Makefile inkludieren.
Ich würde sagen, dass es auch eher üblich ist, solche
Konfigurationsoptionen alle gesammelt ins Makefile zu stecken und nicht
irgendwo im Code zu verteilen. Im Makefile hat man eben viel mehr
Möglichkeiten, wie eben z.B. abhängig von der Einstellung C-Files ganz
wegzulassen oder mit anderen Optionen zu übersetzen.
eagle user schrieb:> Aber es wäre halt auch interessant, was sich das Komitee am grünen Tisch> dabei gedacht hat.
Kann einfach nur ein Seiteneffekt der Syntaxregeln sein, genauso wie
eben durch das Preprocessing die Forderung besteht, dass er zeilenweise
aufgebaut ist und daher formal die letzte Zeile mit einem Zeilenende
enden muss.
eagle user schrieb:> Aber es wäre halt auch interessant, was sich das Komitee am grünen Tisch> dabei gedacht hat.
Nicht viel. Die Syntaxregel sieht auf den ersten Blick OK aus (§ 6.9):
1
translation-unit:
2
external-declaration
3
translation-unit external-declaration
4
external-declaration:
5
function-definition
6
declaration
Eine leere Datei zu erlauben, wäre umständlicher gewesen:
eagle user schrieb:> Genauso schaut's hier aus, ausser, dass das "if" in der .c steht und> nicht im Makefile. Oder wie soll ich je nach Konfiguration die eine oder> die andere .c einbinden? "if" im Makefile finde ich noch> unübersichtlicher als in der Quelle.
da braucht's kein if:
Markus F. schrieb:> da braucht's kein if:> target1: OBJS += target1_config.o> target2: OBJS += target2_config.o
ACK.
Würde auch in jedem Fall versuchen, derartige Sachen über das Makefile
zu steuern, sprich Einbindung der benötigten Low-Level-Treiber und der
unterstützten High-Level-Funktionen in Abhängigkeit der Zielplattform.
Rolf M. schrieb:> Im Makefile am Anfang eine Zeile, in der man auswählt, für welche> Hardware übersetzt werden soll. Oder ggf. auch in ein separates> Makefile.config oder so auslagern und das im Makefile inkludieren.> Ich würde sagen, dass es auch eher üblich ist, solche> Konfigurationsoptionen alle gesammelt ins Makefile zu stecken und nicht> irgendwo im Code zu verteilen. Im Makefile hat man eben viel mehr> Möglichkeiten, wie eben z.B. abhängig von der Einstellung C-Files ganz> wegzulassen oder mit anderen Optionen zu übersetzen.
Eben.
Wenn man es richtig gut machen will, unterscheidet man zumindest noch
die Target-Filenames an Hand des Targets (ob man die Object-Files etc.
auch noch unterscheidet oder bei einem neuen Target alles neu übersetzt,
darüber kann man streiten - ich würde es zumindest machen, wenn sich die
Architektur unterscheidet) - schon ist man auf einem guten Weg zum
vollautomatisierten nächtlichen Build-Server ;-)
Persönlich finde ich es übrigens auch recht schnieke, auch im
Embedded-Bereich gleich auf reproducible builds hinzuarbeiten (vgl.
reproducible-builds.org); gibt einem einfach ein gutes Gefühl, zu
wissen, dass ein Anderer das Projekt ohne Weiteres auch z. B. auf dem
Mars übersetzen kann und exakt das gleiche Ergebnis erzielt.