Forum: Compiler & IDEs Enum Export für Docu


von Michael R. (mr-action)


Lesenswert?

Moin Moin,

folgende Situation: Ich habe eine Software, die an einigen Stellen Enums 
nutzt. Diese Enums werden teilweise auch in Logdaten gespeichert 
(numerisch). Die Enums kann ich nicht auf dem µC schon von der Zahl in 
einen String wandeln, weil ich dafür in den Logdaten zu wenig 
Speicherplatz habe (das Format kann ich nicht ändern).

Da eh ein Tool genutzt wird um die Logdaten später auszuwerten war meine 
Idee, darin die Enums in Text zu wandeln. Diese Umwandlung könnte ich 
jetzt natürlich von Hand pflegen - möchte ich aber nicht. Aufgrund von 
unterschiedlichen Sprachen kann ich leider die Enums auch nicht einfach 
in dem Tool includieren.

Mein Gedanke war jetzt die Enums als Tabelle zu exportieren um diese 
Tabelle dann in dem Tool zu nutzen. Aber wie mache ich das?
Kennt jemand ein Tool um das zu machen?

Was mir grade in den Sinn kommt, wäre alle Header mit diesen Enums in 
ein C++ Tool (Qt kann da über den Moc ja bissl was zaubern) zu 
includieren und darin die Header ein eine entsprechende Tabelle zu 
wandeln. Aber ich wäre da für eine einfachere Lösung durchaus offen... 
;-)

Viele Grüße
Michael

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Es gibt diverse Tricks, das in C++ hinzubekommen. Dementsprechend 
könntest du deine enums zentral in eine Header-Datei packen und diese im 
uC-Code inkludieren und normal verwenden. Auf dem PC inkludierst du die 
Datei in ein C++-Programm welches einen dieser Tricks nutzt:

https://stackoverflow.com/questions/28828957

Ggf. müssen die genutzten Makros so angepasst werden, dass sie im C-Fall 
nur das enum produzieren, im C++-Fall zusätzlich das drumherum.

von Patrick C. (pcrom)


Lesenswert?

Versuche mal Doxygen

von Bartli (Gast)


Lesenswert?

Code generieren. Enum z.B. in einem Python/Ruby/Sprache deiner Wahl 
definieren und Skript schreiben welches die Enumdefinition für den uC 
und das Tool (in anderer Sprache) erzeugt.

von Vincent H. (vinci)


Lesenswert?

Bartli schrieb:
> Code generieren. Enum z.B. in einem Python/Ruby/Sprache deiner Wahl
> definieren und Skript schreiben welches die Enumdefinition für den uC
> und das Tool (in anderer Sprache) erzeugt.

So grauslich die ganzen aktuellen "Reflection" Implementierungen auch 
sein mögen, sie sind immer noch besser als externe Tools und Skripts. Es 
ist einfach ein Naturgesetz, dass Code und externe Skripts irgendwann 
auseinanderlaufen...

Schau mal da rein:
https://www.youtube.com/watch?v=GCkhcT2oxCA

Das ist so ziemlich state-of-the-art was Enum Stringifizierung usw. 
angeht.

von Rolf M. (rmagnus)


Lesenswert?

Patrick C. schrieb:
> Versuche mal Doxygen

Damit kann man übrigens nicht nur so Sachen wie HTML oder PDF erzeugen, 
sondern auch eine XML-Datei, die alle im Code erkannten Dinge in XML 
speichert. Da findet man auch den enum und alle seine Enumeratoren 
wieder. Ist allerdings ein schon eher zu gesprächiges XML.

Vincent H. schrieb:
> Bartli schrieb:
>> Code generieren. Enum z.B. in einem Python/Ruby/Sprache deiner Wahl
>> definieren und Skript schreiben welches die Enumdefinition für den uC
>> und das Tool (in anderer Sprache) erzeugt.

Ja, so würde ich das auch machen.

> So grauslich die ganzen aktuellen "Reflection" Implementierungen auch
> sein mögen, sie sind immer noch besser als externe Tools und Skripts. Es
> ist einfach ein Naturgesetz, dass Code und externe Skripts irgendwann
> auseinanderlaufen...

Wie sollen sie das tun, wenn der Code durch dieses externe Skript 
erzeugt wird? Ich kann dann gar keine enum-Werte benutzen, die nicht 
vorher vom Skript generiert worden sind.

> Schau mal da rein:
> Youtube-Video "CppCon 2018: Nir Friedman “Wise Enum: A C++ Smart Enum
> For All Your Reflective Enum Needs”"
>
> Das ist so ziemlich state-of-the-art was Enum Stringifizierung usw.
> angeht.

Frage ncht gelesen? Oder kannst du erklären, wie man das gleichzeitig in 
zwei verschiedenen Sprachen und auf zwei Systemen (von denen eins ein µC 
ist) verteilt nutzt?

von A. S. (Gast)


