Forum: Mikrocontroller und Digitale Elektronik C: One definition rule bei structs


von Walter T. (nicolas)


Lesenswert?

Hallo zusammen,

ich habe ein großes , konstantes, globales struct, was eine globale 
Konfiguration meines µC-Programms enthält.

Die Deklaration und das Typedef befindet sich in einer Header-Datei. Die 
Definition in einer *.c-Datei.

Um die Benennung der Variablen und ihren Inhalt nicht komplett getrennt 
bearbeiten zu müssen, gibt es ein großes #define. In der *.c-Datei wird 
die Variable dann mit dem großen Define zugewiesen. Ungefähr so:
1
settings_header.h
2
    typedef struct Settings_S
3
    {
4
       int var0;
5
       int var1;
6
       int var2;
7
    }
8
    Settings_t;
9
10
    extern Settings_t Settings;
11
12
    #define SETTINGS_DEFAULT \
13
    {\
14
        .var0 = 0,\
15
        .var1 = 2,\
16
        .var2 = 1,\
17
    }
und
1
#include "settings_header.h"
2
3
Settings_t Settings = (Settings_t) SETTINGS_DEFAULT;
Was mich stört: Fehlersuche. Ein Semikolon statt Komma kann man schon 
ein Weilchen suchen, weil der Compiler logischerweise die Zeile der 
"echten" Definition nicht kennt.

Geht das auch schöner?

von Oliver S. (oliverso)


Lesenswert?

Bei der Menge der Fragen muß ich dann doch mal den Wilhelm machen, und 
die Cpp Core Guideline CPL.1 zitieren:

"Prefer C++ to C"

Das Leben könnte so einfach sein ;)

Oliver

von Walter T. (nicolas)


Lesenswert?

Oliver S. schrieb:
> Das Leben könnte so einfach sein

Mitten in einem Projekt eine neue Programmiersprache zu lernen, gehört 
nicht zu den Dingen, die das Leben vereinfachen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Walter T. schrieb:
> Was mich stört: Fehlersuche. Ein Semikolon statt Komma kann man schon
> ein Weilchen suchen, weil der Compiler logischerweise die Zeile der
> "echten" Definition nicht kennt.

Doch die kennt er :-)

Problem ist aber, dass Location-Info aus #line Notes bezogen wird, die 
ins Präcompilat eingefügt wurden und sich auf die c-Datei beziehen und 
eben nicht aufs Präcompilat.

Ergo:

Wenn in einem Makro ein Tippfehler ist und man aus den üblichen, auf's 
.c / .h bezogenen Fehlermeldung nicht schlau wird, dann kann man bei GCC 
übersetzten mit
1
-save-temps -P

Dies bewirkt zweierlei:

1) Das Präcompilat wird nicht gelöscht und bleibt als .i erhalten (.ii 
bei C++, .s bei Assembler).

2) Die Fehlermeldung bezieht sich auf die übersetzte Quelle, also auf's 
i-File.  Damit kommt man direkt zur Fehlerstelle und kann so besser auf 
den Fehler im Makro zurückschließen.

Für C++ geht das natürlich genauso, nur indem man einen g++ nimmt ändert 
sich ja nichts daran, dass sich die Diagnostic auf die .cpp Datei 
bezieht und nicht auf's .ii.

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Johann L. schrieb:
> Für C++ geht das natürlich genauso, nur indem man einen g++ nimmt ändert
> sich ja nichts daran, dass sich die Diagnostic auf die .cpp Datei
> bezieht und nicht auf's .ii.

Schon. Allerdings kann man in C++ die default-Initialisierungen gleich 
mit in die Deklaration schreiben, und damit das tritt das Problem gar 
nicht auf.

Oliver

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wenn ich das Komma in Zeile 14 der Header-Datei in ein Semikolon ändere,
erhalte ich folgende Fehlermeldung (GCC 9.1.0):

