Forum: Compiler & IDEs GCC Präprozessor macro '__FILE__' abkürzen


von Wym (Gast)


Lesenswert?

Hallo

Ich benutze den GNU GCC Compiler für ein Projekt. Dieses Projekt enthält 
sehr viele asserts.
Wenn ein assert ausgelöst wird, so wird auf einer Konsole einiges an 
Information ausgespuckt. Unter anderem der Name des Files welchen den 
assert auslöst, die Zeilennummer sowie der Grund für den assert.
Normalerweise werden bei einem Release die asserts ausgeschaltet, um 
Speicherplatz einsparen zu können. Das möchte ich aber aus verschiedenen 
Gründen nicht tun.

Also bleibt mir nichts anderes übrig als den assert abzukürzen. Eine 
Optimierungsmöglichkeit wäre da das Makro
1
__FILE__
Der Präprozessor des Compilers ersetzt dieses Makro durch den Filenamen. 
Also etwas in der Form "/usr/local/include/myheader.h".
Das Makro
1
__FILE__
 enthält also gemäss Standard nicht nur den Filenamen, sondern auch den 
Pfad, wo sich das File befindet. Das kann ein sehr langer Text sein, 
welcher dementsprechend viel Speicher benötigt.

Nun meine Frage:
Kann ich durch irgend eine Option den GCC dazu zwingen das Macro
1
__FILE__
 ohne den Pfad zu erzeugen? Dann nämlich hätte ich schon sehr viel an 
Speicher eingespart.

Unter folgendem Link habe ich keinen Hinweis darauf gefunden wie das 
gemacht werden könnte:
http://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html

Für einen Tipp oder Hinweis bin ich dankbar.

Gruss
Wym

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Da würd ich eher mich in der gcc-help@gcc.gnu.org Mailing-Liste anmelden 
und dann dort fragen.

http://gcc.gnu.org/lists.html

Falls es nicht geht — absehbar — dann ist __FILENAME__ vielleicht eine 
sinnvolle Erweiterung.

Gibt __FILE__ denn immer den absoluten Pfad? Oder das, was man gcc/cc1 
übergibt? In letzerem Falle hilft Umschreiben des 
Build-Prozesses/Makefiles.

von Leo C. (rapid)


Lesenswert?

An gcc bzw. __FILE__ liegt es nicht:
1
leo@cb:/tmp$ cat file.c
2
const char *s = __FILE__;
3
4
leo@cb:/tmp$ gcc -E file.c
5
# 1 "file.c"
6
# 1 "<command-line>"
7
# 1 "file.c"
8
const char *s = "file.c";
9
leo@cb:/tmp$

von Wym (Gast)


Lesenswert?

Vielen Dank für den Hinweis!

Ich habe es ausprobiert, und es liegt am Aufruf des Compilers wie Johann 
vermutet und Leo bestätigt hat.
Der Build-Prozess muss umgeschrieben werden.

Wäre zwar wesentlich einfacher ein Macro zur Verfügung zu haben welches 
den Pfad nicht enthält, aber leider bin ich diesbezüglich nicht fündig 
geworden.

von Sam P. (Gast)


Lesenswert?

Wenn du den String mit dem Dateinamen als separaten String statt per 
Konkatenation verwendest (also nicht so: "Assert 1==2 failed in " 
_FILE_  sondern printf-ähnlich "Assert %s failed in %s"), dann sollte 
gcc schlau genug sein, den Dateinamen nur einmal im binary abzulegen, da 
es ja in diesem Fall immer der selbe string ist. Die Assert-Basismeldung 
kann dann nebenbei auch noch geshared werden.

Das hängt natürlich vom verwendeten assert-Mechanismus ab, es lohnt sich 
möglicherweise seine eigenen Makros dafür zu schreiben.

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


Lesenswert?

Leo C. schrieb:
> An gcc bzw. __FILE__ liegt es nicht:

Jein.  Der Präprozessor fügt dort den Dateinamen ein, mit dem er
die Datei geöffnet hat.  Insbesondere also bei impliziten Include-
Pfaden ist es ein absolter Pfadname.

Ich habe mal im Präprozessor-Code nachgesehen, etwas wie das von
Johann als Idee vorgeschlagene __FILENAME__ gibt's derzeit nicht.
Sollte nicht schwierig sein, das zu implementieren, das größte
Problem ist es sicher, den OS-abhängigen Verzeichnistrenner zu
ermitteln und zu berücksichtigen.  (Das ist nicht zwingend nur die
Entscheidung zwischen '/' und '\\', sondern meines Wissens dürfte
auch VMS noch unterstützt sein, welches wohl '.' benutzt.)

von Klaus W. (mfgkw)


Lesenswert?

Da assert() relativ schlicht ist und schnell geschrieben, wäre es 
vielleicht eine Lösung: eigenes assert() bauen, darin aber nicht 
_FILE_ ausgeben, sondern stattdessen eine eigene Funktion aufrufen, 
die den Namen wie gewünscht beschneidet (ist ja jetzt per Definition 
keine Körperverletzung mehr).

Wenn die Originalversion etwa so aussehen könnte (nur gcc wg. 
_PRETTY_FUNCTION_):
1
#define assert(a)                                                           \
2
if( !(a) )                                                                  \
3
{                                                                           \
4
  fprintf( stderr,                                                          \
5
           __FILE__":%d:%s Assertion `" #a "\' failed\n",                   \
6
           __LINE__,                                                        \
7
           __PRETTY_FUNCTION__                                              \
8
           );                                                               \
9
  abort();                                                                  \
10
}