Lesenswert?

Ich benutze dafür "C-Scripte", also Formulierungen in C, die mit 
verschiedenen, teilweise per Textfresser generierten C-Dateien 
(konsolapplikatiinen) kompiliert und gestartet werden und text-Output 
erzeugen.

Vorteil: vollständig im Makeprozess scriptbar, also keine Klicks hier 
und da. Und innerhalb der Programmiersprache, nicht außerhalb. Dadurch 
wartbar, flexibel und einfache Prüfungen.

von Dr. Sommer (Gast)


Lesenswert?

Rolf M. schrieb:
> Oder kannst du erklären, wie man das gleichzeitig in
> zwei verschiedenen Sprachen und auf zwei Systemen (von denen eins ein µC
> ist) verteilt nutzt?

Dr. Sommer schrieb:
> Dementsprechend
> könntest du deine enums zentral in eine Header-Datei packen und diese im
> uC-Code inkludieren und normal verwenden. Auf dem PC inkludierst du die
> Datei in ein C++-Programm welches einen dieser Tricks nutzt

von Vincent H. (vinci)


Lesenswert?

Rolf M. schrieb:
> Wie sollen sie das tun, wenn der Code durch dieses externe Skript
> erzeugt wird? Ich kann dann gar keine enum-Werte benutzen, die nicht
> vorher vom Skript generiert worden sind.

Und was hindert irgendwen dran in dem erzeugten Code einen Enum Wert 
hinzuzufügen? Ich bin zwar im Normalfall schon ein Freund von "richtiges 
Werkzeug" usw., aber grad in so einem Fall sollte man imho möglichst 
viel im Code lassen und so wenig wie möglich über Skripts lösen.

> Frage ncht gelesen? Oder kannst du erklären, wie man das gleichzeitig in
> zwei verschiedenen Sprachen und auf zwei Systemen (von denen eins ein µC
> ist) verteilt nutzt?

Zwei Systeme wären wohl kein Problem, den Teil mit den zwei Sprachen hab 
ich aber leider sehr wohl überlesen.

von Markus F. (mfro)


Lesenswert?

Was das Extrahieren von enums anbelangt, kann evt. ctags behilflich 
sein:

ctags -f - --fields=K --C-kinds=e include/*.h sources/*.c | awk -e 
'{print $4,$6}'

von Rolf M. (rmagnus)


Lesenswert?

Vincent H. schrieb:
> Rolf M. schrieb:
>> Wie sollen sie das tun, wenn der Code durch dieses externe Skript
>> erzeugt wird? Ich kann dann gar keine enum-Werte benutzen, die nicht
>> vorher vom Skript generiert worden sind.
>
> Und was hindert irgendwen dran in dem erzeugten Code einen Enum Wert
> hinzuzufügen?

Die Datei hat einen Namen und/oder liegt in einem Verzeichnis, woraus 
sofort erkennbar ist, dass sie autogeneriert ist. Im Code steht 
zusätzlich oben ein auffälliger Kommentar, dass diese Datei 
autogeneriert wird und das Buildsystem sie überschreibt. Das tut es dann 
auch. Sobald also jemand das alles ignoriert und trotzdem da einen Wert 
hinzufügt, ist der beim nächsten Compiler-Lauf wieder weg, und man 
bekommt bei Benutzung dieses Wertes einen Fehler.

> Ich bin zwar im Normalfall schon ein Freund von "richtiges
> Werkzeug" usw., aber grad in so einem Fall sollte man imho möglichst
> viel im Code lassen und so wenig wie möglich über Skripts lösen.

Wenn es denn nur an einer Stelle wäre bzw. wenn der Code an allen 
Stellen der gleiche wäre. Aber das ist hier nicht der Fall. Es gibt zwei 
Stellen, die was unterschiedliches tun, aber immer zusammen passen 
müssen. Bietet sich also an, das zu automatisieren, denn von Hand ist 
schon von vorne herein klar, dass das irgendwann mal nicht passt.

>> Frage ncht gelesen? Oder kannst du erklären, wie man das gleichzeitig in
>> zwei verschiedenen Sprachen und auf zwei Systemen (von denen eins ein µC
>> ist) verteilt nutzt?
>
> Zwei Systeme wären wohl kein Problem,

Inwiefern? Es geht ja darum, enums, die auf einem der Systeme als Zahl 
ausgegeben werden, in einem anderen System in Strings zu wandeln. Ich 
wüßte nicht, wie der Code das bewerkstelligen könnte. Ich halte da ein 
Skript, das für beide Seiten den Code generiert, für am sinnvollsten und 
auch robustesten.

: Bearbeitet durch User
von Michael R. (mr-action)


Lesenswert?

Moin Moin,

vielen Dank für eure ganzen Antworten! Was ich noch vergessen habe: Die 
Idee den Enums für beide Seiten zu generieren hatte ich schön - mein 
Problem ergibt sich grade aber daraus, das ich auch Enums aus externen 
libs in den Logdaten habe und deren Header sind halt schon da - die neu 
zu generieren macht ja wenig Sinn. Ob die sich bei einem Update der lib 
jemals ändern weiß ich zwar nicht - aber abschreiben und nachher 
fehlende (oder schlimmer geänderte) Werte zu haben ist mir da zu 
riskant...

Ich werde mir nächste Woche mal den XML Export von doxygen und ctags 
ansehen. Die Reflektion Geschichten sind zwar kein echtes Problem - aber 
es ist halt doch etwas aufwändiger in der Umsetzung als einfach nur ein 
Tool über die entsprechenden Header laufen zu lassen.

Viele Grüße
Michael

von Vincent H. (vinci)


Lesenswert?

Rolf M. schrieb:
> Inwiefern? Es geht ja darum, enums, die auf einem der Systeme als Zahl
> ausgegeben werden, in einem anderen System in Strings zu wandeln. Ich
> wüßte nicht, wie der Code das bewerkstelligen könnte. Ich halte da ein
> Skript, das für beide Seiten den Code generiert, für am sinnvollsten und
> auch robustesten.

So wie in jedem anderen cross-platform Code auch?
1
void print(enum E e) {
2
#ifdef __x86_64__
3
  printf("%s\n", to_str(e));
4
#else
5
  printf("%d\n", e);
6
#endif
7
}

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Trotzdem hier noch ein Beispiel, wie es komplett in C geht:
1
#include <assert.h>
2
3
#include "map.h"
4
5
#define DEF_ENUMSTR_I2(name,assign) case name: return #name; break;
6
#define DEF_ENUMSTR_I(elem) EVAL0(DEF_ENUMSTR_I2 elem)
7
8
#define DEF_ENUM_I2(name,assign) name assign
9
#define DEF_ENUM_I(elem) EVAL0(DEF_ENUM_I2 elem)
10
#define DEF_ENUM(name,...) enum name { MAP_LIST(DEF_ENUM_I, ## __VA_ARGS__) }; const char* name##ToStr (enum name val) { switch (val) { MAP(DEF_ENUMSTR_I,## __VA_ARGS__ ) default: assert (0); return 0; break; } }

Die "map.h" Datei ist diese: 
https://github.com/swansontec/map-macro/blob/master/map.h

Wenn obiger Code in einer "enunmstr.h" vorliegt, kann man das so nutzen:
1
#include <stdio.h>
2
#include "enumstr.h"
3
4
DEF_ENUM(Animals,(Cat,),(Dog,=3),(Mouse,=42))
5
6
void test (enum Animals a) {
7
  puts (AnimalsToStr (a));
8
}
9
10
int main () {
11
  test (Cat);
12
  test (Dog);
13
  test (Mouse);
14
//  test (100);
15
}

Die Definitionssyntax ist nicht sehr schön, generiert aber ein normales 
enum und zusätzlich eine Funktion AnimalsToStr, welche aus einem "enum 
Animals" einen "const char*" macht. Auf dem uC braucht man die Funktion 
natürlich nicht; indem man sie einfach nie aufruft, wird sie 
wegoptimiert. Auf dem PC inkludiert man den selben Code und benutzt die 
Funktion um Strings zu generieren.

Zusätzliche externe Tools zum (C-) Code Generieren sollten nur eine 
Notlösung sein; man hat immer das Problem dass diese unflexibel sind, 
sich nicht gut ins Buildsystem integrieren lassen, man Code auf dem 
kompilierenden System ausführen muss (bei Python o.ä. muss die runtime 
installiert sein, bei C braucht's einen Compiler für den Host). 
Insbesondere ist der generierte Code fix, es können keine Daten in die 
Code-Erzeugung eingeschleust werden; durch den Compiler generierter Code 
kann sich auf andere Makros o.ä. beziehen.

von Michael R. (mr-action)


Lesenswert?

Moin Moin,

also ich möchte euch meine Lösung nicht vorenthalten...
doxygen und ctags extrahieren zwar hervorragend die enums, aber leider 
muss ich sie dann immernoch selbst hoch zählen.

Daher bin ich wirklich auf Qt und die QMetaEnum Klasse gegangen. Ich 
includiere die Header mit den Enums jetzt in eine Klasse (wirklich 
mitten drin) schreibe da den Q_ENUM Kram drunter und generiere eine 
Funktion die dann über alle Enums iteriert.

Testcode:
1
    QMetaEnum metaEnum = QMetaEnum::fromType<MyTestEnum>();
2
    for(int i=0; i<metaEnum.keyCount(); ++i)
3
    {
4
        const char* name = metaEnum.key(i);
5
        int value = metaEnum.value(i);
6
7
        qDebug() << "Name[" << name << "] = Value[" << value << "]";
8
    }

Vielen Dank für eure Tipps!!

Viele Grüße
Michael

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.