Forum: Compiler & IDEs Struc as header einbinden - Compiler dreht durch


von Chris (Gast)


Lesenswert?

Hallo Forum,

wer kann mit bezüglich Programmierung in C und insbesondere Arduino auf 
die Sprünge helfen?

Ich habe folgendes Problem, bei dem ich ein bestehendes ESP8266 Projekt 
auf meine Bedürfnisse abändern muss. In dem Projekt, welches in seinem 
Ausgangszustand erfolgreich kompiliert, gibt es eine Datei Namens 
store.h
1
#ifndef store_h
2
#define store_h
3
4
// Change this if the settings structure changes
5
#define CONFIG_VERSION "b5g"
6
7
// Dont change this
8
#define CONFIG_START 0
9
10
enum fx_mode {
11
  FX_MODE_PIXEL_MAP = 0,
12
  FX_MODE_12 = 1
13
};
14
15
enum p_type {
16
  TYPE_DMX_OUT = 0,
17
  TYPE_RDM_OUT = 1,
18
  TYPE_DMX_IN = 2,
19
  TYPE_WS2812 = 3,
20
  TYPE_SK6812 = 4
21
};
22
23
enum p_protocol {
24
  PROT_ARTNET = 0,
25
  PROT_ARTNET_SACN = 1
26
};
27
28
enum p_merge {
29
  MERGE_LTP = 0,
30
  MERGE_HTP = 1
31
};
32
33
struct StoreStruct {
34
  // StoreStruct version
35
  char version[4];
36
37
  // Device settings:
38
  IPAddress ip, subnet, gateway, broadcast, hotspotIp, hotspotSubnet, hotspotBroadcast, dmxInBroadcast;
39
  bool dhcpEnable, standAloneEnable;
40
  char nodeName[18], longName[64], wifiSSID[40], wifiPass[40], hotspotSSID[20], hotspotPass[20];
41
  uint16_t hotspotDelay;
42
  uint8_t portAmode, portBmode, portAprot, portBprot, portAmerge, portBmerge;
43
  uint8_t portAnet, portAsub, portAuni[4], portBnet, portBsub, portBuni[4], portAsACNuni[4], portBsACNuni[4];
44
  uint16_t portAnumPix, portBnumPix, portApixConfig, portBpixConfig;
45
  bool doFirmwareUpdate;
46
  uint8_t portApixMode, portBpixMode;
47
  uint16_t portApixFXstart, portBpixFXstart;
48
  uint8_t resetCounter, wdtCounter;
49
  
50
} deviceSettings = {
51
  CONFIG_VERSION,
52
  
53
  // The default values
54
  IPAddress(2,0,0,1), IPAddress(255,0,0,0), IPAddress(2,0,0,1), IPAddress(2,255,255,255), IPAddress(2,0,0,1), IPAddress(255,0,0,0), IPAddress(2,255,255,255), IPAddress(2,255,255,255),
55
  true, false,
56
  "espArtNetNode", "espArtNetNode by Matthew Tong", "", "", "espArtNetNode", "byMtongnz2017",
57
  15,
58
  TYPE_DMX_OUT, TYPE_DMX_OUT, PROT_ARTNET, PROT_ARTNET, MERGE_HTP, MERGE_HTP,
59
  0, 0, {0, 1, 2, 3}, 0, 0, {4, 5, 6, 7}, {1, 2, 3, 4}, {5, 6, 7, 8},
60
  680, 680, 0, 0,
61
  false,
62
  FX_MODE_PIXEL_MAP, FX_MODE_PIXEL_MAP,
63
  1, 1,
64
  0, 0
65
};
66
67
68
void eepromSave() {
69
  for (uint16_t t = 0; t < sizeof(deviceSettings); t++)
70
    EEPROM.write(CONFIG_START + t, *((char*)&deviceSettings + t));
71
  
72
  EEPROM.commit();
73
}
74
75
void eepromLoad() {
76
  // To make sure there are settings, and they are YOURS!
77
  // If nothing is found it will use the default settings.
78
79
  if (EEPROM.read(CONFIG_START + 0) == CONFIG_VERSION[0] &&
80
      EEPROM.read(CONFIG_START + 1) == CONFIG_VERSION[1] &&
81
      EEPROM.read(CONFIG_START + 2) == CONFIG_VERSION[2]) {
82
83
    // Store defaults for if we need them
84
    StoreStruct tmpStore;
85
    tmpStore = deviceSettings;
86
    
87
    // Copy data to deviceSettings structure
88
    for (uint16_t t = 0; t < sizeof(deviceSettings); t++)
89
      *((char*)&deviceSettings + t) = EEPROM.read(CONFIG_START + t);
90
    
91
    // If we want to restore all our settings
92
    if (deviceSettings.resetCounter >= 5 || deviceSettings.wdtCounter >= 10) {
93
      deviceSettings.wdtCounter = 0;
94
      deviceSettings.resetCounter = 0;
95
96
      // Store defaults back into main settings
97
      deviceSettings = tmpStore;
98
    }
99
100
101
  // If config files dont match, save defaults then erase the ESP config to clear away any residue
102
  } else {
103
    eepromSave();
104
    delay(500);
105
    
106
    ESP.eraseConfig();
107
    while(1);
108
  }
109
}
110
111
#endif

