Hallo Leute!
Ich meine die Programmiersprache C gut zu beherschen, doch jetzt gibt
mit das AVRStudio (GCC) einen Fehler aus, bei dem ich seit Tagen am
verzweifeln bin. Folgendes Problem:
Ich habe zwei Header Dateien:
AnalogInputDriver.h
EEPROM_API.h
In der AnalogInputDriver.h steht folgendes:
1
#ifndef ANALOG_INPUT_DRIVER_H
2
#define ANALOG_INPUT_DRIVER_H
3
4
5
#include"Main.h"
6
7
/*** ENUM ALL ANALOGSTICK AXIS ***/
8
typedefenumeAnalogInputs
9
{
10
L3_HORIZONTAL,// Stick -100 to +100
11
L3_VERTICAL,// Stick -100 to +100
12
R3_HORIZONTAL,// Stick -100 to +100
13
R3_VERTICAL,// Stick -100 to +100
14
L2,// Switch 0 to +100
15
R2// Switch 0 to +100
16
}eAnalogInput;
17
18
/*** ENUM TYPES OF ANALOG RESULT ***/
19
typedefenumeAnalogReturnTypes
20
{
21
CALIBRATED,
22
NOT_CALIBRATED
23
}eAnalogReturnType;
24
25
/*** ENUM INVERTED STATES ***/
26
typedefenumeInvertStates
27
{
28
INVERTED,
29
NOT_INVERTED
30
}eInvertState;
31
32
/*** STRUCTS ***/
33
typedefstructsAnalogStickCalibValueSets
34
{
35
signedshortusMax;
36
signedshortusNeutralMax;
37
signedshortusNeutralMin;
38
signedshortusMin;
39
eInvertStateInvertState;
40
}sAnalogStickCalibValueSet;
41
42
typedefstructsAnalogSwitchCalibValueSets
43
{
44
signedshortusMax;
45
signedshortusMin;
46
}sAnalogSwitchCalibValueSet;
47
48
49
/*** GET AN ANALOG RESULT AS CALIBRATED VALUE OR AS NOT CALIBRATED VALUE ***/
Dieser Teil Funktioniert auc wunderbar mit der zugehörigen
AnalogInputDriver.c zusammen. So, nun möchte ich die Typen der
Strukturen in EEPROM_API.h verwenden wie folgt:
Jedoch bekomme ich folgende Fehlermeldungen:
../EEPROM_API.h:9: error: expected ')' before 'analogInput'
../EEPROM_API.h:10: error: expected ')' before 'analogInput'
../EEPROM_API.h:12: error: expected ')' before 'analogInput'
../EEPROM_API.h:13: error: expected ')' before 'analogInput'
Weiß jemand warum dieser Fehler kommt? Eigentlich müsste das gehen, die
Datentypen sind durch #include "AnalogInputDriver.h" bekannt und er gibt
ja auch nicht aus, das er die Typen nicht kennt. Liegt das am AVRStudio?
Danke für jede Hilfe.
Gruß, Markus
Deine EEPROM_API.h kompiliert mit obigen Codeteilen durch ohne sich zu
beschweren. Somit solltest Du vielleicht noch die Main.h nachliefern
(für die habe ich eine leere Datei beim Test verwendet). In den
gezeigten Teilen ist kein Fehler (bis auf das fehlende #endif am Ende).
Nachtrag:
Markus schrieb:> und er gibt> ja auch nicht aus, das er die Typen nicht kennt.
Oh doch. Er gibt aus, dass er eAnalogInput nicht kennt. Sonst würde er
dort keine schließende Klammer erwarten. Er interpretiert eAnalogInput
als untypisierten Parameternamen (was unsauber, aber nicht unzulässig
wäre).
Also, ich hab jetzt das #include "Main.h" aus der Datei
AnalogInputDriver.h rausgenommen und in die AnalogInputDriver.c
reingemacht, jetzt gehts, allerdinsg würde mich interessieren warum es
vorher nicht ging. Hier nochmal meine Main.h:
1
#ifndef MAIN_H
2
#define MAIN_H
3
4
/*********************/
5
/*** INCLUDE FILES ***/
6
/*********************/
7
#include<string.h>
8
9
#include"avr_compiler.h"
10
#include"port_driver.h" // PORT driver
11
#include"clksys_driver.h" // ClockSystem driver
12
#include"TC_driver.h" // Timer driver
13
#include"adc_driver.h"
14
#include"usart_driver.h"
15
16
#include"SoundDriver.h"
17
#include"XBeeRingBufferSender.h"
18
#include"DisplayDriver.h"
19
#include"DisplayRingBufferSender.h"
20
#include"LED_Driver.h"
21
#include"AnalogInputDriver.h"
22
#include"DigitalInputDriver.h"
23
#include"EEPROM_API.h"
24
25
26
27
/***************************/
28
/*** FUNCTION PROTOTYPES ***/
29
/***************************/
30
voidInit_ClockSystem(void);// Adjust the CPU clock
31
voidInit_PORT_Pins(void);// IO Port init
32
voidInit_Timer(void);// Timer init
33
voidInit_Interrupts(void);// Interrupt init
34
voidInit_ADC(void);// ADC init
35
voidInit_UART(void);
36
37
voidInit_SoundDriver(void);
38
39
40
41
42
#endif // MAIN_H
PS: Was meinst Du mit "(bis auf das fehlende #endif am Ende)", es sind
doch alle #endif´s da.
Danke schonmal für Deine Mühe.
Gruß, Markus
Jetzt kann natürlich alles mögliche in den anderen Includes stecken.
Vielleicht ist es einfacher, das ganze durch den Präprozessor zu jagen
und dann anzuschauen, was der Compiler zu sehen bekommt. Da sieht man
dann, ob ein #define irgendwo was kaputt gemacht hat.
Einfach den cpp auf die Quelldatei aufrufen, mit all den -I und -D
Optionen, die auch dem gcc übergeben werden.
Ja, ich bin jetzt auch überzeugt, dass irgendwoe was mit nem #definde
sein muss, da werd ich mich mal bei Gelegenheit dran setzen. Ich danke
Euch erstmal für Eure Hilfe.
Gruß, Markus
Scheinbar beherscht du die Sprache C doch nicht so gut! Denn sonst würde
dir auffallen das du ein Problem bei den Includes hast.
Ich versuch das einmal zu erklären:
Wir arbeiten dein Programm von oben nach unten ab, angefangen bei der
AnalogInputDriver.h.
Hiermit sorgst du dafür, das die Datei nur einmal durchkompiliert wird:
#ifndef ANALOG_INPUT_DRIVER_H
#define ANALOG_INPUT_DRIVER_H
Als nächstes wird die main.h includiert, wir springen also in die
main.h.
Dort werden wieder verschiedene Header includiert und an der 15. Stelle
die EEPROM_API.h. Wir springen in die EEPROM_API.h.
Dort versuchst du die AnalogInputDriver.h zu includieren, dies geht aber
nicht weil du schon "ANALOG_INPUT_DRIVER_H" definiert hast. Der
Compieler verlässt die AnalogInputDriver.h und arbeitet die EEPROM_API.h
weiter ab.
Jetzt versuchst du deine typedefs zu verwenden, die aber noch nicht
kompiliert wurden. Weil du über die Zeile "#include "Main.h" deine
Header-Datei verlassen hast.
Du hast 2 Möglichkeiten das Problem zu umgehen:
1. die Main.h nicht includieren.
2. aus der Main.h die Zeile "#include "EEPROM_API.h" löschen.
Ralf schrieb:> Du hast 2 Möglichkeiten das Problem zu umgehen:> 1. die Main.h nicht includieren.> 2. aus der Main.h die Zeile "#include "EEPROM_API.h" löschen.
@TO
Diese Rundumschlag-'1 Header includiert alles andere' sind meistens
sowieso keine gute Idee. Jetzt hast du am eigenen Leib erfahren warum.
So wird es nämlich immer schwieriger zu überblicken wer wen in welcher
Reihenfolge includiert.
Markus schrieb:> PS: Was meinst Du mit "(bis auf das fehlende #endif am Ende)", es sind> doch alle #endif´s da.
Stimmt. Beim Copy&Paste zum Testkompilieren hatte ich eine Zeile zu
wenig selektiert und habe diesen Mangel dann Dir zugerechnet.
Fälschlicherweise.
@Ralf: Du hast natürlich vollkommen recht, woran der Fehler lag ist mir
jetzt auch klar geworden. Aber wie das eben so ist, gerade wenn man eine
Sprache lange nicht implementiert hat, sieht man manchmal den Wald vor
lauter Bäumen nicht, und das passiert Dir sicher auch ab und zu, daher
sollte man gerade mit provokanten Aussagen aufpassen ;-).
@All: Ich danke Euch allen für die schnelle und kopentente Hilfe. Ich
räum jetzt mal meine includes auf.
Grüße,
Markus
Karl heinz Buchegger schrieb:> Diese Rundumschlag-'1 Header includiert alles andere' sind meistens> sowieso keine gute Idee. Jetzt hast du am eigenen Leib erfahren warum.> So wird es nämlich immer schwieriger zu überblicken wer wen in welcher> Reihenfolge includiert.
Hallo Karl Heinz,
hast du einen Tipp: Wie organisierst du header files ?
Viele Grüße
Klaus
Naja, im Prinzip ist es ganz einfach: Jede Datei includiert nur genau
das, was die unbedingt braucht, um kompilierbar zu sein.
Außerdem ist es sinnvoll, dass die .c Dateien ihre zugehörige .h Datei
auch einbinden.
Das heißt:
-deine main.h fliegt komplett raus (es sei denn da sind irgendwelche
defines drin, die global gebraucht werden. Dann nenn ich die Datei aber
meisten config.h oder ähnliches).
- AnalogInputDriver.h includiert gar nichts.
- EEPROM_API.h includiert AnalogInputDriver.h
Ich will mir jetzt KHBs Identität anmaßen, aber ich mache es in
aller Regel so:
Alles, was thematisch eng zusammengehört und in sich abgeschlossen
ist (je nach Fall) kommt in ein Paar von .c und .h.
Es gibt also eine 1:1-Entsprechung von Definitionen (Funktionen,
notfalls globale Variablen) in einer .c und den restlichen
Dingen in der .h (Funktionsdeklarationen, Typdefinitionen,
Konstanten, Makros).
Welchen Umfang jetzt in einem .c/.h-Paar steckt, ist natürlich
Ermessenssache bzw. Erfahrung. Da kann man wenig vorschreiben.
Interessant ist immer, wie universell+wiederverwendbar ein
solches Paar ist.
Die .c includet immer die eigene .h.
In der .h werden weiterhin alle anderen .h includet, die
darin nötig sind (zugrundeliegende Typdefinitionen besonders).
So braucht man in den .c in der Regel nur wenig includen, weil
die weiter unten benutzten Spielerein schon von den .h abgehakt
werden.
Zudem hat natürlich jede .h das Spiel mit
#ifndef...#define...#endif.
Eine globale .h für alles (wie eine main.h) braucht man
eigentlich selten, meistens hat man dann etwas falsch gemacht.
Mit diesen Regeln sollte man in den allermeisten Fällen gut
zurecht kommen.
Von Fall zu Fall kann man natürlich auch davon abweichen, aber
nur wenn man einen guten Grund hat.
Bei Bedarf kann man mehrere kompilierte .c zu einer Lib
zusammenfassen, dann macht es oft auch Sinn, die
zugehörigen Header in einer neuen .h mit #include
zusammenzufassen.
Ich gebe Klaus vollkommen recht.
Allerdings habe ich mir angewöhnt, in den h-Files grundsätzlich keine
anderen Files zu includen. Das überlasse ich komplett den c-files.
StinkyWinky schrieb:> Ich gebe Klaus vollkommen recht.>> Allerdings habe ich mir angewöhnt, in den h-Files grundsätzlich keine> anderen Files zu includen. Das überlasse ich komplett den c-files.
Das solltest du umgewöhnen.
Wenn in einem h-File ein anderer Datentyp gebraucht wird (zb um einen
Funktionsprototypen zu schreiben), und eine Forward-Deklaration nicht
reicht, dann soll das Header-File den zusätzlichen Header ebenfalls
inkludieren.
Bürde niemals demjenigen, der ein bestimmtes Header File includieren
möchte die Arbeit auf, sich daran zu erinnern, dass er dazu vorher ein
anderes Header File inkludieren muss. Und bei einer Forward Deklaration
ist man dann auch so nett und schreibt einen Kommentar in den Header, wo
der eigentliche Datentyp zu finden ist. Das Ziel ist es, dass man nur
ein Header File für eine Funktionalität inkludieren muss und dann hat
man automatisch alles inkludiert, was für diese Funktionalität gebraucht
wird.
Wenn du dafür sorgst, dass alle Header in sich vollständig sind, aber
auch nicht einfach alles andere inkludieren was sie gar nicht brauchen,
dann erweist sich das in der Praxis als der gangbarste Weg, mit den
wenigsten Schwierigkeiten.
Bei meinen Projekten habe ich es immer so gemacht, dass es eine einzige
Header file gibt, die alles inkludiert. Alle Module (Header und Source
Files) binden dann nur noch diesen Header ein.
Hat Vor- und Nachteile.
Vorteile:
- Man sieht sofort in welcher Reihenfolge includes vorgenommen werden
und wie es der Compiler zu sehen bekommt
- Wird ein Modul umbenannt muss man den Namen nur in dieser einen Header
File ändern und sonst nirgends, selbst wenn das Modul von 100 anderen
Modulen genutzt wird (insbesondere: denkt man an Versionierung würden
sich dadurch 100 Files ändern, so nur eine).
Nachteile:
- Compilieren kann etwas länger dauern
- Die Information welche includes überhaupt benötigt werden (eigentlich
eine Eigenschaft des jeweiligen Moduls) ist nicht mehr direkt erkennbar
klaus schrieb:> - Wird ein Modul umbenannt muss man den Namen nur in dieser einen Header> File ändern und sonst nirgends, selbst wenn das Modul von 100 anderen> Modulen genutzt wird (insbesondere: denkt man an Versionierung würden> sich dadurch 100 Files ändern, so nur eine).
Gerade dann, wenn du 100 andere Module hast, ist das was du machst genau
das was du nicht tun solltest.
Denn damit hebelst du den make-Mechanismus komplett aus, der nur die
Dinge nachcompiliert, die sich tatsächlich geändert haben. Ein
Header-File ändern und schon muss alles nachcompiliert werden.
Was bei 3 oder 4 Modulen noch eine verzeichliche Nachlässigkeit ist,
wird immer mehr zum Boomerang, je größer die Projekte werden. Bei 3 oder
4 Modulen gibt es aber ohnehin noch keine Reihenfolge-Schwierigkeiten.