kann man sich selbst ein eigenes assert() so schreiben:
1
inline const char *littleJewischPrince( const char *s )
2
{
3
  const char *p_slash = strrchr( s, '/' );
4
  return ( p_slash ? p_slash+1 : s );
5
}
6
7
#define assert(a)                                                           \
8
if( !(a) )                                                                  \
9
{                                                                           \
10
  fprintf( stderr,                                                          \
11
           "%s:%d:%s Assertion `" #a "\' failed\n",                         \
12
           littleJewischPrince(__FILE__),                                   \
13
           __LINE__,                                                        \
14
           __PRETTY_FUNCTION__                                              \
15
           );                                                               \
16
  abort();                                                                  \
17
}

Bei größeren Programmen habe ich früher oder später ohnehin immer 
assert() selbst definiert, weil man dann noch andere Spielereien 
einbauen kann (aktuellen Stand von irgendwelchen Kellerleichen dumpen 
o.ä.).

von Wym (Gast)


Lesenswert?

@Sam: Wir verwenden eCos als Betriebssystem. eCos ist von seiner Idee 
her stark modularisiert, was zu sehr tiefen Verzeichnis-Strukturen 
führt. Daher auch meine Idee den Pfad zu entfernen, da es sich wirklich 
lohnt. Die von Dir vorgeschlagene Optimierung ist wahrscheinlich 
innerhalb eines Files wirksam. Ich verspreche mir daher nicht eine 
wesentliche Einsparung aufgrund der grossen Verteilung auf (extrem) 
viele Files.
Wie ich im Code von eCos feststellen konnte ist der assert zwar nicht 
mit printf aufgebaut, aber der Compiler sollte trotzdem bereits jetzt 
die von Dir erwähnte Optimierung vornehmen, da alle verschiedenen 
asserts letztendlich zentral über einen einzigen Funktionsaufruf 
implementiert sind:
1
cyg_assert_msg( const char *psz_func, const char *psz_file,
2
                cyg_uint32 linenum, const char *psz_msg ) __THROW
3
{
4
...
5
    diag_write_string("ASSERT FAIL: ");
6
    write_thread_id();
7
    diag_write_string(trim_file(psz_file));
8
    write_lnum(linenum);
9
    diag_write_string(trim_func(psz_func));
10
    diag_write_char(' ');
11
    diag_write_string(psz_msg);
12
    diag_write_char('\n');
13
...
14
}

Hinzu kommt noch unsere eigene Applikation welche sich in der 
Verzeichnis-Struktur an einem anderen Ort befindet (übrigens auch tief 
verschachtelt). Darin haben wir die asserts etwas anders formuliert. 
Hier verwenden wir am Ende einen printf Mechanismus. Nachdem der assert 
in einem Logger festgehalten wurde wird ein hartes reset ausgelöst:
1
extern "C" int AppAssert(char* file, int line)
2
{
3
    #ifdef DEBUG_FUNCION
4
    CommonCmds::cmdShowSysInfo(NULL);
5
    #endif
6
    Logger::Default()->printLogText(Logger::LOG_FATAL, "ASSERT @ %s:%d\n", file, line);
7
    //let logging mechanism dump the data
8
    Kernel::Default()->delayFor(50);
9
    // actually simply reset the system
10
    HAL_PLATFORM_RESET();
11
    return 0;
12
}

von Wym (Gast)


Lesenswert?

@Klaus: Habe ich das nicht verstanden? Das wird mir doch keinen Code 
einsparen? Es wird zwar nur der Filename und nicht der Pfad ausgegeben, 
aber im build wird trotzdem der ganze string bestehend aus Pfad und 
Filename integriert?

von Klaus W. (mfgkw)


Lesenswert?

sorry, ich hatte nicht realisiert, daß es dir um die gespeicherten 
Strings geht.
Die werden natürlich nicht kleiner, nur die Ausgabe.

Wenn ihr mit make arbeitet, könnte man ersatzweise beim Kompilieren den 
nackten Namen ohne Pfad als Makro übergeben und das dann für ein assert 
nehmen.

von Wym (Gast)


Lesenswert?

@Klaus: Das ist eine guter Vorschlag. Wenn ich die Problematik als 
Ganzes betrachte wird das wohl die beste Lösung sein.

@Jörg: Den GCC ändern wäre wohl eine Möglichkeit, aber das geht mir 
persönlich doch etwas zu weit.

von Rolf Magnus (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> sorry, ich hatte nicht realisiert, daß es dir um die gespeicherten
> Strings geht.
> Die werden natürlich nicht kleiner, nur die Ausgabe.
>
> Wenn ihr mit make arbeitet, könnte man ersatzweise beim Kompilieren den
> nackten Namen ohne Pfad als Makro übergeben und das dann für ein assert
> nehmen.

Das wollte ich auch schon vorschlagen. Ddas kann man im Makefile z.B. 
direkt über die CFLAGS machen:
1
CFLAGS=-D__FILENAME__=\"$(notdir $<)\"
2
3
%.o: %.c
4
  $(CC) -c $< $(CFLAGS) -o $@

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.