1
In file included from settings.c:1:
2
settings_header.h:14:12: error: expected ‘}’ before ‘;’ token
3
   14 |   .var1 = 2;\
4
      |            ^~
5
   15 |   .var2 = 1,\
6
      |             
7
settings_header.h:14:12: note: in definition of macro ‘SETTINGS_DEFAULT’
8
   14 |   .var1 = 2;\
9
      |            ^~
10
   15 |   .var2 = 1,\
11
      |             
12
settings_header.h:12:1: note: to match this ‘{’
13
   12 | {\
14
      | ^~
15
   13 |   .var0 = 0,\
16
      |  
17
settings_header.h:12:1: note: in definition of macro ‘SETTINGS_DEFAULT’
18
   12 | {\
19
      | ^~
20
   13 |   .var0 = 0,\
21
      |

Der Compiler meckert zwar statt des falschen Semikolons eine fehlende
geschweifte Klammer an (das ist für ihn schwer zu unterscheiden, ohne
die nachfolgenden Zeilen bereits im Blick zu haben), zeigt aber dennoch
mit seinem Finger genau auf die richtige Stelle im Code.

Vielleicht wird es so langsam doch mal Zeit, deinen Compiler upzudaten.
Seit deiner 4.x-Version wurden viele deutliche Verbesserungen
vorgenommen, insbesondere auch bei der Qualität der Fehlermeldungen.

von leo (Gast)


Lesenswert?

Walter T. schrieb:
> Settings_t Settings = (Settings_t) SETTINGS_DEFAULT;
> Was mich stört: Fehlersuche. Ein Semikolon statt Komma kann man schon
> ein Weilchen suchen,

Ja, die #define's sind hinderlich.

Folgendes hilft ev.:
https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html

leo

von Wilhelm M. (wimalopaan)


Lesenswert?

Oliver S. schrieb:
> Bei der Menge der Fragen muß ich dann doch mal den Wilhelm machen, und
> die Cpp Core Guideline CPL.1 zitieren:
>
> "Prefer C++ to C"
>
> Das Leben könnte so einfach sein ;)
>
> Oliver

Ich danke Dir: besser hätte ich es nicht schreiben können.

Nur noch zwei Hinweise (auch wenn der Thread mit C überschrieben ist):

1) Du brauchst keine neue Sprache zu lernen, denn man kann ja 
inkrementell auf C++ umsteigen, also genau diesen einen Punkt 
verbessern.

2) Sollte es mal komplizierter werden mit den Initialisierungen (etwa 
Arrays, deren Elemente man etwas komplizierter berechnen muss): IIFE 
(immediate invoked function expressions), das sind 
constexpr-lambda-expressions, die man sofort aufruft, und die zur 
Compilezeit ein Objekt liefern, mit dem dann 
kopier-/verschiebungsinitialisiert wird.

von Markus F. (mfro)


Lesenswert?

Man kann durchaus auch in C (natürlich längst nicht so schön wie in C++) 
"information hiding" betreiben. Einigermassen elegant geht das mit einer 
separaten Übersetzungseinheit, die "nach aussen" nur Setter und Getter 
bereitstellt.

Muß also nicht unbedingt gleich C++ lernen (aber sich Macros abgewöhnen 
- zumindest dort, wo man sie nicht unbedingt braucht).

Beitrag #5923935 wurde vom Autor gelöscht.
von Walter T. (nicolas)


Lesenswert?

C++ steht zwar auf der Liste von Sachen, die ich mal anfangen will - 
aber zu meiner Aussage von oben stehe ich weiterhin. Eine neue 
Programmiersprache lernt man am besten, wenn man ein kleines neues 
Projekt aufzieht.

Wenn man ein altes Projekt mit frisch erworbenem Wissen und Konzepten 
umstellen wollte, produziert man hauptsächlich Blindleistung.

