Forum: PC-Programmierung Doxygen: Alle #if-Zweige des Präprozessors


von Walter T. (nicolas)


Lesenswert?

Guten Morgen,

gibt es in Doxygen die Möglichkeit, alle Zweige von Präprozessor-Makros 
in die Dokumentation aufzunehmen?

Oder andersherum: Gibt eine Möglichkeit, den Präprozessor komplett 
abzuschalten, ohne dass die Include-Graphen verloren gehen?

von DPA (Gast)


Lesenswert?

Es gibt bei Stackoverflow ein zwei vorschläge dazu: 
https://stackoverflow.com/questions/26043007/make-doxygen-doxument-the-ifdef-parts-too

Ansonsten im Makefile halt was hinzufügen, das die Dateien kopiert und 
die ifdefs entfernt: (ungetestet)
1
SOURCES=$(wildcard src/*.c) $(wildcard src/**/*.c)
2
SOURCES=$(wildcard src/*.h) $(wildcard src/**/*.h)
3
4
docs: $(patsubst src/%,build/docsrc/%,$(SOURCES))
5
  doxygen Doxyfile
6
7
build/docsrc/%: src/%
8
  mkdir -p "$(dir "$@")"
9
  sed '/^#if/d;/#endif/d;/#else/d' <"$<" >"$@"

Aber wozu will man sowas überhaupt? Sollte man in der Doku nicht drin 
haben, was tatsächlich drin ist? Gibt es eventuell bessere alternativen 
zum Einsatz der ifdefs für das, wofür du diese momentan einsetzt?

von Walter T. (nicolas)


Lesenswert?

DPA schrieb:
> Es gibt bei Stackoverflow ein zwei vorschläge dazu

Der schlägt ja vor, Preprocessing zu deaktivieren, was mich die 
Baumdiagramme kostet. Letztere waren allerdings der Grund, überhaupt mit 
Doxygen zu arbeiten.

DPA schrieb:
> Sollte man in der Doku nicht drin
> haben, was tatsächlich drin ist

Naja, in der Doku des Quelltextes sollte drin sein, was implementiert 
wurde. Wenn es von einer Funktion zwei Varianten (z.B. für 
unterschiedliche Targets gibt), sollten natürlich beide dokumentiert 
sein.

Momentan behelfe ich mir damit, statt
1
#if TARGET == TARGET1
2
  ...
3
#elif TARGET == TARGET2
4
  ...
5
#else
6
  error ...
7
#endif
im Quelltext
1
#ifdef( TARGET1 )
2
  ...
3
#endif
4
#ifdef( TARGET2 )
5
  ...
6
#endif
und dann in Doxygen alle Targets zu definieren. Ist nur im Quelltext 
deutlich weniger geradlinig.

von DPA (Gast)


Lesenswert?

Ich mache in der regel ein Verzeichnis mit Unterverzeichnissen für jedes 
Target, und tue den Platformabhängigen Code dann in Dateien dort drin 
rein. Ich versuche dann immer, so wenig Platformabhängigen Code wie 
möglich zu haben, und schaue, dass der generische Code nicht von einer 
bestimmten Platform abhängig wird. In der regel mach ich das so, dass 
ich in Headern Interfaces definiere, und jede Platform diese dann 
implementiert. Beim compilieren lass ich dann die Dateien für andere 
Plattformen, als ich grad baue, einfach weg. Doxygen kann man die dann 
trotzdem alle vorwerfen, aber dort muss ich in der regel nicht viel 
Dokumentieren, weil das Interface schon zentral einmal dokumentiert ist 
und Programmunabhängig ist.

von Walter T. (nicolas)


Lesenswert?

Ich bin mir nicht sicher, ob ich die Kernaussage richtig verstehe. 
Lautet sie "der Plattformabhängige Code ist so kurz, daß er nicht in die 
Doku braucht" ?

von Dr. Sommer (Gast)


Lesenswert?

Walter T. schrieb:
> #ifdef( TARGET1 )
>   ...
> #endif
> #ifdef( TARGET2 )
>   ...
> #endif

oder
1
#if defined(TARGET1) || defined(DOXYGEN)
2
...
3
#endif
4
#if defined(TARGET2) || defined(DOXYGEN)
5
...
6
#endif

Alternativ einfach nur die Definitionen der Funktionen in #ifdef packen 
- dann sind die Deklarationen immer da, und wenn man eine unpassende 
Funktion benutzt gibt's einen "undefined reference" Linker Fehler.

Oder die Dinge für Target 1 in eine Datei, die für Target 2 in eine 
andere usw. Man muss dann die jeweils richtige Datei inkludieren, aber 
Doxygen sieht sie alle.

Oder die unpassenden Funktionen per SFINAE statt per Makros ausblenden, 
dann sieht Doxygen sie auch.

Oder verschiedene Klassen für die verschiedenen Targets definieren, und 
man muss dann je nach Target die richtige Klasse instanziieren. Doxygen 
sieht dann auch alles.

von DPA (Gast)


Lesenswert?

Nunja, die Funktionalität meiner Programme ist in der regel bei den 
meisten Targets grösstenteils gleich oder ähnlich. Sagen wir, ich 
deklariere in einem Header die Platformabhängige Funktion "void 
setBlaMotorSpeed(int speed);", dann Dokumentiere ich dort z.B. "Setzt 
die Drehgeschwindigkeit von Motor bla. \param speed Geschwindigkeit in 
RPM im Uhrzeigersinn, negative Werte gegen den Urzeigersinn.". Wenn ich 
dann für verschiedene Plattformen andere Implementationen davon habe, 
dann schreib ich das dort in der Regel nicht nochmal hin. Wo der Motor 
angeschlossen ist steht wo anders, und interessiert nicht, wenn man nur 
wissen will, was die Funktion tut. Höchstens Wenn es noch was spezielles 
zu beachten gibt, schreib ich das hin.

von Vincent H. (vinci)


Lesenswert?

Exakt jene Frage hab ich vor 5 Monaten ebenfalls auf stackoverflow 
gestellt:
https://stackoverflow.com/questions/55242882/doxygen-parse-everything-regardless-of-preprocessor-directive#

Fazit: Ich entferne die Direktiven via Python-Script und parse die 
temporäre Datei...

von Nick M. (Gast)


Lesenswert?

Dr. Sommer schrieb:
> #if defined(TARGET1) || defined(DOXYGEN)
> ...
> #endif
> #if defined(TARGET2) || defined(DOXYGEN)
> ...
> #endif

Gute Lösung!
Das 'DOXYGEN' lässt sich in Doxygen selbst definieren. So muss man nur 
einmal zusätzlich rumfummeln.
In der Konfiguration von Doxygen ist das dann nur ein:
PREDEFINED             = _DOXYGEN_

Ja, ich hab das mit zwei underlines gemacht. Und ja, die sind eigentlich 
für den Compiler (?) reserviert.

Interessanterweise wirft Doxygen alternative defines dann raus:
1
#if defined(DEBUG) || defined(__DOXYGEN__)
2
  /**
3
  \brief Debug something. Needs 'DEBUG' to be defined!
4
 */
5
  void DebugSomething();
6
#else
7
  /// See function DebugSomething
8
  #define DebugSomething() 
9
#endif

Hier wird nur die Doku für die Funktion und nicht das Makro erstellt.

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.