Inhalt
Topic:.Jc.Cheader.
Last changed: 2018-01-07
Headerfiles liefern als Teil der C-Compilierung eigentlich nur die beim Compilieren notwendigen gemeinsamen Teile, also vorderhand
die Definition externer Labels, die vom Linker in anderen Compilierungseinheiten oder Libraries aufzufinden sind. Dazu kommt
die struct
-Definition gemeinsam notwendiger Daten. Im C++-Bereich sieht es nicht anders aus, nur statt struct
class
und Definition der Methoden innerhalb der class
.
So sehen auch Headerfiles häufig aus, kurz, durchsetzt mit #ifdef
für die verschiedenen zu berücksichtigenden Varianten, Comment-frei.
Headerfiles stellen sachlich aber das Interface einer Compilierungseinheit nach außen dar. Dort ist deklariert, was die Compilierungseinheit leistet, wie man sie einsetzen kann. Wenn man diesem Gedankengang folgt, dann sollten Headerfiles wie folgt strukturiert sein:
Strenge Zuordnung Filename.h zur Compilierungseinheit
Dokumentation der jeweiligen Deklarationen und Datendefinitionen
Strukturierung zusammengehöriger Teile
Keine bedingte Compilierung, eine Schnittstelle sollte implementierungsunabhängig sein.
Damit gewinnen aber Headerfiles an Bedeutung für den Software-Entwicklungsprozess: Dokumentation und Strukturierung. Mehr noch: Headerfiles sind einsetzbar beispielsweise zur Beschreibung der Daten auf Kommunikationsleitungen, unabhängig davon ob nach außen die C-Programmierung direkt verwendet wird (häufig nur innen, in der Realisierungsebene verwendet).
Topic:.Jc.Cheader..
Der Dokumenationsstil ist nirgends festgeschrieben. Man sollte sich aber an Standards halten. Verbreitet sind bestimmte Default-Styles, die mit Doxygen sich für Header etabliert haben. Allerdings nicht einheitlich. Schaut man dagegen in einem Seitenblick auf Java, dann gibt es dort eine vollkommen einheitliche Art der Dokumentation. Diese lässt sich problemlos auch in Headerfiles anwenden und wird mit den gezielten Einstellungen auch von Doxygen erkannt. Nachfolgend das Muster:
/**This struct is used for ... * Details */ typedef struct MyStruct_t { /**Meaning of the element. */ int32_t data_xy; } MyStruct; /**This operation is used for... * @param arg1 argument for... * @return the number of... */ int doSomething_MyStruct(MyStruct* thiz, int arg1);
In dem Muster ist neben dem Javadoc-Stil folgendes auffällig und empfehlenswert:
Kommentierung in englisch
Typedefinition mit Schlüsselwort typedef und Definition des Tagnamens mit _t
: wichtig für Vorwärtsdeklarationen.
C-Funktionen werden als Operation bezeichnet. Das ist lingual: Das Wort Funktion gegenüber Prozedur stammt aus den Sprachen der 1970/80-ger und insoweit in C verankert. Methode klingt etwas zu stark objektorientiert-lastig. Auch bei UML-Tools hat sich der Begriff Operation anstatt Methode etabliert. Er ist allgemeingültig.
In struct
sollten nur int-Typen mit definierter Datenbreite verwendet werden, da damit der Datenaufbau unabhängig vom Zielsystem definiert
ist. Nur so sind die Daten plattformunabhängig eindeutig.
Topic:.Jc.Cheader.zbnf.
Der ZBNF-Parser ist seit 2005 nur mit kleineren Erweiterungen in Verwendung unter anderem für das Parsen von Inputtexten auch dieser Dokumentation, für Java-Programme innerhalb des Java2C-Translators, für JZtxtcmd-Scripts und eben auch für Headerfiles. Die seit Anfang benutzte Syntax ist im File zbnfjax:zbnf/Cheader.zbnf verankert.
Für die Syntax in Cheader.zbnf
gelten folgende Prämissen:
Eigentlich wird ein Headerfile beim Compilieren zunächst mit dem Preprocessor des Compilers vorverarbeitet. Insbesondere werden damit Makros (#define
) aufgelöst. Dieser Schritt entfällt bewusst.
Damit sind in den Headerfiles bestimmte Informationen, die mit Makros formuliert werden, eben bewusst herauslesbar.
Ein Preprocessor verwirft meist auch Kommentare, diese möchte man aber haben.
Die gezielte Verwendung von Makros ist hilfreich. Bei verkomplizierenden Makros, die allerdings auch für die C-Programmierung eher nicht so sehr dienlich sind, versagt das parsen.
Im Header können insbesondere für inline
-Funktionen im C++-Bereich auch Implentierungen enthalten sein. Diese interessieren allerdings für die Informationsentnahme
aus einem Headerfile meist nicht. In der Regel werden diese Implementierungen aber korrekt geparst und das Ergebnis auch abgelegt.
Da die Syntax der Implementierungen aber aus C/++-Sicht nochmals komplexer sein kann, können diese Teile auch vom Parsen ausgenommen
werden. Im Headerfile muss dafür dann eine Pseudo-bedingte Compilierung mit
#ifndef __ignoreInCheader_zbnf__ ... #endif//__ignoreInCheader_zbnf__
stehen. Dieser Compilerschalter wird nirgends gesetzt, damit ist der Bereich dazwischen für den C-Compiler aktiv. Der ZBNF-Parser überliest aber diesen Teil.
Es gibt eine dokumentatische Untergliederung von Bereichen des Headerfiles mit Comments der Art
/*@CLASS_C name @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/ ...struct und damit zusammenhängende Operation-Prototypen
Diese sind für die Objektorientierte Programmierung in C vorgesehen. Diese Untergliederung wird eigentlich nur für die Dokumentationsgenerierung
aus Headerfiles verwendet und ist mittlerweile eigentlich obsolete. Die Erkennung der Zusammengehörigkeit von struct
und deren Operationen kann auch aus der Namensgebung ermittelt werden. - Für Neuentwicklungen also nicht empfohlen.
Trotz dieser Prämissen sollten jegliche ordentlich geschriebene Headerfiles (ohne strukturzerstörende Makros) geparst werden können. Bestimmte zielgerichtet zweckmäßige Makros oder doch nicht berücksichtigte Syntaxmöglichkeiten der C(++-Programmierung können mit Erweiterung der Syntax in Cheader.zbnf berücksichtigt werden.
Topic:.Jc.Cheader..
Es gibt hierbei 3 Möglichkeiten:
Wird der srcJava_Zbnf/org/vishia/zbnf/ZbnfParser aus Java aufgerufen, dann stehen die Parserergebnisse als Java-Daten als Kartenstapel abgelegt oder als Container von Nodes zur Weiterverarbeitung in Java bereit. Dieses System wird auch für das in Java geschriebene Tool srcJava_Zbnf/org/vishia/header2Reflection/CmdHeader2Reflection benutzt, das ab 2018 obsolete ist. Siehe Punkt 3.
Der Aufruf srcJava_Zbnf/org/vishia/zbnf/Zbnf2Xml oder als Kommandozeile
java -cp path/to/zbnf.jar org.vishia.zbnf.Zbnf2Xml -i:input.h -s:path/to/zbnf/Cheader.zbnf -y:output.xml
legt das Parsing-Ergebnis als XML ab. Man kann nun in der XML-Welt weiterverarbeiten bzw. hat einen allgemeinen Blick darauf, wie der Inhalt des Headers erkannt wurde.
Der Aufruf über ein JZtxtcmd-Script kann die Java-Funktionen direkt aufrufen, dabei im Script entscheiden wie was ausgeführt werden soll. Man spart sich hier die Erstellung eines speziellen Java-Programmes. Gleichzeitig kann in diesem Script die Konvertierung in eine textuelle Ausgabeform programmiert sein. Vom Verfasser wurde diese Technologie erstmals entwickelt und benutzt, um in Mathworks Simulink Die Wrapper für S-Funktionen aus den Headerfiles zu generieren. Das Verfahren wurde dann ab Anfang 2018 auch für die Reflectiongenerierung aus Headerfiles verwendet.
Es liegt nun nahe für eigene Arbeiten das dritte Verfahren zu verwenden, wenn man nicht Java programmieren möchte oder XML
im Auge hat. Das dritte Verfahren legt die Daten in einer Instanz von srcJava_Zbnf/org/vishia/header2Reflection/CheaderParser mit seinen Subclass-Instanzen als Baum ab. Man kann diese Daten dann direkt in einem JZtxtcmd-Script herauslesen und passend
ein Output-Textfile zusammensetzen. Die Java-class CheaderParser
muss für alle Semantik-Ergebnisse auch entsprechende Ziel-Elemente haben. Diese werden per Reflection mittels der Java-Funktionalität
aufgesucht. Wird ein Zielelement nicht gefunden, dann gibt es eine Klartextfehlermeldung. Diese kann genutzt werden, um die
noch fehlende Ziel-Elemente einzubringen. Das ist der Stand 2018-01.
Es gibt eine weitere Überlegung, die im Moment(2018-01) noch nicht realisiert ist:
Der Zbnf-Parser sollte sein Parserergebnis grundsätzlich in einer passenden Variablenstruktur für JZtxtcmd ablegen. Der Baumaufbau dieser Variablen hängt dann nur von der Semantik im Zbnf-File ab. Das JZtxtcmd-Script kann dann auf die Parserergebnisse direkt als Variable zugreifen. Bisher legt zwar der ZBNF-Parser die Daten ebenfalls in einem Tree-Container ab. Dieser ist aber zu stark XML-lastig. Ein Umsetzer von der einen Tree-Container-Form in die andere ist nun nicht gerade speicherplatzeffektiv. Die Containertypen unterscheiden sich allerdings etwas, so dass eine einfache Umsetzung nicht möglich ist und die vollständige Lösung bisher aus Zeitgründen gescheitert ist.
Kontrolle der abgelegten Daten:
Über die Funktion srcJava_vishiaBase/org/vishia/cmd/JZtxtcmdTester#dataHtml(java.lang.Object, org.vishia.cmd.JZtxtcmdFilepath) oder aus Java gerufen mit der class srcJava_vishiaBase/org/vishia/util/DataShow kann der Inhalt einer Java-Instanz mit allen referenzierten Daten als html ausgegeben werden. Damit ist eine manuelle Navigation in den Daten möglich, insbesondere für Datenpfade für das JZtxtcmd-Script.
Topic:.Jc.Cheader..
Die Reflectiongenerierung in der CRuntimeJavalike wird ab 2018-01 successive auf die hier vorgestellte Variante umgestellt. Das Start-Script, beispielsweise für ...vishia/Jc/CRuntimeJavalike/make/genRefl_FwcJc.jz.cmd
sieht wie folgt aus:
REM start problem: The batch needs the zbnf.jar File. Either the ZBNFJAX_HOME environment variable is set already, REM or it is set via a found setZBNFJAX_HOME.bat, or it is on D:\vishia... or you should adapt it. call setZBNFJAX_HOME.bat if "%ZBNFJAX_HOME%" == "" set ZBNFJAX_HOME=D:/vishia/ZBNF/zbnfjax java -cp %ZBNFJAX_HOME%/zbnf.jar org.vishia.jztxtcmd.JZtxtcmd %0 if ERRORLEVEL 1 pause exit /B ==JZtxtcmd== include $ZBNFJAX_HOME/jzTc/Cheader2Refl.jztxt.cmd; currdir=scriptdir; Fileset headers = ( source:Fwc/*.h , source:Jc/*.h ); main() { mkdir T:/header/Jc; zmake "../genRefl/*.crefl" := genReflection(..&headers, html="T:/header"); <+out>success<.+n> }
Es ist eine Kombination aus batch-File (Windows) und JZtxtcmd-Script. Letzteres ist ein Zmake-Script. Es werden die Header
im Fileset
benannt. Die Arbeit wird dann im includiertem Script ...zbnfjax/jzTc/Cheader2Refl.jztxt.cmd
ausgeführt. Die html-Ausgabe wird hier bestimmt oder abgeschaltet (html-Argument weglassen).
Der Kern des JZtxtcmd-Scripts für das einzelne Parsen der Header sieht dann wie folgt aus:
Obj headerTranslator = java new org.vishia.header2Reflection.CheaderParser(console); sub genReflectionFile(Obj filepath :org.vishia.cmd.JZtxtcmdFilepath, String fileRefl, String html = null) { ##java org.vishia.util.DataAccess.debugMethod("setSrc"); Obj args = java new org.vishia.header2Reflection.CheaderParser$Args(); args.addSrc(filepath.absfile(), filepath.localname()); args.setZbnfHeader(<:><&scriptdir>/../zbnf/Cheader.zbnf<.>); Obj headers = headerTranslator.execute(args); for(headerfile: headers.files){ <+out>generate <&headerfile.fileName> <.+n> ##Only to help change the script, output all parsed data: if(html !=null && html.length()>0) { <+out>html: <&html>/<&headerfile.fileName><.+n> mkdir <:><&html>/<&headerfile.fileName>'<.>; test.dataHtml(headers, File:<:><&html>/<&headerfile.fileName>.html<.>); } call genReflectionHeader(headerfile = headerfile, fileRefl = fileRefl); } }
Die Instanz des headerTranslator
ist global im Script angelegt. Sie wird wiederverwendet. Diese Subroutine im Script wird mit den einzelnen bereits separierten
Files gerufen, also mehrfach. Der Filename wird in die angelegte args
-Variable geschrieben. Der CheaderParser
kann mehrere Files verarbeiten, verarbeitet hier aber nur einen. Damit ist die for...
-Schleife singular, sie holt die einzige headerfile
-Instanz aus dem Container headers.files
heraus.
In diesem Script wird die html-Testausgabe gezeigt. Die Reflectiongenerierung erfolgt dann mit der danach gerufenen Subroutine.