Ich gehe mal davon aus, daß wenn selbst Johann eher darauf hinweist, wie 
man Fehler in der obigen Vorgehensweise findet, daß es keine wirklich 
viel schönere Alternative gibt.

Was auch noch ginge, wäre eine Definition in eine #ifdef-Abzweigung zu 
packen, die nur in einer inkludierenden Datei definiert ist. Aber 
wirklich viel schöner ist das auch nicht.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Walter T. schrieb:
> C++ steht zwar auf der Liste von Sachen, die ich mal anfangen will -
> aber zu meiner Aussage von oben stehe ich weiterhin. Eine neue
> Programmiersprache lernt man am besten, wenn man ein kleines neues
> Projekt aufzieht.

Jein. Ich gehe einfach mal davon aus, dass Du so viel know-how hast, 
dass es Dir kein Kopfzerbechen macht, C und C++ zu mischen. Deswegen 
brauchst Du nicht gleich eine vermeintlich neue Sprache komplett zu 
lernen, nur bestimmte Anteile daraus. Natürlich macht man das nicht an 
einem produktiven System. Man kann aber gezielt einige Sachen 
transferieren.

von torusle (Gast)


Lesenswert?

Walter T. schrieb:
> Was mich stört: Fehlersuche. Ein Semikolon statt Komma kann man schon
> ein Weilchen suchen, weil der Compiler logischerweise die Zeile der
> "echten" Definition nicht kennt.
>
> Geht das auch schöner?

Ganz pragmatisch: Schreib Dir ein kleines Komandozeilen Tool, welches 
eine Config Datei liest (z.B. im JSON Format) und Dir daraus die 
Konfiguration als C-Code rausschmeißt.

Das kannst Du dann im Build Prozess automatisch aufrufen und gut.

von Heiko L. (zer0)


Lesenswert?

Walter T. schrieb:
> Ich gehe mal davon aus, daß wenn selbst Johann eher darauf hinweist, wie
> man Fehler in der obigen Vorgehensweise findet, daß es keine wirklich
> viel schönere Alternative gibt.

Ja, doch. Man könnte auf die statischen Initialisierungen verzichten und 
das sauber mit einer Funktion regeln. Global wirksame defines sind echt 
der Anfang vom Ende. Ich könnte schreien, wenn ich irgendwo in einem 
Header
#define min ...
#define max ...
oder am besten noch
#define B0 ...
sehe.

C++ ist in dieser Hinsicht ungefährlicher, aber immer noch grausam, was 
schlechte Mechanik betrifft: Wie viele tausend Zeilen Quelltext 
inkludiert man, um auch nur einen unique_ptr zu benutzen? Ein schon 
a-priori gescheitertes Konzept, die Implementierung(en) in eine 
_Kopf_datei zu verschieben. Das ist in etwa so, wie seine E-Mails im 
Betreff zu verfassen!
Der Kompiler streckt nach den ersten 50k Zeilen an Templates alle viere 
von sich und braucht für eine 100 Zeilen Datei 2 Sekunden zum 
übersetzen, IDEs mit ihren Indexern können das nicht handeln und einem 
Entwickler bringt ein "Header", der zu 99% aus Implementierungs-Details 
besteht, natürlich auch überhaupt gar nichts. Das nennt man 
Fortschritt.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Heiko L. schrieb:
> Man könnte auf die statischen Initialisierungen verzichten und
> das sauber mit einer Funktion regeln.

Was ist denn an einem Initializer "unsauber"?

Das über eine Funktion zu regeln hat doch nur Nachteile:

* Es funktioniert nicht für const.

* Mehr Code; sowohl für die Funktion als auch für deren Aufruf.

* Das Problem mit dem Makro ist nicht gelöst, denn wir hätten
1
#include "settings_header.h"
2
3
Settings_t Settings;
4
5
void init_Settings()
6
{
7
    Settings = (Settings_t) SETTINGS_DEFAULT;
8
}

