MoinMoin
ich bin grad dabei ein wenig Ordnung in mein Programm zu bringen, in
welchem ich mir eine TWI-Routine programmiert hab, um ein Sensorshield
abzufragen.
Dazu hab ich nun alles was sich auslagern lies (TWI_ISR,
Shield-Routinen)
in eine blaTWi.h und eine blaSHIELD.h und die entsprechenden .c
ausgelagert.
In den .h stehen die ganzen #defines für Register usw und die
Funktionsprototypen. In den .c dann die Funktionen selbst.
Wenn ich nun nun im main.c die .h und die .c #include-de dann läuft
alles wie am Schnürchen. Nur seh ich das in "professionellen" "Libs" nie
so.
( Das hab ich grad auf der Suche nach Lösungen gelernt, eine Libary ist
eigentlich etwas fertig kompiliertes, was ist dann so ein Sammelsorium
von lesbarem Quelltext.c und .h eigentlich genau?(Nur ne Nebenfrage,
eigl unwichtig) )
Dort wird immer nur die .h eingebunden. Wenn ich aber das #include bla.c
in der bla.h mache, dann beschwert er sich in der bla.c er würd die
ganzen Sachen die in der bla.h stehen, nicht kennen. Daraufhin hab ich
noch probiert, die bla.h in der bla.c nochmal einzubinden. Hat aber auch
zu nix geführt, was aber eigentlich auch so sein sollte, da ich in der
.h #indef #def benutzt hab.
P.P.S.
Falls ihr euch das antun wollt, im Anhang die Dateien, um die es geht.
Ich versuchs grad erstmal nur mit ...SHIELD_DRIVER als die MyTWI lohnt
eigentlich nicht anzuschauen.
Das kann doch eigentlich nicht so kompliziert sein? Aber irgendwie
stellte es sich die paar Male die ich es bisher versucht hab, als nahezu
unmöglich raus :D
Also ich bitte dringendst um Hilfe.
P.S.
Noch ne kleine Nebenfrage. Auf der Suche nach Hilfe bin unter anderem in
nem Online C-Buch auf das hier gestoßen. (Nur ausm Kopf, den Link hab
ich schon wieder nicht mehr).
a.h
#define foo
void bar(void)
{
mach irgendwas;
}
Das wäre doch aber kein Funktionsprototyp mehr? Und müsste eigentlich in
die .c? Ein Funktionsprototyp wäre doch sowas hier:
void bar(void);
?
MfG Chaos
J. T. schrieb:> Ich versuchs grad erstmal nur mit ...SHIELD_DRIVER als die MyTWI lohnt> eigentlich nicht anzuschauen.
Geht besonders an den, der sichs grad anschaut. Sorry es fiel mir erst
Sekundenbruchteile nachdem ich auf Absenden geklickt hatte ein.
C-Files includet man nicht!
In das header file kommen Funktionsprototypen und defines, datentypen
etc.
In der C-Datei sind dann die Funktionen selbst, welche dann natürlich
auch ihr eigenes H-File includen muss um ggf. auch ihre eigenen
Funktionen (über die Funktionsprototypen zu kennen, sofern sie nicht in
der Reihenfolge des Aufrufs im C file vorkommen).
"Private" Funktionen, also jene die nur von dem "Modul" aufrufbar sein
sollen kannst du in der C-Datei als Prototyp angeben und ggf. noch
static deklarieren.
Gibt dazu genug im Netz:
https://de.wikibooks.org/wiki/C-Programmierung:_Eigene_Headerhttps://www.cs.cf.ac.uk/Dave/C/node35.htmlhttp://www.csse.monash.edu.au/courseware/cse2304/hndtC.html
J. T. schrieb:> Wenn ich nun nun im main.c die .h und die .c #include-de dann läuft> alles wie am Schnürchen. Nur seh ich das in "professionellen" "Libs" nie> so.
Bitte keine *.c Dateien includen. Die verschiedenen *.c Dateien werden
alle separat compiliert und später zusammen gelinked. Wie das im
einzelnen geht hängt von deiner Entwicklungsumgebung ab. Man kann aus
den *.c auch eine shared/static Library machen was du wohl mit "fertig
kompiliert" meinst. Für eigene Libraries macht das aber nur Sinn wenn
die Library einigermaßen fertig ist und sich nicht ständig ändert. Ich
würde dir daher empfehlen erstmal bei deiner Sammlung *.c/*.h Dateien zu
bleiben.
J. T. schrieb:> Das wäre doch aber kein Funktionsprototyp mehr? Und müsste eigentlich in> die .c? Ein Funktionsprototyp wäre doch sowas hier:
Ist richtig. Wenn es unbedingt in den Header soll kann man das mit
inline machen.
Timmo H. schrieb:> https://de.wikibooks.org/wiki/C-Programmierung:_Eigene_Header
Danke für den Link, ich versuchs grad danach. Übrigens im AVRStudio 6.2
auf nem ATMEGA2560
In der .h wie dort beschrieben als erstes #ifndef def,
dann kommen die #defines, dann die Deklarationen, dann die
Funktionsprototypen. In der .c auch wie dort beschrieben die .h
includieren, dann die Variablen definieren, dann Funktionen. Siehe
Anhang, die ist zu lang für hier.
1
#ifndef _MyTWI_H_
2
#define _MyTWI_H_ 1
3
4
#define TWI_Error_Flag 7
5
#define TWI_Read_Flag 1
6
#define TWI_Send_Flag 0
7
#define TWI_StreamingMode_Flag 2
8
9
10
#define TWI_WatchdogMax 100 //Kann nach Bedarf verändert werden.
Aber bei den Deklarationen gehts schon los, im Falle das ich in der
X_NUCLEO_IKS01A1.c (Standardmäßig die main.c) das #inlcude "MyTWI.h"
auskommemtiere. Mit extern gibt es einen Error: 63 ld returned 1 exit
status collect2.exe 0 0 X_NUCLEO_IKS01A1
und 62 undefinierte Referenzen auf die oben deklarierten Variablen.
Lasse ich extern weg, bleibt der eine Fehler gleich, aber die
undefinierten Referenzen schrumpfen auf 12 zusammen, und beziehen sich
auf die Funktionen TWI_ClearRead/WriteBuffer. Komischerweise sollen alle
Fehler in der X_NUCLEO_IKS01A1.c line1 collumn1 sein.... Damit kann
ich irgendwie garnichts anfangen.
Im Anhang nochmal der aktuelle Stand.
Sebastian V. schrieb:> Bitte keine *.c Dateien includen. Die verschiedenen *.c Dateien werden> alle separat compiliert und später zusammen gelinked.
Danke für die Erläuterung, aber das hatte ich dann schon rausgefunden,
das man dass nicht macht. Aber da ich es ohne nicht hinbekomme, frage
ich hier nach.
In Atmel Studio musst Du die C-Dateien in das Projekt hinzufügen
(Solution Explorer -> Dein Projekt -> Add Existing Files o.ä.). Dann
werden sie mitcompiliert und gelinkt, ohne dass Du sie includen musst.
In das H-File gehören übrigens ausschließlich Dinge, die der Benutzer
der Library benötigt. Interne Dinge, die Du nur zum Implementieren der
Library brauchst, wie z.B. Registerdefinitionen, sind am Anfang der
C-Datei besser aufgehoben.
Falls Du bestimmte Sachen (Puffergrößen, verwendete Pins, ...)
konfigurierbar machen möchtest, würde ich dafür ein eigenes
Config-Header-File verwenden, das in jeder Anwendung individuell sein
kann. Damit hast Du immer die Möglichkeit, z.B. eine neue Funktion in
den Hauptheader hinzuzufügen, ohne dass Du das von Hand in sämtlichen
Anwendungen mit angepassten Konfigurationen nachziehen musst.
Fabian O. schrieb:> In das H-File gehören übrigens ausschließlich Dinge, die der Benutzer> der Library benötigt.
Schreib' nicht "Library". Hier ist weit und breit keine Library im
Spiel.
Rufus Τ. F. schrieb:> Fabian O. schrieb:>> In das H-File gehören übrigens ausschließlich Dinge, die der Benutzer>> der Library benötigt.>> Schreib' nicht "Library". Hier ist weit und breit keine Library im> Spiel.
Das kam von dir schon öfter, ist und bleibt aber falsch:
https://de.wikipedia.org/wiki/Programmbibliothek
Fabian O. schrieb:> In Atmel Studio musst Du die C-Dateien in das Projekt hinzufügen> (Solution Explorer -> Dein Projekt -> Add Existing Files o.ä.). Dann> werden sie mitcompiliert und gelinkt, ohne dass Du sie includen musst.
Ich bin langsam am verfzweifeln. Genau darauf bin ich bei meinen
Nachforschungen dann auch irgendwann gestoßen. Ich hab irgendwann auch
keinen anderen Grund mehr gesehen. Die Dateien sind nun nämlich so
aufgebaut, wie is in den Links beschrieben wird.
Wenn ich dann in der main.c .h einbinde, findet er halt den Kram aus .c
nicht, und wenn die ich die .c per #include einbinde, bin ich inzwischen
soweit, dass er sich beschwert, die ISR wäre doppelt definiert.
Wenn ich dann aber oben zitiertes versuche, dann klick ich entweder auf
die Pünktchen, um ein Fenster zu bekommen, wo dann meine
Ordnerstrukturen angezeigt werden und ich mir aussuchen könnte, welche
Dateien ich angeben möchte, jedoch stürzt das Studio konsequent ab, wenn
ich dort raufklicke. Wenn ich den Pfad per Hand in die Zeile eintrage,
merkt er sich ums Verrecken nicht, was ich da eingetragen habe....
le x. schrieb:> ist und bleibt aber falsch:
Wenn wir hier seit Jahrzehnten etablierte Fachbegriffe durch allgemeine
Wikipedia-Interpretationen ersetzen, dann ja.
Hier geht es aber um C, bzw. um C-Compiler & Linker. Und da ist klar
definiert, was eine Library ist.
Auf die Libarygeschichte bin ich im Eröffnungspost schon eingegangen...
Eine Libary ist etwas fertig kompiliertes....
Aber das ist hier nicht Thema der Diskussion. Bitte beim Thema bleiben.
J. T. schrieb:> Auf die Libarygeschichte bin ich im Eröffnungspost schon eingegangen
Du ja. Andere in diesem Thread habens nicht begriffen.
Übrigens: Lib_r_ary.
> Bitte beim Thema bleiben.
Alles relevante ist Dir bereits mitgeteilt worden:
1. Keine .c-Dateien mit #include einbinden
2. Alle .c-Dateien, aus denen Dein Projekt besteht, in die
Projektverwaltung Deines Entwicklungssystemes eintragen
Und wenn Dein Entwicklungssystem (anscheinend AVR-Studio 6.2) damit
Probleme hat, wirst Du entweder in dessen Dokumentation nachsehen
müssen, wie man das macht, oder Deine Installation des
Entwicklungssystems ist kaputt. Du bist ganz sicher nicht der erste, der
ein Projekt, das aus mehreren Quelltextdateien besteht, mit AVR-Studio
bearbeitet.
J. T. schrieb:> Wenn ich dann in der main.c .h einbinde, findet er halt den Kram aus .c> nicht, und wenn die ich die .c per #include einbinde, bin ich inzwischen> soweit, dass er sich beschwert, die ISR wäre doppelt definiert.
Genaue Fehlermeldungen?
Rufus Τ. F. schrieb:> Genaue Fehlermeldungen?
Error 4 redefinition of 'ISR' C:\Users\Ichich\documents\atmel
studio\6.2\GccApplication3\GccApplication3\MyTWI.c 60 1
GccApplication3
Ist die einzig verbleibende Fehlermeldung. Sonst noch n paar Warnings
Warning 7 control reaches end of non-void function [-Wreturn-type]
C:\Users\Ichich\documents\atmel
studio\6.2\GccApplication3\GccApplication3\MyTWI.c 263 1
GccApplication3
Hiervon 2(jeweils bei den ISRs):
Warning 1 return type defaults to 'int' [enabled by default]
C:\Users\Ichich\documents\atmel
studio\6.2\GccApplication3\GccApplication3\MyTWI.c 43 1
GccApplication3
Und hier von auch 2(bei den ISRs):
Warning 2 type of '__vector_21' defaults to 'int' [enabled by default]
C:\Users\Ichich\documents\atmel
studio\6.2\GccApplication3\GccApplication3\MyTWI.c 43 1
GccApplication3
Rufus Τ. F. schrieb:> Und wenn Dein Entwicklungssystem (anscheinend AVR-Studio 6.2) damit> Probleme hat, wirst Du entweder in dessen Dokumentation nachsehen> müssen, wie man das macht, oder Deine Installation des> Entwicklungssystems ist kaputt.
Laut Doku geb ich das einfach an der genannten Stelle an, jedoch stürzt
es bei mir ab....
Mal sehen, Neuinstallation?
Die Fehlermeldungen und Warnungen lassen darauf schließen, daß der
Compiler Deine ISR-Syntax nicht kennt.
Ich verwende weder avr-gcc noch AVR-Studio, deswegen kann ich Dir nicht
exakt sagen, wo der Fehler jetzt liegt - aber wenn ich mir ansehe,
welche zum Compiler gehörenden Dateien Du einbindest -- keine! -- dann
würde ich vermuten, daß mindestens
Rufus Τ. F. schrieb:> Ich verwende weder avr-gcc noch AVR-Studio, deswegen kann ich Dir nicht> exakt sagen, wo der Fehler jetzt liegt - aber wenn ich mir ansehe,> welche zum Compiler gehörenden Dateien Du einbindest -- keine! -- dann> würde ich vermuten, daß mindestens> #include <avr/interrupt.h>
Oh ja, das ist ein guter Tipp, die <avr/interrupt.h> ist nur in der
main.c drinne.
Ich installier grad Studio7, mal sehen wenn das fertig ist, werd ichs
nochmal versuchen.
Die Datei gehört mindestens in jedem *.c-File eingebunden, in dem Du
Interrupthandler schreibst, also "ISR(vektor)" verwendest.
<avr/io.h> einzubinden dürfte auch nicht störend sein.
Ich danke dir Rufus. Nun kompiliert er fehler- und warnungsfrei.
Ich hasse das so sehr, wenn etwas an so einer Kleinigkeit liegt, die man
ewig übersieht. Andrerseits ist es auch sehr erleichternd, dass es nun
läuft =)
MfG Chaos
Ahhhhh doch nicht... Zu früh gefreut. Ich hatte noch das #include .c in
der main.... =(
Ich hab nun das Studio7 installiert, und dort kann ich einstellen, in
welchem Ordner die Files liegen.
Folgende 3 Fehlermeldung schmeißt er mir:
Severity Code Description Project File Line
Error undefined reference to `TWI_init' GccApplication3
C:\Users\Ichich\Documents\Atmel
Studio\7.0\GccApplication3\GccApplication3\Debug/.././GccApplication3.c
18
Error undefined reference to `TWI_Flags' GccApplication3
C:\Users\Ichich\Documents\Atmel
Studio\7.0\GccApplication3\GccApplication3\Debug/.././GccApplication3.c
20
Error ld returned 1 exit status GccApplication3 collect2.exe 0
Heißt doch im Klartext, er findet die .c immer noch nicht? Schließlich
liegen dort die Definition von TWI_Flags und die Funktion TWI_init liegt
auch dort....
Im Anhang nochmal die aktuellen Dateien, um die es geht.
J. T. schrieb:> Heißt doch im Klartext, er findet die .c immer noch nicht?
Das heisst es nicht.
Wenn dem so wäre, dann würdest du bereits vom Compiler eine
Fehlermeldung kriegen, dass die Datei MyTWI.c nicht gefunden werden
konnte.
Das heisst, wenn du MyTWI.c überhaupt mit ins Projekt eingefügt hast.
Und daran wird es wahrscheinlich auch liegen: du hast MyTWI.c nicht zum
Projekt hinzugefügt.
Die Fehlermeldung stammt vom Linker und zeigt an, dass die übersetzte
IMplementierung der Funktionen nicht gefunden werden konnte. Was kein
Wunder ist, wenn MyTWI.c nicht mit im Projekt ist.
J. T. schrieb:> und dort kann ich einstellen, in welchem Ordner die Files liegen.
Darum geht es nicht. Du musst nicht einstellen, wo irgendwelche Dateien
sind, Du musst jede *.c-Datei einzeln zur Projektverwaltung hinzufügen,
so daß sie im "Project Explorer" (oder wie auch immer das im Atmel
Studio genannt wird) auch auftaucht.
Timmo H. schrieb:> Und durch langes suchen und kapieren statt kopieren lernt man sogar aus> seinen Fehlern und macht sie nicht nochmal.
Ich hab ziemlich lang gesucht und bin fast verzweifelt. Auch ist nahezu
keine Zeile kopiert. Ich hatte mal was, in nem anderen Thread, mit der
UART drin. Da hab ich mir die Baudratenberechnung rauskopiert, die ist
praktisch.
Ansonsten hab ich jede Zeile selbst geschrieben. Die Definitionen der
Register abzutippen war ne stumpfe Fleißarbeit.
Und langsam kapiere ich ja auch. Wenn du mein Eingangsposting gelesen
hast, solltest du auch mitbekommen haben, dass ich mir sogar bewusst
war, dass man eigentlich keine .c indludedet, ich mir aber nicht anders
zu helfen wusste, und genau das dann ja der Anstoß meiner Nachfrage war.
Was ich damit eigentlich sagen will ist: Was willst du mir damit sagen?
Wieso kann hier nichtmal ein einziger Thread laufen, ohne das irgendwer
seine völlig belanglose Meinung zu irgendwas, das mit dem eigentlichen
Thema in keinster Weise zu tun hat, kund tut?
Wie kommst du darauf, das mein Code zusammenkopiert ist?
Die Statemachine für die TWI hab ich auch erst aus dem Bauch raus
runtergeschrieben, nach dem ATMEGA Datenblatt, für die
Statusregisterwerte.
Dann bin ich auf die Appnote dazu gestoßen, und bis auf ein zwei
Fehlerfälle hatte ich es genau so umgesetzt, wie dort beschrieben. Nach
der Lektüre hab ich noch die Fehler angepasst, und sie lief. Dann wollte
ich sie auslagern, hab dazu dieses und jenes gelesen (evtl nicht
verstanden) und das über einen Zeitraum von mehreren Tagen. Wobei die
meiste Zeit grübeln und nicht mehr lesen war. Und kurz vor der
Verzweiflung, fragte ich dann hier nach. Meistens konnte mir hier sehr
gut geholfen werden. Auch wenn es des öfteren solche Kleinigkeiten
waren, wie von Rufus angemerkt. Man hätte selber draufkommen können.
Aber du kennst das sicher auch, wenn du eine Zeile gefühlte 100000 mal
gelesen hast, bist du irgendwann betriebsblind.
Rufus Τ. F. schrieb:> Darum geht es nicht. Du musst nicht einstellen, wo irgendwelche Dateien> sind, Du musst jede *.c-Datei einzeln zur Projektverwaltung hinzufügen,> so daß sie im "Project Explorer" (oder wie auch immer das im Atmel> Studio genannt wird) auch auftaucht.
Sowas wie in CodeBlocks "Add File to Project"? Sowas hab ich auch schon
gesucht... Ich werd nochmal googeln statt selber zu gucken... Meld mich
gleich nochmal.
Danke nochmal für deine Unterstützung
JAWOLLJA!!!
So klappt es. Irgendwo gibt es rechts das Fensterchen, in dem man beim
Debuggen die Register angucken kann. Dort versteckt sich in einem Reiter
der "Solution Explorer", dort kann man dann mit nem Rechtsklick die
Files hinzufügen.
Also nun endgültig, vielen Dank für die Hilfe =)
MfG Chaos
Eine Frage evtl doch noch nachgeschoben. Sie ist aber nur noch
theoretischer Natur.
Ist der Unterschied zwischen der Version wo man quasiverbotenerweise das
.c in main.c includedet, und dem zum Projekt hinzufügen der, das im
ersten Fall die sämtliche .c in ein File gehen, dass dann compiliert
wird? Und im letzteren Fall werden die .c einzeln compiliert und erst
vom Linker zusammengeführt?
Warum soll man dann nicht die .c includen? Ist das nicht eigentlich
egal, wo die Files zusammengeführt werden? Da sollte am Ende doch
trotzdem das selbe bei rauskommen? Ich las irgendwo was vom
"ASIF"-Prinzip, das zwar eher was mit der Optimierung zu tun hat, aber
sollte hier nicht was ähnliches gelten?
J. T. schrieb:> Eine Frage evtl doch noch nachgeschoben. Sie ist aber nur noch> theoretischer Natur.>> Ist der Unterschied zwischen der Version wo man quasiverbotenerweise das> .c in main.c includedet, und dem zum Projekt hinzufügen der, das im> ersten Fall die sämtliche .c in ein File gehen, dass dann compiliert> wird? Und im letzteren Fall werden die .c einzeln compiliert und erst> vom Linker zusammengeführt?
Genau.
> Warum soll man dann nicht die .c includen?
Weil du es richtig machen sollst. Auch bei kleinen Projekten.
C ist so hingetrimmt, dass man Dinge einzeln kompilieren kann und nicht
alles in einem Rutsch compilieren muss. Eines der letzten Projekte, in
denen ich beteiligt war, bestand aus ein paar tausend Files. Alles
durchzucompilieren dauert etwas über eine Stunde. Da ist man doch froh,
wenn nach einer Änderung in einem C-File lediglich dieses eine C-File
neu compiliert werden muss, was in ein paar Sekunden abgeht.
Karl H. schrieb:> Weil du es richtig machen sollst. Auch bei kleinen Projekten.> C ist so hingetrimmt, dass man Dinge einzeln kompilieren kann und nicht> alles in einem Rutsch compilieren muss. Eines der letzten Projekte, in> denen ich beteiligt war, bestand aus ein paar tausend Files. Alles> durchzucompilieren dauert etwas über eine Stunde. Da ist man doch froh,> wenn nach einer Änderung in einem C-File lediglich dieses eine C-File> neu compiliert werden muss, was in ein paar Sekunden abgeht.
Ja das leuchtet mir ein. Danke dir für deine wie immer treffenden Worte!
Ich denke, nun sind tatsächlich alle Klarheiten beseitigt :D
Ein anderer Grund, warum man mehrere C-Dateien getrennt kompiliert, sind
Daten und Funktionen, die als "static" deklariert sind. Diese sind nur
innerhalb der jeweiligen Übersetzungseinheit gültig.
Wenn man jede C-Datei einzeln kompiliert, ist jede C-Datei (samt den
Header-Files, die sie inkludiert) eine eigene Übersetzungseinheit. Man
kann also problemlos jeweils ein "static int x" in zwei C-Dateien haben,
ohne dass es einen Konflikt gibt.
Wenn man dagegen den Code aus allen C-Dateien in eine C-Datei inkludiert
und compiliert, gibt es nur eine Übersetzungseinheit und dementsprechend
Fehler, wenn in mehreren Dateien static-Elemente mit gleichem Namen
definiert sind.
Bei kleineren Projekten, gerade fuer Mikrocontroller, kann es durchaus
sinnvoll sein, alles zu einem grossen Sourcefile zusammen zu setzen: Der
Compiler kann so wesentlich mehr und besser optimieren.
xXx schrieb:> Bei kleineren Projekten, gerade fuer Mikrocontroller, kann es> durchaus> sinnvoll sein, alles zu einem grossen Sourcefile zusammen zu setzen: Der> Compiler kann so wesentlich mehr und besser optimieren.
Seitdem es LTO (Link Time Optimization) gibt ist das auch nicht mehr der
Fall.
Die Frage ob man c-Dateien direkt inkludiert oder nicht scheint mir
schnell in einen Glaubenskrieg auszuarten.
Ich habe das auch mal so gelernt, dass ein #include ".c" schlechter Stil
ist.
Mittlerweile ist mir im Hinblick auf CleanCode, Architektur-Design und
Testen nicht mehr so klar warum.
Vielmehr bin ich der Meinung, das man wie immer die Optionen abwägen
muss.
Gehen wir die einzelnen Themen mal durch:
Testen:
Hintergrund:
Beim Testen per Unit-Test muss dem Linker bekannt sein, welche
Funktionen (Symbole) es gibt, so dass diese durch "Fake/Mock"-Funktionen
für den Unit-Test
ersetzen werden können. Im Fall von "static"-Funktionen ist dies nicht
möglich, da diese Funktionen außerhalb der Compiliereinheit (die
c-Datei) nicht bekannt
sind. Meines Wissens gibt es hier nur zwei Möglichkeiten um solche
Funktionen zu testen:
Möglichkeit #1 - Anlegen eines Defines und eines entsprechenden
Compiler-Schalters (Beispiel: gcc -D__TEST__ ...)
1
#ifdef __TEST__
2
#define PRIVATE
3
#else
4
#define PRIVATE static
5
#endif
Möglichkeit #2 - Direktes inkludieren der C-Datei
1
#include"unit.c"
Was sind die Vorteile der #1 Version?
- Ich muss keine c-Dateien inkludieren.
Was sind die Nachteile der #1 Version?
- Ich muss das Schlüsselwort "static"-Situationsbedingt definieren. Ich
darf das Schlüsselwort bei Funktionen nicht direkt verwenden,
sondern muss stattdessen "PRIVATE" verwenden (bzw. was auch immer
stattdessen definiert wurde).
- Ich muss den zu testenden Code auf Tests vorbereiten. Streng genommen
ist das zu testende Programm ein anderes als das welches Produktiv
eingesetzt wird.
Was sind die Vorteile der #2 Version?
- Ich spare mir die Definition und den Compilerschalter.
- Ich muss den Code nicht verändern / auf das Testen vorbereiten
Was sind die Nachteile der #2 Version?
- Ich muss eine neue Konfiguration für das Compilieren zum Testen
erstellen (zum Beispiel in Eclipse CDT)
und die Datei "unit.c" explizit vom compilieren in dieser
Konfiguration ausschließen, da sonst doppelte Definitionen auftreten
Architektur-Design:
Hintergrund:
Die Komplexität von Software lässt sich leichter beherrschen, wenn das
Gesamtsystem in Teilsysteme - Module - gegliedert wird. Jedes Modul
besteht aus einer Schnittstelle
(header-Datei) welche vom Anwender des Moduls eingebunden wird, um die
Funktionalität des Moduls zu nutzen. Grundsätzlich sollte in dieser
"öffentlichen"
(=von außerhalb des Moduls genutzten) Schnittstelle nur Definitionen und
andere Header-Dateien bereitgestellt werden, welche unmittelbar zur
Nutzung der Schnittstelle
benötigt werden. Der Nutzer sollte nicht gezwungen werden Elemente zu
inkludieren, welche nicht zur Nutzung der Schnittstelle des Moduls
benötigt werden. Es sollten
weiterhin keine Definitionen auftauchen die nur innerhalb des Moduls
genutzt werden (Prinzip der Kapselung). Daraus folgt, dass falls ein
Modul
sehr komplex ist, dieses durchaus aus mehreren c-/h-Dateien bestehen
kann. Was dann zu dem Problem führt wie man Nutzer des Moduls davon
abhält nicht öffentliche
Header-Dateien einzubinden.
Möglichkeit #1 - Anlegen von private.h-Dateien um gemeinsame
Definitionen und Prototypen für Funktionen des Moduls bereitzustellen
Beispiel Unterverzeichnis Modul:
module
+ - modulePublicInterface.h
1
#include<stdint.h>
2
#define SUPERIMPORTANT_PUBLIC_DEFINE ...
3
extern...function...
+ - modulePublicImplementation.c
1
#include"modulePublicInterface.h"
2
#include"modulePrivateInterface.h"
+ - modulePrivateInterface.h
1
#define COMMON_TO_ALL_CODE_WITHIN_MODULE ...
2
extern...functionA(...)
+ - modulePrivateImplementation.c (<-Modul-interne Funktionen dürfen
nicht "static" sein! Da andere Compiliereinheit)
1
#include"modulePrivateInterface.h"
2
static...function...//(<- Nur in "modulePrivateImplementation.c" verwendbar)
3
...functionA(...)//(<- Prototyp in "modulePrivateInterface.h" definiert)
Möglichkeit #2 - Direktes inkludieren von privaten C-Dateien
Beispiel Unterverzeichnis Modul:
module
+ - modulePublicInterface.h
1
#include<stdint.h>
2
#define SUPERIMPORTANT_PUBLIC_DEFINE
3
extern...function...
+ - modulePublicImplementation.c
1
#include"modulePublicInterface.h"
2
#define COMMON_TO_ALL_CODE_WITHIN_MODULE ...
3
#include"modulePrivateImplementation.c"
+ - modulePrivateImplementation.c
1
#define ...
2
static...function...
Im ersten Fall gebietet es nur die Vernunft der Anwender die Datei
"modulePrivateInterace.h" nicht direkt zu verwenden. Im zweiten Fall
stellt sich diese Option dem Anwender erst nicht. Er hat nur die Datei
"modulePublicInterface.h" zur Verfügung.
Definiert man Prototypen für statische Funktionen müssen diese gepflegt
werden! Insbesondere die oft stiefmütterlich behandelte Dokumentation
muss immer nachgezogen werden. Daher wird im Allgemeinen angestrebt die
Dokumentation zentral an einer Stelle durchzuführen. Im Fall der
öffentlichen Header-Datei werden die meisten sicher die Dokumentation
der Funktionen dort vornehmen, zum Beispiel mit Doxygen. Dies kommt
sogleich dem Anwender der Schnittstelle zu gute.
Die interne Dokumentation der Funktionen für die nachfolgenden
Bearbeiter kann dann entweder bei der Deklaration der Protoypen der
statischen Funktionen oder bei den Definitionen der statischen
Funktionen selbst stehen. Spart man sich die Prototypen, stellt sich
auch diese Frage nicht.
Um die Übersicht zu wahren, welcher Teil des Codes "privat" und welcher
Teil "öffentlich" ist bietet es sich an die privaten Funktionen
statisch zu definieren und in eine eigene C-Datei auszulagern. Diese
kann dann direkt in der Hauptdatei des Moduls inkludiert wird (siehe
Möglichkeit #2)
Ich kann auch sagen, dass MISRA-C:2012 keine Regel hat die ein
inkludieren von C-Dateien untersagt. Ebenfalls ist mit keine
Fachliteratur bekannt die dies explizit mit Beispielen, warum man das
nicht tun sollte, belegt.
Aus meiner Sicht ist es ganz klar eine Sache der Erfahrung und des
Abwägens der Vor- und Nachteile. Es kann wie die Verwendung des
goto-Statements sinnvoll sein oder furchtbar missbraucht werden. Das
Einbinden von C-Dateien ist zumindest "ungewohnt".