Forum: Compiler & IDEs [C11] CONFIG_FOO -> ISO C forbids an empty translation unit


von eagle user (Gast)


Lesenswert?

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
ISO C forbids an empty translation unit
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?

von Tom (Gast)


Lesenswert?

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

von Clemens L. (c_l)


Lesenswert?

Du könntest das Build-System so umorganisieren, dass die Datei in diesem 
Fall gar nicht erst kompiliert wird.

von Markus F. (mfro)


Lesenswert?

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?

von eagle user (Gast)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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
static int _dummy;
3
#endif
durch static gibt es keinen Namenskonflikt. Die 2 oder 4 byte spielen 
dabei bei uns keine Rolle.

von W.S. (Gast)


Lesenswert?

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.

von eagle user (Gast)


Lesenswert?

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.

von eagle user (Gast)


Lesenswert?

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.

von Andre R. (physicist)


Lesenswert?

Das ist aber nicht C11 sondern schon ganz lange drin, z.B.
http://computer-programming-forum.com/47-c-language/40e66968b3c164d1.htm
gcc auf pedantic gestellt?

von W.S. (Gast)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Achim S. schrieb:
> Wir verwenden
>
1
#else
2
> static int _dummy;
3
> #endif
> 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;

von eagle user (Gast)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Clemens L. (c_l)


Lesenswert?

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:
1
translation-unit:
2
    external-declaration-sequence_opt
3
external-declaration-sequence:
4
    external-declaration
5
    external-declaration-sequence external-declaration
6
external-declaration:
7
    function-definition
8
    declaration

Es ist nicht explizit verboten worden, es hat sich halt nur niemand die 
Mühe gemacht, es zu erlauben.

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

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:
1
target1: OBJS += target1_config.o
2
target2: OBJS += target2_config.o

von Toni Tester (Gast)


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.