Es ist nämlich immer noch unklar, warum hier der Zwischenschritt über 
ein Makro gegangen wurde bzw. dies für notwendig erachtet wurde.

von Heiko L. (zer0)


Lesenswert?

Johann L. schrieb:
> Was ist denn an einem Initializer "unsauber"?

1. Das ist ein globales define, das jegliches textliche Vorkommen seines 
Namens gnadenlos ersetzt ohne jeden Kontextbezug.
2. Details gehören nicht in Header.

Johann L. schrieb:
> * Es funktioniert nicht für const.

Doch, natürlich. Return by value.
Statische Initialisierung geht nicht.

Johann L. schrieb:
> * Mehr Code; sowohl für die Funktion als auch für deren Aufruf.

Touche. Faulheit wird kein Vorschub geleistet.

Johann L. schrieb:
> * Das Problem mit dem Makro ist nicht gelöst, denn wir hätten
> #include "settings_header.h"
>
> Settings_t Settings;
>
> void init_Settings()
> {
>     Settings = (Settings_t) SETTINGS_DEFAULT;
> }

Ich dachte eher an
1
ABC_ABI ABC_INLINE ABC_DECL(Settings, default_settings) ABC_WHATEVER {
2
 ABC_INLINE_RETURN(Settings, { 1,2,3 });
3
}
Wobei ABC_* zu ersetzen wäre durch was immer das Sch*ißding an Compiler, 
mit dem man gerade konfrontiert ist so schluckt (Portabel?!? Am 
A*sch...!!!)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Heiko L. schrieb:
1
> ABC_ABI ABC_INLINE ABC_DECL(Settings, default_settings) ABC_WHATEVER {
2
>  ABC_INLINE_RETURN(Settings, { 1,2,3 });
3
> }

Was soll das denn darstellen?

Mach's mal konkret.

: Bearbeitet durch User
von M.K. B. (mkbit)


Lesenswert?

Heiko L. schrieb:
> C++ ist in dieser Hinsicht ungefährlicher, aber immer noch grausam, was
> schlechte Mechanik betrifft:

Die Implementierung muss in diesem Fall im Header stehen, weil es sich 
um ein Template handelt und der Compiler den Code für alle möglichen 
Typen erzeugen können muss.

Man könnte natürlich sagen, dass ein unique_ptr für alle Typen im 
Prinzip das gleiche macht. Das ist aber nicht der Fall, weil es z.B. für 
Arraytypen int[] ein anderes new/delete gibt. Außerdem kann sich auch 
das Verhalten durch noexcept oder eben kein noexcept ändern.

von M.K. B. (mkbit)


Lesenswert?

torusle schrieb:
> Ganz pragmatisch: Schreib Dir ein kleines Komandozeilen Tool, welches
> eine Config Datei liest (z.B. im JSON Format) und Dir daraus die
> Konfiguration als C-Code rausschmeißt.

Das wäre ja dann quasi das, was der Präprozessor macht, nur mit einem 
eigenen Tool.

von Heiko L. (zer0)


Lesenswert?

Johann L. schrieb:
> Heiko L. schrieb:> ABC_ABI ABC_INLINE ABC_DECL(Settings,
> default_settings) ABC_WHATEVER {
>>  ABC_INLINE_RETURN(Settings, { 1,2,3 });
>> }
> Was soll das denn darstellen?

Das ist eine "portable" Definition einer Funktion, wenn man noch auf 
sowas wie alte C-Compiler Rücksicht nimmt...


> Mach's mal konkret.

Aber ich denke mal, du meinst
1
inline Settings default_settings() {
2
  return (Settings){1,2,3};
3
}

von Heiko L. (zer0)


Lesenswert?

M.K. B. schrieb:
> Die Implementierung muss in diesem Fall im Header stehen, weil es sich
> um ein Template handelt und der Compiler den Code für alle möglichen
> Typen erzeugen können muss.