Wie ihr sehen könnt, sorgt diese store.h Datei im wesentlichen für zwei 
Dinge. Zum einen gibt es ein paar Funktionen, die EEPROM Zugriffe 
umsetzten. Dann gibt es noch eine Struktur, die Programmparameter 
speichert und welche auch gleich in der store.h initialisiert und mit 
default Werten geladen wird. Genau diese Programmparameter aus der 
Struktur benötige ich nun an anderer Stelle im Quelltext als der Main 
Datei. Und das will nicht klappen.

1. Problem:
Ich inkludiere die Store.h in einer weiteren Quelltext Datei. Nun 
meckert Arduino an, dass die Funktion eeprom.read nicht bekannt sei. Nun 
diese entstammt der Arduino eigenen EEPROM.h und wird in der Store.h 
auch wirklich nicht inkludiert. Fügt man
1
#include <EEPROM.h>

zur store.h hinzu erhält man Problem 2:
1
sketch\ws2812Driver.cpp.o:(.data.deviceSettings+0x0): multiple definition of `deviceSettings'
2
3
sketch\source.ino.cpp.o:(.data.deviceSettings+0x0): first defined here
4
5
sketch\ws2812Driver.cpp.o: In function `ws2812Driver::ws2812Driver()':
6
7
sketch/store.h:50: multiple definition of `eepromSave()'
8
9
sketch\source.ino.cpp.o:sketch/store.h:85: first defined here
10
11
sketch\ws2812Driver.cpp.o: In function `ws2812Driver::doPixelDouble(unsigned char*, unsigned char, unsigned char*, unsigned char, unsigned short)':
12
13
sketch/store.h:92: multiple definition of `eepromLoad()'
14
15
sketch\source.ino.cpp.o:sketch/store.h:92: first defined here
16
17
collect2.exe: error: ld returned 1 exit status

Die source.ino ist die Hautdatei des ganzen Programmes und dort werden 
sowohl die EEPROM.h als Header als auch die Store.h bislang eingebunden.

Ich verstehe nur nicht, warum ich die store.h nicht auch noch woanders 
einbinden kann. Das ist doch der Sinn von Headern und eigentlich sollten 
so doch auch Klassen und Strukturen im Programm überall bekannt gemacht 
werden können.

Jemand eine Idee, wie man diese bestimmt sehr triviale Problematik 
richtig auflösen kann?

von (prx) A. K. (prx)


Lesenswert?

Deklaration von Typen, Variablen und Funktionen sind in .h richtig. Die 
Definition von Variablen (also mit Inhalt, implizit oder explizit) 
hingegen nicht, ebensowenig Funktionsdefinitionen, weil:
  multiple definition of `deviceSettings'
  multiple definition of `eepromLoad()'

: Bearbeitet durch User
von Der Andere (Gast)


Lesenswert?

Chris K. schrieb:
> Wie ihr sehen könnt, sorgt diese store.h Datei im wesentlichen für zwei
> Dinge. Zum einen gibt es ein paar Funktionen, die EEPROM Zugriffe
> umsetzten. Dann gibt es noch eine Struktur, die Programmparameter
> speichert und welche auch gleich in der store.h initialisiert und mit
> default Werten geladen wird.

Und genau das ist Scheiße und gegen alle Regeln der C Programmierung.
Man trennt Deklarationen von Strukturen, makros und Typedefs von der 
Implementation.
Die Deklaration in ein .h file, die Implementation in ein .c File, das 
dann einmal compiliert und zu dem Projekt dazugelinkt wird.

Besorge die ein C Buch und arbeite es von vorne durch. Alles andere wird 
nix.

Beitrag #5436623 wurde vom Autor gelöscht.
von Richtig Steller (Gast)


Lesenswert?

Chris K. schrieb:
> Struc as header einbinden - Compiler dreht durch

Nein, er dreht nicht durch sondern er meldet dir einfach
dass du dich nicht an die Regeln hältst.

Und das ist gut so. Es hilft dir dein Unwissen und Chaos
zu bewältigen.

von Chris (Gast)


Lesenswert?

Gut,

ich sehe ein, dass der Compiler es nicht mag, wenn die gesamte Funktion 
im Header definiert wird und dann der Header an mehreren Stellen 
eingebunden wird. Hilft mir jetzt aber leider auch nicht dabei weiter 
das Problem zu lösen, dass der ursprüngliche Verfasser des Quelltextes 
eingebaut hat.

Die erste Baustelle ist also die store.h aufzuräumen, damit die 
Definition aus der Header Datei verschwindet.
Naiver Ansatz also:
1
#ifndef store_h
2
#define store_h
3
4
// Change this if the settings structure changes
5
#define CONFIG_VERSION "b5g"
6
7
// Dont change this
8
#define CONFIG_START 0
9
10
enum fx_mode {
11
  FX_MODE_PIXEL_MAP = 0,
12
  FX_MODE_12 = 1
13
};
14
15
enum p_type {
16
  TYPE_DMX_OUT = 0,
17
  TYPE_RDM_OUT = 1,
18
  TYPE_DMX_IN = 2,
19
  TYPE_WS2812 = 3,
20
  TYPE_SK6812 = 4
21
};
22
23
enum p_protocol {
24
  PROT_ARTNET = 0,
25
  PROT_ARTNET_SACN = 1
26
};
27
28
enum p_merge {
29
  MERGE_LTP = 0,
30
  MERGE_HTP = 1
31
};
32
33
34
struct StoreStruct {
35
  // StoreStruct version
36
  char version[4];
37
38
  // Device settings:
39
  IPAddress ip, subnet, gateway, broadcast, hotspotIp, hotspotSubnet, hotspotBroadcast, dmxInBroadcast;
40
  bool dhcpEnable, standAloneEnable;
41
  char nodeName[18], longName[64], wifiSSID[40], wifiPass[40], hotspotSSID[20], hotspotPass[20];
42
  uint16_t hotspotDelay;
43
  uint8_t portAmode, portBmode, portAprot, portBprot, portAmerge, portBmerge;
44
  uint8_t portAnet, portAsub, portAuni[4], portBnet, portBsub, portBuni[4], portAsACNuni[4], portBsACNuni[4];
45
  uint16_t portAnumPix, portBnumPix, portApixConfig, portBpixConfig;
46
  bool doFirmwareUpdate;
47
  uint8_t portApixMode, portBpixMode;
48
  uint16_t portApixFXstart, portBpixFXstart;
49
  uint8_t resetCounter, wdtCounter;
50
  
51
}; 
52
53
extern StoreStruct deviceSettings; 
54
55
extern void eepromSave(void);
56
extern void eepromLoad(void);
57
58
#endif

und dann eine store.c Datei erzeugen, in der die Funktionen 
ausgearbeitet werden:
1
#include "store.h"
2
#include <EEPROM.h>
3
4
struct StoreStruct {
5
  // StoreStruct version
6
  char version[4];
7
8
  // Device settings:
9
  IPAddress ip, subnet, gateway, broadcast, hotspotIp, hotspotSubnet, hotspotBroadcast, dmxInBroadcast;
10
  bool dhcpEnable, standAloneEnable;
11
  char nodeName[18], longName[64], wifiSSID[40], wifiPass[40], hotspotSSID[20], hotspotPass[20];
12
  uint16_t hotspotDelay;
13
  uint8_t portAmode, portBmode, portAprot, portBprot, portAmerge, portBmerge;
14
  uint8_t portAnet, portAsub, portAuni[4], portBnet, portBsub, portBuni[4], portAsACNuni[4], portBsACNuni[4];
15
  uint16_t portAnumPix, portBnumPix, portApixConfig, portBpixConfig;
16
  bool doFirmwareUpdate;
17
  uint8_t portApixMode, portBpixMode;
18
  uint16_t portApixFXstart, portBpixFXstart;
19
  uint8_t resetCounter, wdtCounter;
20
  
21
} deviceSettings = {
22
  CONFIG_VERSION,
23
  
24
  // The default values
25
  IPAddress(2,0,0,1), IPAddress(255,0,0,0), IPAddress(2,0,0,1), IPAddress(2,255,255,255), IPAddress(2,0,0,1), IPAddress(255,0,0,0), IPAddress(2,255,255,255), IPAddress(2,255,255,255),
26
  true, false,
27
  "espArtNetNode", "espArtNetNode by Matthew Tong", "", "", "espArtNetNode", "byMtongnz2017",
28
  15,
29
  TYPE_DMX_OUT, TYPE_DMX_OUT, PROT_ARTNET, PROT_ARTNET, MERGE_HTP, MERGE_HTP,
30
  0, 0, {0, 1, 2, 3}, 0, 0, {4, 5, 6, 7}, {1, 2, 3, 4}, {5, 6, 7, 8},
31
  680, 680, 0, 0,
32
  false,
33
  FX_MODE_PIXEL_MAP, FX_MODE_PIXEL_MAP,
34
  1, 1,
35
  0, 0
36
};
37
38
39
void eepromSave() {
40
  for (uint16_t t = 0; t < sizeof(deviceSettings); t++)
41
    EEPROM.write(CONFIG_START + t, *((char*)&deviceSettings + t));
42
  
43
  EEPROM.commit();
44
}
45
46
void eepromLoad() {
47
  // To make sure there are settings, and they are YOURS!
48
  // If nothing is found it will use the default settings.
49
50
  if (EEPROM.read(CONFIG_START + 0) == CONFIG_VERSION[0] &&
51
      EEPROM.read(CONFIG_START + 1) == CONFIG_VERSION[1] &&
52
      EEPROM.read(CONFIG_START + 2) == CONFIG_VERSION[2]) {
53
54
    // Store defaults for if we need them
55
    StoreStruct tmpStore;
56
    tmpStore = deviceSettings;
57
    
58
    // Copy data to deviceSettings structure
59
    for (uint16_t t = 0; t < sizeof(deviceSettings); t++)
60
      *((char*)&deviceSettings + t) = EEPROM.read(CONFIG_START + t);
61
    
62
    // If we want to restore all our settings
63
    if (deviceSettings.resetCounter >= 5 || deviceSettings.wdtCounter >= 10) {
64
      deviceSettings.wdtCounter = 0;
65
      deviceSettings.resetCounter = 0;
66
67
      // Store defaults back into main settings
68
      deviceSettings = tmpStore;
69
    }
70
71
72
  // If config files dont match, save defaults then erase the ESP config to clear away any residue
73
  } else {
74
    eepromSave();
75
    delay(500);
76
    
77
    ESP.eraseConfig();
78
    while(1);
79
  }
80
}

Ist aber auch blöd, weil es so die Deklaration der Struktur 2 mal gibt 
und das macht ja eigentlich auch keinen Sinn. Aber wie muss die store.c 
aussehen, wenn dort der Konstruktor für die Struktur sein soll?

von foobar (Gast)


Lesenswert?

> Ist aber auch blöd, weil es so die Deklaration der Struktur 2 mal gibt

Dann lass es halt sein! Im .c-File ist sie unnötig, gar falsch ;-)

Ins .c-File einfach nur nen
1
struct StoreStruct deviceSettings = { CONFIG_VERSION, ... };

von foobar (Gast)


Lesenswert?

Btw:

[c]
-extern StoreStruct deviceSettings;
+extern struct StoreStruct deviceSettings;
[/c[

von Chris (Gast)


Lesenswert?

Danke foobar,

habe die Dateien umgebaut und kotze schon wieder im Strahl:

store.h
1
ifndef store_h
2
#define store_h
3
4
#include <ESP8266WiFi.h>
5
6
// Change this if the settings structure changes
7
#define CONFIG_VERSION "b5g"
8
9
// Dont change this
10
#define CONFIG_START 0
11
12
enum fx_mode {
13
  FX_MODE_PIXEL_MAP = 0,
14
  FX_MODE_12 = 1
15
};
16
17
enum p_type {
18
  TYPE_DMX_OUT = 0,
19
  TYPE_RDM_OUT = 1,
20
  TYPE_DMX_IN = 2,
21
  TYPE_WS2812 = 3,
22
  TYPE_SK6812 = 4
23
};
24
25
enum p_protocol {
26
  PROT_ARTNET = 0,
27
  PROT_ARTNET_SACN = 1
28
};
29
30
enum p_merge {
31
  MERGE_LTP = 0,
32
  MERGE_HTP = 1
33
};
34
35
36
struct StoreStruct {
37
  // StoreStruct version
38
  char version[4];
39
40
  // Device settings:
41
  IPAddress ip, subnet, gateway, broadcast, hotspotIp, hotspotSubnet, hotspotBroadcast, dmxInBroadcast;
42
  bool dhcpEnable, standAloneEnable;
43
  char nodeName[18], longName[64], wifiSSID[40], wifiPass[40], hotspotSSID[20], hotspotPass[20];
44
  uint16_t hotspotDelay;
45
  uint8_t portAmode, portBmode, portAprot, portBprot, portAmerge, portBmerge;
46
  uint8_t portAnet, portAsub, portAuni[4], portBnet, portBsub, portBuni[4], portAsACNuni[4], portBsACNuni[4];
47
  uint16_t portAnumPix, portBnumPix, portApixConfig, portBpixConfig;
48
  bool doFirmwareUpdate;
49
  uint8_t portApixMode, portBpixMode;
50
  uint16_t portApixFXstart, portBpixFXstart;
51
  uint8_t resetCounter, wdtCounter;
52
  
53
}; 
54
55
extern struct StoreStruct deviceSettings;
56
57
extern void eepromSave(void);
58
extern void eepromLoad(void);
59
60
#endif

und die store.cpp
1
#include <EEPROM.h>
2
#include <ESP8266WiFi.h>
3
#include "store.h"
4
5
6
7
struct StoreStruct deviceSettings = {
8
  CONFIG_VERSION,
9
  
10
  // The default values
11
  IPAddress(2,0,0,1), IPAddress(255,0,0,0), IPAddress(2,0,0,1), IPAddress(2,255,255,255), IPAddress(2,0,0,1), IPAddress(255,0,0,0), IPAddress(2,255,255,255), IPAddress(2,255,255,255),
12
  true, false,
13
  "espArtNetNode", "espArtNetNode by Matthew Tong", "", "", "espArtNetNode", "byMtongnz2017",
14
  15,
15
  TYPE_DMX_OUT, TYPE_DMX_OUT, PROT_ARTNET, PROT_ARTNET, MERGE_HTP, MERGE_HTP,
16
  0, 0, {0, 1, 2, 3}, 0, 0, {4, 5, 6, 7}, {1, 2, 3, 4}, {5, 6, 7, 8},
17
  680, 680, 0, 0,
18
  false,
19
  FX_MODE_PIXEL_MAP, FX_MODE_PIXEL_MAP,
20
  1, 1,
21
  0, 0
22
};
23
24
25
void eepromSave() {
26
  for (uint16_t t = 0; t < sizeof(deviceSettings); t++)
27
    EEPROM.write(CONFIG_START + t, *((char*)&deviceSettings + t));
28
  
29
  EEPROM.commit();
30
}
31
32
void eepromLoad() {
33
  // To make sure there are settings, and they are YOURS!
34
  // If nothing is found it will use the default settings.
35
36
  if (EEPROM.read(CONFIG_START + 0) == CONFIG_VERSION[0] &&
37
      EEPROM.read(CONFIG_START + 1) == CONFIG_VERSION[1] &&
38
      EEPROM.read(CONFIG_START + 2) == CONFIG_VERSION[2]) {
39
40
    // Store defaults for if we need them
41
    StoreStruct tmpStore;
42
    tmpStore = deviceSettings;
43
    
44
    // Copy data to deviceSettings structure
45
    for (uint16_t t = 0; t < sizeof(deviceSettings); t++)
46
      *((char*)&deviceSettings + t) = EEPROM.read(CONFIG_START + t);
47
    
48
    // If we want to restore all our settings
49
    if (deviceSettings.resetCounter >= 5 || deviceSettings.wdtCounter >= 10) {
50
      deviceSettings.wdtCounter = 0;
51
      deviceSettings.resetCounter = 0;
52
53
      // Store defaults back into main settings
54
      deviceSettings = tmpStore;
55
    }
56
57
58
  // If config files dont match, save defaults then erase the ESP config to clear away any residue
59
  } else {
60
    eepromSave();
61
    delay(500);
62
    
63
    ESP.eraseConfig();
64
    while(1);
65
  }
66
}

nun meckert der Compiler zurecht, dass er die "variable" IPAdresse in 
der Struktur nicht auflösen kann. Muss auch wieder sowas Adurino 
spezifisches sein. Scheinbar wird die in der Header Datei ESP8266WiFi.h 
beschrieben. Diese ist in der Main auch inkludiert. Will ich die 
zusätzlich in der Store.h mit einbinden erhält man folgenden Fehler

...AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\2.3.0\libra 
ries\ESP8266WiFi\src/ESP8266WiFiGeneric.h:27:22:  fatal error: 
functional: No such file or directory

 #include <functional>

Argh... als Programmieranfänger kann einen so etwas wirklich dazu 
bringen in die Tischkante zu beißen. Warum hat er denn keine Probleme 
mit dem Inkludieren in der Main Datei, aber in einer anderen Header 
Datei?

von Joachim B. (jar)


Lesenswert?

Chris K. schrieb:
> Danke foobar,
> habe die Dateien umgebaut und kotze schon wieder im Strahl:
>
> store.h
>
> ifndef store_h
> #define store_h

wie wäre es mit # vor ifndef?

von Richtig Steller (Gast)


Lesenswert?

Chris K. schrieb:
> Argh... als Programmieranfänger kann einen so etwas wirklich dazu
> bringen in die Tischkante zu beißen.

Nein, du hast soeben eine Situation erlebt die dir die
Macher des Arduino-Environments eingebrockt haben.

von Der Andere (Gast)


Lesenswert?

Chris K. schrieb:
> und kotze schon wieder im Strahl

Dagegen hilft Vomex oder ein gutes C-Buch.
Allerdings letzteres nicht einnehmen sondern geistig inhalieren.

Du musst die Konzepte verstehen, sonst wird das nix, sorry aber 
programmieren in C ist nix für "maker".

von Chris (Gast)


Lesenswert?

@Joachim,

ist drin. Fehlte beim copy und paste des Quelltextes.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Du kannst das Problem "lösen", indem du das chaotische Headerfile nur 
genau ein Mal #include-ierst. Schön wird es deswegen trotzdem nicht. 
Variablen gehören allenfalls mit "extern" in ein Headerfile (Ausnahme: 
inline).

Die fehlenden Funktionsexports musst du dir dann über ein anderes 
Headerfile exportieren.

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Chris K. schrieb:
> Hilft mir jetzt aber leider auch nicht dabei weiter
> das Problem zu lösen, dass der ursprüngliche Verfasser des Quelltextes
> eingebaut hat.

Und was hindert dich dran, das Problem auszubauen? Du hast den gesamten 
Quelltext doch vor dir. Also teile die Datei in eine Header- und eine 
Sourcedatei auf, und alles wird gut.

Oliver

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.