Apologet! Wenn das ein vernünftiger Grund wäre, würde das ISO-Komitee 
nicht darüber nachdenken, wie Module realisiert werden könnten.

von A. S. (Gast)


Lesenswert?

Ob dein Vorgehen (C, defines) richtig oder gut ist, kann ich nicht 
beurteilen, ich kenne aber Fälle, wo es alternativlos gut ist.

Überleg dir vielleicht, das zu trennen: zu jedem Strukturelement x ein 
define DX, und dann in der .c die Zuweisung explizit.

Durch die feste namens-Konvention ist der Aufwand klein, die 
Flexibilität groß. Z.b. kann DVar1 dann leicht DVar0+5 sein.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

>> Mach's mal konkret.
> Aber ich denke mal, du meinst
1
> inline Settings default_settings() {
2
>   return (Settings){1,2,3};
3
> }

Ich wollte wissen, was DU damit meintest.

Das von dir vorgeschlagene funktioniert jedenfalls nicht als 
Initializer, weil das in C für Variablen im Static Storage keine 
Rückgabewerte von Funktionen sein dürfen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Heiko L. schrieb:
> Das ist eine "portable" Definition einer Funktion, wenn man noch
> auf sowas wie alte C-Compiler Rücksicht nimmt...

Du meinst vor C89, also über 30 Jahre alt, und das verwendet Walter in 
seinem aktuellen Projekt...?

: Bearbeitet durch User
von Heiko L. (zer0)


Lesenswert?

Johann L. schrieb:
> Das von dir vorgeschlagene funktioniert jedenfalls nicht als
> Initializer, weil das in C für Variablen im Static Storage keine
> Rückgabewerte von Funktionen sein dürfen.

Ja, das habe ich auch vor einigen Beiträgen so geschrieben:

Heiko L. schrieb:
> Statische Initialisierung geht nicht.



Johann L. schrieb:
> Du meinst vor C89, also über 30 Jahre alt, und das verwendet Walter in
> seinem aktuellen Projekt...?

Oder zB Microsoft, die 2015 verkündet haben, dass das neue Visual Studio 
jetzt doch schon C99 fast komplett unterstützt.
Gibt es überhaupt irgendeinen Compiler, der das 100% ISO-konform 
übersetzt?

: Bearbeitet durch User
von no C (Gast)


Lesenswert?

C ist halt mittelalterlicher Schei..... !!!

von Bernd K. (prof7bit)


Lesenswert?

torusle schrieb:
> Schreib Dir ein kleines Komandozeilen Tool, welches
> eine Config Datei liest (z.B. im JSON Format) und Dir daraus die
> Konfiguration als C-Code rausschmeißt.

Dann kann er im JSON-Code nach verlorenen Kommas suchen, auch nichts 
gewonnen.

Ich setze zwar auch in Python geschriebene Codegeneratoren ein, aber nur 
wenn ich relativ viel redundanten oder schreibintensiven Boilerplate 
erzeugen will mit relativ wenig Konfiguration. Nicht wenn der Aufwand 
fast gleich und der Gewinn minimal oder gar negativ ist wie in diesem 
Fall.

von Wilhelm M. (wimalopaan)


Lesenswert?

Bernd K. schrieb:
> torusle schrieb:
>> Schreib Dir ein kleines Komandozeilen Tool, welches
>> eine Config Datei liest (z.B. im JSON Format) und Dir daraus die
>> Konfiguration als C-Code rausschmeißt.
>
> Dann kann er im JSON-Code nach verlorenen Kommas suchen, auch nichts
> gewonnen.
>
> Ich setze zwar auch in Python geschriebene Codegeneratoren ein, aber nur
> wenn ich relativ viel redundanten oder schreibintensiven Boilerplate
> erzeugen will mit relativ wenig Konfiguration. Nicht wenn der Aufwand
> fast gleich und der Gewinn minimal oder gar negativ ist wie in diesem
> Fall.

Er wollte doch nicht mitten im Projekt eine neue Sprache lernen ;-)

Das macht man zur Compilezeit alles in C++; zur Not auch einen 
JSON-Parser der Zur Compilezeit arbeitet.

von Bernd K. (prof7bit)


Lesenswert?

Johann L. schrieb:
> Es ist nämlich immer noch unklar, warum hier der Zwischenschritt über
> ein Makro gegangen wurde bzw. dies für notwendig erachtet wurde.

Damit die Initialisierung im Header direkt neben der Strukturdefinition 
stehen kann, nicht in ner anderen Datei. Ist ordentlicher.

Ich persönlich mach das übrigens exakt genauso, man könnte fast meinen 
OP wär in meinen Rechner oder in mein Gehirn eingedrungen und hätte 
meinen Code kopiert. Das geht aber nicht denn der Code ist unter 
Verschluß, nur 3 Leute haben ihn je gesehen, nur 2 haben ihn gelesen, 
also keine Anschuldigung ;-) Ich hab echt kurz innegehalten und mir die 
Augen gerieben so verblüffend identisch ist das in meinem eigenen 
settings-Modul, Zeile für Zeile(!), bis auf die Groß-Kleinschreibung, 
bei mir ist es extern settings_t settings;

Bei mir wird auch der Header beibehalten und nur die C-Datei wird 
ausgetauscht.

Zufälle gibts, erstaunlich...

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Eine Möglichkeit sind XMacros.

Dabei lagert man die Information in eine eigene Datei aus, hier 
field.def.  Eignet sich dann, wenn man es in recht vielen 
unterschiedlichen Konfigurationen / Projekten verwendet und die 
def-Datei mehr als eine handvoll Einträge hat.

In Situationen, die nicht allzu kompliziert sind, einfacher als ein 
externer Code-Generator.

*field.def*
1
// Before including this file, define a macro
2
// FIELD(TYP, NAME, INIT)...
3
4
FIELD (int, var0, 0)
5
FIELD (int, var1, 2)
6
#ifdef VAR2
7
FIELD (int, var2, VAR2)
8
#endif // VAR2

*settings.h*
1
typedef struct
2
{
3
#define FIELD(TYP, NAME, INIT) \
4
    TYP NAME;
5
#include "field.def"
6
#undef FIELD
7
} Settings_t;
8
9
extern Settings_t Settings;

*settings.c*
1
#include "settings.h"
2
3
Settings_t Settings =
4
{
5
#define FIELD(TYP, NAME, INIT) \
6
    .NAME = INIT,
7
#include "field.def"
8
#undef FIELD
9
};

Beispielsweise werden so die bekannten -mmcu= von avr-gcc angegeben und 
an unterschiedlichen Stellen im Compiler verwendet, etwa auch um den 
entsprechenden Teil der texi Dokumentation zu erstellen oder die 
spec-Files.

http://gcc.gnu.org/viewcvs/gcc/trunk/gcc/config/avr/avr-mcus.def?revision=267494&view=markup#l36

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Und weil die originale Frage zu C ist, darf der obligatorische Hinweis 
auf C++ Features nicht fehlen.

Ab C++17 gibt es inline-Objekte, die man in einem Header definieren 
kann.  Zweck ist "Header-only" Programmierung, so dass man keine extra 
Module braucht wenn Objekte (im Static Storage) gebraucht werden:

*settings.h*
1
struct Settings { ... };
2
3
inline Settings settings = { 0, 2, 1 };
Damit hat man dann Typ-Definition und Object-Definition an einer Stelle.

Intern wird settings als ein weak Symbol definiert und als comdat oder 
linkonce — je nach Unterstützung im Linker.  Hier eine Umsetzung als 
comdat:
1
  .weak  settings
2
  .section  .data.settings,"awG",@progbits,settings,comdat
3
  .type  settings, @object
4
  .size  settings, 6
5
settings:
6
  .word  0
7
  .word  2
8
  .word  1

Der Linker verwendet nur ein Object aus der jeweiligen comdat-Gruppe 
(hier setting).

: Bearbeitet durch User
von A. S. (Gast)


Lesenswert?

Bernd K. schrieb:
> Damit die Initialisierung im Header direkt neben der Strukturdefinition
> stehen kann, nicht in ner anderen Datei. Ist ordentlicher.

Aber das kann doch auch der präprozessor:
1
    extern Settings_t Settings;
2
#ifdef _SETTING_C_
3
    Settings_t Settings={
4
        ....

von Walter T. (nicolas)


Lesenswert?

Johann L. schrieb:
> Es ist nämlich immer noch unklar, warum hier der Zwischenschritt über
> ein Makro gegangen wurde bzw. dies für notwendig erachtet wurde.

Ich nehme zwar an, dass sich die Frage erledigt hat, aber der 
Vollständigkeit halber: Es geht darum, die Einstellungen möglichst in 
einer Datei zu deklarieren und zu definieren, weil hier eine Trennung 
syntaktisch nötig, aber inhaltlich ungünstig ist.

Bernd K. schrieb:
> so verblüffend identisch ist das in meinem eigenen
> settings-Modul,
>
> [...]
> Zufälle gibts, erstaunlich...

Hm. Ich hätte gesagt: Einstellungen braucht jedes µC-Projekt, das auch 
etwas macht. Vom Programmablauf sind die Stellen, in denen die 
Einstellungen vorgenommen und/oder gespeichert werden, oft komplett an 
anderen Stellen, als da, wo sie benötigt werden. Und sie müssen irgendwo 
in einem NVRAM gesichert werden.

Also wird vermutlich in jedem kleinen oder mittelgroßen µC-Projekt ein 
großes struct existieren, das von vielen Programmteilen ausgelesen wird, 
in irgendeinem Einstellungsmodul bearbeitet werden kann und in 
irgendeiner Form in einem NVRAM hinterlegt ist. Mit dem netten 
Nebeneffekt, dass man es auch mit dem Debugger übersichtlich lesen und 
verändern kann.

Johann L. schrieb:
> struct Settings { ... };
>
> inline Settings settings = { 0, 2, 1 };

Das liest sich sogar für einen nicht-C++-Kenner erstaunlich naheliegend.

A. S. schrieb:
> extern Settings_t Settings;
> #ifdef _SETTING_C_
>     Settings_t Settings={
>         ....

Und genau eine der inkludierenden Dateien definiert
1
#define _SETTINGS_C
2
   #include settings.h
3
#undef _SETTINGS_C
Genau. Das war die anderen Variante.

Johann L. schrieb:
> Eine Möglichkeit sind XMacros.

Da muß ich nach dem Samstagseinkauf mal gucken, ob ich die schöner oder 
häßlicher als den IST-Stand finde. Es dürfte auf jeden Fall sehr gut 
skalieren.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

A. S. schrieb:
> #ifdef _SETTING_C_

Bezeichner die mit Unterstrich + Großbuchstabe oder 2 Unterstrichen 
anfangen sind reserviert ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Walter T. schrieb:

> Da muß ich nach dem Samstagseinkauf mal gucken, ob ich die schöner oder
> häßlicher als den IST-Stand finde.

Macros sind immer(!) hässlich.

von Walter T. (nicolas)


Lesenswert?

Dr. Sommer schrieb:
> Bezeichner die mit Unterstrich + Großbuchstabe oder 2 Unterstrichen
> anfangen sind reserviert ;-)

Stimmt. Ich habe das nur von oben gecopypasted. Bei mir heißt das 
natürlich     #ifdef INCLUDE_DEFINE_VARIABLES, aber das Prinzip ist das 
Gleiche.

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.