Forum: Mikrocontroller und Digitale Elektronik Konstanten-Datei in C


von Walter T. (nicolas)


Lesenswert?

Guten Morgen,

ich habe eine .c-Quelltext-Datei, die ausschließlich dazu dient, 
Konstanten zu definieren. Diese Konstanten sind Strings und String 
Arrays (Textelemente in Landessprache).

Parallel dazu gibt es natürlich eine .h-Datei, die alle Konstanten als 
extern deklariert und dem Projekt zur Verfügung stellt.

Beide werden momentan separat gepflegt, und es wird langsam umständlich.

Gibt es eine Möglichkeit, die Konstanten (Deklarationen und 
Definitionen) mit Bordmitteln an einer Stelle zu pflegen oder ist es 
sinnvoller, ein Script zu schreiben, das die *.h-Datei aus der *.c-Datei 
erzeugt?

: Bearbeitet durch User
von STK500-Besitzer (Gast)


Lesenswert?

Walter T. schrieb:
> Gibt es eine Möglichkeit, die Konstanten (Deklarationen und
> Definitionen) mit Bordmitteln an einer Stelle zu pflegen

Variablen als static in der .h definieren.
Durch das "static" wird auch bei mehrfachem #include nicht versucht, sie 
mehrfach anzulegen.

von Steve van de Grens (roehrmond)


Lesenswert?

Eigentlich wird der ganze Inhalt einer Include sowieso nur einmal 
"eingelesen", wegen den include guards:
1
#ifndef CONSTANTS_H
2
#define CONSTANTS_H
3
4
... der eigentliche Inhalt
5
6
#endif /* CONSTANTS_H */

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

Korrigiert mich bitte, wenn da ein Denkfehler ist:

Wenn ich jede Konstante als "static" deklariere, wird jeder Textstring 
für jede *.c-Datei, in der er aufgerufen wird, erneut in das Binary 
eingebaut.

Heißt das, daß ich dann zwingend auf Link-Time-Optimization angewiesen 
bin, oder ist der Linker von sich aus intelligent genug, diese 
Duplizierung wieder zu entfernen?

: Bearbeitet durch User
von Peter Pan (Gast)


Lesenswert?

STK500-Besitzer schrieb:
> Durch das "static" wird auch bei mehrfachem #include nicht versucht, sie
> mehrfach anzulegen.

Dafür hat man doch den Include Guard.


Spricht etwas gegen gettext und Alternativen? Schau dir einfach ein paar 
OpenSource Projekte an wie die das einbinden...


https://www.gnu.org/software/gettext/

von Walter T. (nicolas)


Lesenswert?

Peter Pan schrieb:
> Spricht etwas gegen gettext und Alternativen?

Ja. Es geht um einen Mikrocontroller ohne Dateisystem und nur um wenige 
hundert Zeichenketten.

von STK500-Besitzer (Gast)


Lesenswert?

Walter T. schrieb:
> Wenn ich jede Konstante als "static" deklariere, wird jeder Textstring
> für jede *.c-Datei, in der er aufgerufen wird, erneut in das Binary
> eingebaut.

Ich wusste doch, dass ich das noch schreiben sollte:
Die .c-Datei wird dadurch überflüssig.
Die komplette Initialisierung erfolgt in der .h:
1
static const char[][2] = {{"blablabla"},{"Tuuut"}};

das habe ich jetzt nicht getestet.
Vielleicht weiß jemand anders eine schönere Lösung.

von Peter Pan (Gast)


Lesenswert?

Walter T. schrieb:
> Peter Pan schrieb:
>
>> Spricht etwas gegen gettext und Alternativen?
>
> Ja. Es geht um einen Mikrocontroller ohne Dateisystem und nur um wenige
> hundert Zeichenketten.

Brauchst du alle Konstanten zur Laufzeit oder kompilierst du für jede 
Sprache eine eigene Firmware?

von Programmierer (Gast)


Lesenswert?

Steve schrieb:
> Eigentlich wird der ganze Inhalt einer Include sowieso nur einmal
> "eingelesen", wegen den include guards:

Nein! Einmal pro Translation Unit (.c -Datei die mit 1 Compileraufruf 
übersetzt wird). Aber wenn der Header von mehreren .c -Dateien 
inkludiert wird, ist er auch jedes Mal da! Woher soll der Compiler 
wissen, dass beim übersetzen irgendeiner anderen .c -Datei ein 
Inklude-Guard definiert wurde?

Walter T. schrieb:
> Wenn ich jede Konstante als "static" deklariere, wird jeder Textstring
> für jede *.c-Datei, in der er aufgerufen wird, erneut in das Binary
> eingebaut.

Korrekt!

Peter Pan schrieb:
> STK500-Besitzer schrieb:
>> Durch das "static" wird auch bei mehrfachem #include nicht versucht, sie
>> mehrfach anzulegen.
>
> Dafür hat man doch den Include Guard.

Der hilft hier überhaupt nicht. Die in der Headerdatei angelegten 
Konstanten werden pro .c -Datei einmal angelegt.

Walter T. schrieb:
> oder ist der Linker von sich aus intelligent genug, diese
> Duplizierung wieder zu entfernen?

Manche Linker ja. Probier es am Besten einfach aus.

Man kann sich mit fiesen Tricks behelfen, à la:

constants.h:
1
#ifndef CONSTANTS_H_
2
#define CONSTANTS_H_
3
4
#ifdef IMPL_CONST
5
#define DEFCONST(decl,val) decl = val;
6
#else
7
#define DEFCONST(def,val) extern def;
8
#endif
9
10
IMPL_CONST(const char* const mystr, "Hello!")
11
IMPL_CONST(const int foo, 42)
12
13
#endif

constants.c:
1
#define IMPL_CONST
2
#include "constants.h

Dann constants.h normal überall inkludieren, und constants.c normal 
kompilieren und mit linken. Richtig toll ist das aber nicht...

von Jester (Gast)


Lesenswert?

Walter T. schrieb:
> ich habe eine .c-Quelltext-Datei, die ausschließlich dazu dient,
> Konstanten zu definieren. Diese Konstanten sind Strings und String
> Arrays (Textelemente in Landessprache).
>
> Parallel dazu gibt es natürlich eine .h-Datei, die alle Konstanten als
> extern deklariert und dem Projekt zur Verfügung stellt.

Parallele Pflege ist immer doof.

Pflege das ein EINE .def/.hc/.irgendwas ein - aus der du per Script .h 
und .c erzeugst (z.B. per awk, m4, ...). Rule in's Makefile rein und 
fertig.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

warum so kompliziert mit .h und .c? Sind doch nur Konstanten, dafür 
reicht ein Headerfile, soll doch sowieso wo inkludiert bekannt sein. Und 
weil du in C programmierst definierst du sicherlich deine Konstanten mit 
#define und damit werden die sowieso vom Präprozessor gnadenlos im 
Quelltext 1:1 ersetzt. Das heißt von deinem Headerfile bleibt nichts 
übrig und es gibt auch kein Overhead.

Die Linker Option wäre übrigens -flto gewesen.

von Programmierer (Gast)


Lesenswert?

Veit D. schrieb:
> Und
> weil du in C programmierst definierst du sicherlich deine Konstanten mit
> #define und damit werden die sowieso vom Präprozessor gnadenlos im
> Quelltext 1:1 ersetzt.

Was wenn es eine komplexere Konstante mit verschachtelten structs o.ä. 
ist? Die überall einzusetzen ist sicher nicht so wahnsinnig effizient. 
Strings können Linker zum Glück ganz gut de-duplizieren.

von 🐧 DPA 🐧 (Gast)


Lesenswert?

Ich würde mehrere .c und .h Dateien nehmen, und die dementsprechend 
benennen, was drin ist. Dann sollte das eigentlich übersichtlich 
bleiben. Oft ist es auch Man kann auch eine .h Datei machen, die diese 
dann einfach alle mit #include einbindet.

Bei grösseren Texten, Bildern usw. würde ich, sofern man sie wirklich in 
der Anwendung selbst drin haben muss, die .c und .h Dateien generieren 
lassen.

Wenn du wirklich unbedingt einen einzigen Header willst, würde ich das 
in eine Datei packen (ungetestet):
1
#define UNPACK(...) __VA_ARGS__
2
#ifndef DEFINE
3
#define DECDEF(T, V) UNPACK T = UNPACK V;
4
#else
5
#define DECDEF(T, V) extern UNPACK T;
6
#endif
7
8
DEF( (const char data[]), ("Hello, World!") )

Sollte dann "extern const char data[];" generieren, wenn per #include 
eingebunden, und 'const char data[] = "Hello, World!";' wenn vorher 
'#define DEFINE' gesetzt wird. Bei den meisten Compilern kann man das 
oft auch mit "-D" machen, dann braucht man keine separate Datei dafür.

Das mit den () und Unpack ist da, damit wenn man mal Kommas dort hat, 
das nichts aus macht.

von Andreas M. (amesser)


Lesenswert?

Anscheinend versucht der gcc ab -O1 automatisch einen "String Merge" 
über Translation Units hinweg. Wichtig ist wohl nur das man die Strings 
so
1
const char *string = "bla";
und nicht so
1
const char string[] = "bla";
definiert. Es gäbe auch noch die Option "-fmerge-all-constants", dann 
versucht er wohl generell alle Konstanten zu "mergen". Das ist aber 
nicht mehr Standardkonform. Müsste man mal ausprobieren ob das auch mit 
"static" klappt.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Walter T. schrieb:
> oder ist es sinnvoller, ein Script zu schreiben, das die *.h-Datei aus
> der *.c-Datei erzeugt?

Ich würde den Script das gleich aus Textdateien sowohl .c als auch .h 
generieren lassen. Das hat dann noch den Vorteil, dass die Texte 
problemlos auch von Nichtprogrammiererinnen übersetzt werden können.

Oder tatsächlich schauen, ob man gettext dafür MCU-mäßig umbauen kann, 
aber dessen Infrastruktur gleich nachnutzen. Der "_"-Makro muss dann 
halt auf eine interne Tabelle / Minidatenbank gehen statt aufs 
Dateisystem.

von Peter D. (peda)


Lesenswert?

Ich schreibe die Strings in das h-File, aber nur in einem c-File wird 
vor dem Include "DEFINE_PARAMETERS" gesetzt.
1
extern cmd_str_t const dac_str[] PROGMEM;
2
#ifdef DEFINE_PARAMETERS
3
cmd_str_t const dac_str[] PROGMEM =
4
{
5
  MODULE_PARA_VXP "\0",                 "vx+",
6
  MODULE_PARA_VXN "\0",                 "vx-",
7
  MODULE_PARA_VYP "\0",                 "vy+",
8
  MODULE_PARA_VYN "\0",                 "vy-",
9
  MODULE_PARA_GX "\0",                  "gx",
10
  MODULE_PARA_GY "\0",                  "gy",
11
};
12
#endif
Das angehängte 0-Byte dient dazu, daß angemeckert wird, wenn der String 
zu lang wird. Ansonsten würde nur das 0-Byte fehlen.

von Veit D. (devil-elec)


Lesenswert?

Programmierer schrieb:
> Veit D. schrieb:
>> Und
>> weil du in C programmierst definierst du sicherlich deine Konstanten mit
>> #define und damit werden die sowieso vom Präprozessor gnadenlos im
>> Quelltext 1:1 ersetzt.
>
> Was wenn es eine komplexere Konstante mit verschachtelten structs o.ä.
> ist? Die überall einzusetzen ist sicher nicht so wahnsinnig effizient.
> Strings können Linker zum Glück ganz gut de-duplizieren.

Wenn ich mir die AVR Controller typischen Headerfiles anschaue sind da 
structs mit defines ohne Ende drin. Macht alles der Präprozessor.

von Programmierer (Gast)


Lesenswert?

Veit D. schrieb:
> Wenn ich mir die AVR Controller typischen Headerfiles anschaue sind da
> structs mit defines ohne Ende drin. Macht alles der Präprozessor.

Hast du ein Beispiel? Wenn der Compiler bei jeder Nutzung des Makros 
eine temporäre Instant des struct anlegen muss...

von W.S. (Gast)


Lesenswert?

Walter T. schrieb:
> Gibt es eine Möglichkeit, die Konstanten (Deklarationen und
> Definitionen) mit Bordmitteln an einer Stelle zu pflegen oder ist es
> sinnvoller, ein Script zu schreiben, das die *.h-Datei aus der *.c-Datei
> erzeugt?

Mach es einfacher: Die einzelnen Texte nicht exportieren, sondern dir in 
der betreffenden Quelle eine Funktion schreiben, die den gewünschten 
Text je nach Landessprache als Zeiger zurückliefert. Damit schrumpft 
deine .h auf den Export nur eben dieser Funktion zusammen.

W.S.

von Veit D. (devil-elec)


Lesenswert?

Programmierer schrieb:
> Veit D. schrieb:
>> Wenn ich mir die AVR Controller typischen Headerfiles anschaue sind da
>> structs mit defines ohne Ende drin. Macht alles der Präprozessor.
>
> Hast du ein Beispiel? Wenn der Compiler bei jeder Nutzung des Makros
> eine temporäre Instant des struct anlegen muss...

Habe keines zur Hand. Ein AVR Headerfile ... Moment, da stehen jede 
Menge defines aber in den structs sind keine defines, Mist, hätte wetten 
können in den structs stehen auch defines drin. Ich nehme alles zurück. 
Entschuldigung, vorm geistigen Auge in Gedanken dachte ich das wäre so.

Nur werden #defines nicht generell vom Präprozessor ersetzt egal wo die 
stehen? Ist doch reine Textersetzung. Ein struct schränkt doch nur die 
Verwendung bzw. ggf. die Sichtbarkeiten ein. Ich sehe erstmal keine 
Einschränkungen für den Präprozessor.

von Programmierer (Gast)


Lesenswert?

Veit D. schrieb:
> Nur werden #defines nicht generell vom Präprozessor ersetzt egal wo die
> stehen?

Ja eben. Wenn ich sowas mache:
1
struct MyStruct {
2
  int a, b, c;
3
};
4
5
void someAPI (const struct MyStruct* s, int param);
6
7
#define MY_STRUCT_CONSTANT ((struct MyStruct) { 1, 2, 3 })
8
9
void foo (void) {
10
  struct MyStruct s = MY_STRUCT_CONSTANT;
11
  someAPI (&s, 42);
12
}
13
14
void bar (void) {
15
  struct MyStruct s = MY_STRUCT_CONSTANT;
16
  someAPI (&s, 43);
17
}

Habe ich in foo und bar eine komplette Initialisierung des struct. Wenn 
ich das mit einer normalen Konstante mache:
1
struct MyStruct {
2
  int a, b, c;
3
};
4
5
void someAPI (const struct MyStruct* s, int param);
6
7
const struct MyStruct MY_STRUCT_CONSTANT = { 1, 2, 3 };
8
9
void foo (void) {
10
  someAPI (&MY_STRUCT_CONSTANT, 42);
11
}
12
13
void bar (void) {
14
  someAPI (&MY_STRUCT_CONSTANT, 43);
15
}

Spart das einiges an Code ein, weil das struct einmal statisch angelegt 
wird.

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> Nur werden #defines nicht generell vom Präprozessor ersetzt egal wo die
> stehen?

Ja. und das ist ja das große Problem, dass die Macros nicht ge-scoped 
sind. Dies ist einer der Gründe, warum man ihn bspw. in C++ los werden 
will.

Betrachte den CPP als nicht-interaktiven Editor.

von vnnn (Gast)


Lesenswert?

Walter T. schrieb:
> Gibt es eine Möglichkeit, die Konstanten (Deklarationen und
> Definitionen) mit Bordmitteln an einer Stelle zu pflegen oder ist es
> sinnvoller, ein Script zu schreiben, das die *.h-Datei aus der *.c-Datei
> erzeugt?

Die vermutlich schnellste Lösung ist es, durch eine Scriptsprache deiner 
Wahl (Python bietet sich heutzutage an) *.c und *.h aus einem Markup 
deiner Wahl (XML, json, yaml, ini) zu generieren. Sollte sich in unter 
100 Zeilen ausgehen.

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Programmierer schrieb:
> Man kann sich mit fiesen Tricks behelfen, à la:

Es gibt noch die Variante eine dritte Datei einzuführen und die jeweils 
in eine .c und .h Datei zu includen. Damit sind .c und .h Datei gleich 
hässlich:

constants.c:
1
#define IMPL_CONST
2
#include "constants.data

constants.h:
1
#ifndef CONSTANTS_H_
2
#define CONSTANTS_H_
3
#undef IMPL_CONST
4
#include "constants.data
5
#endif

> Richtig toll ist das aber nicht...

Ja aber viel mehr kann C nun mal nicht.

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

vnnn schrieb:
> Die vermutlich schnellste Lösung ist es, durch eine Scriptsprache deiner
> Wahl (Python bietet sich heutzutage an) *.c und *.h aus einem Markup
> deiner Wahl (XML, json, yaml, ini) zu generieren. Sollte sich in unter
> 100 Zeilen ausgehen.

Wenn man auf alles verzichtet (Markup, Bequemlichkeit, Leerzeilen, 
Kommentare, Multi-Line, ...) dann ist das ein Einzeiler .c -> .h:
1
cat x.c
2
const int foo = 42;
3
const int bar = 0815;
4
5
sed -e '1i\#ifndef X_H_\n#define X_H_' -e '/^$/d; s/^/extern /; s/ *=.*/;/' -e '$a\#endif' x.c
6
#ifndef X_H_
7
#define X_H_
8
extern const int foo;
9
extern const int bar;
10
#endif

von EAF (Gast)


Angehängte Dateien:

Lesenswert?

Walter T. schrieb:
> Parallel dazu gibt es natürlich eine .h-Datei, die alle Konstanten .....
> dem Projekt zur Verfügung stellt.

Daten in *.h?
z.B. Strings, oder Variablen?

C kann das nicht wirklich.
C++ allerdings schon.


Ein Grund mehr C++ statt C zu verwenden.......

von Walter T. (nicolas)


Angehängte Dateien:

Lesenswert?

Peter Pan schrieb:
> Brauchst du alle Konstanten zur Laufzeit oder kompilierst du für jede
> Sprache eine eigene Firmware?

Es gibt für jede Sprachversion ein eigenes Binary. Ich gebe mich nicht 
der Illusion hin, dass meine Projekte derart interessant sind, dass sie 
international großes Interesse finden. Andererseits hat es sich 
allerdings angeboten, alle sichtbaren Texte zentral zu halten, schon 
allein um die Nutzerführung konsistent zu halten.

Ich habe mal einen Auszug zum IST-Zustand angehängt. Falls euch 
Inkonsistenzen auffallen: Ja, genau, die waren der Anlass zur oben 
gestellten Frage.

Will ich unbedingt eine einzelne Datei? Nein, das ist kein Muss. Ich bin 
lediglich auf der Suche nach einer brauchbaren Praxis.

(Es passiert sonst zu leicht, dass man irgendetwas übersieht, was 
ansonsten jeder zu wissen scheint. Es hätte mich z.B. nicht gewundert, 
wenn automatisch generierte Header mittlerweile zum Standardrepertoire 
von Build-Umgebungen gehörten und ich einfach nur nicht den korrekten 
Suchbegriff gefunden hätte.)

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

Walter T. schrieb:
> Es hätte mich z.B. nicht gewundert,
> wenn automatisch generierte Header mittlerweile zum Standardrepertoire
> von Build-Umgebungen gehörten und ich einfach nur nicht den korrekten
> Suchbegriff gefunden hätte.

Nachdem ich eine Nacht drüber geschlafen habe, wäre ein Automatismus, .C 
und .H-Dateien konsistent zu halten, genau etwas, was ich wirklich gerne 
hätte, da es einiges an Scroll- und Schreibarbeit sparte.

Allerdings bin ich gerade absolut davon überfordert, wie das 
Plugin-System in EmBitz funktioniert. Ich finde keine Doku. Es wird wohl 
ein unerfüllter Traum bleiben.

von PittyJ (Gast)


Lesenswert?

EAF schrieb:
> Walter T. schrieb:
>> Parallel dazu gibt es natürlich eine .h-Datei, die alle Konstanten .....
>> dem Projekt zur Verfügung stellt.
>
> Daten in *.h?
> z.B. Strings, oder Variablen?
>
> C kann das nicht wirklich.
> C++ allerdings schon.
>
> Ein Grund mehr C++ statt C zu verwenden.......

Ich packe meine Konstanten auch immer als Member in C++ Klassen. Dann 
müssen sie noch über den Klassennamen noch angesprochen werden.
Für 2 unterschiedlichen Buffersize bei Ethernet und SPI gibt es dann
Ethernet::Buffersize
SPI::Buffersize
die auch andere Werte haben können.

Und statt gcc kann man im Makefile auch g++ benutzen, und schon hat man 
diese Features.

von Wilhelm M. (wimalopaan)


Lesenswert?

Warum erstellst Du nicht einfach ein Zugriffsfunktion?
1
#pragma once
2
3
enum Text {text1, text2};
4
typedef enum Text text_t;
5
6
extern const char* const data[];
7
8
inline const char* tr(const text_t id) {
9
    return data[id];
10
}

und dann
1
#include "text.h"
2
3
const char* const data[] = {
4
    [text1] = "bla",  
5
    [text2] = "blubb",  
6
};

von Wilhelm M. (wimalopaan)


Lesenswert?

PittyJ schrieb:
> Ich packe meine Konstanten auch immer als Member in C++ Klassen.

Ich glaube es ging um C ;-)

PittyJ schrieb:
> Und statt gcc kann man im Makefile auch g++ benutzen, und schon hat man
> diese Feature

Womit man sich ein paar kleine Differenzen einhandelt.

Doch grundsätzlich stimme ich Dir natürlich zu und vertrete das ja hier 
im Forum auch immer vehement, wofür ich ja dann auch mein Fett weg 
bekomme ;-)

Ich sage ja immer, dass 90% des C-Codes problemlos als C++ kompilieren / 
funktionieren würden, und man hätte die Chance, die kleinen "Goodies" zu 
verwenden (wie hier etwa eine Klasse mit static-Elementen.

: Bearbeitet durch User
von Jester (Gast)


Lesenswert?

Schreibfaul wie ich bin, hätte ich mir ein locale.hc gebaut, und daraus 
dann gawk ein locale.de.c und locale.h bauen lassen - inclusive dem 
ganzen '#include'-, '#define'- und 'constflash char'-Gedöns. Ich hätte 
mir sogar überlegt, ob ich die "Gänsefüßle" mir antun will.
1
// Fehlermeldung: EEPROM-Daten nicht richtig geschrieben
2
LT_EEPROMWRITEFAILED
3
{
4
    "   Fehler 0x%04x\n"
5
    "   EEPROM-Daten \n"
6
    "   nicht korrekt\n"
7
    "   geschrieben"
8
}
9
10
// Erfolgsmeldung: EEPROM-Daten richtig geschrieben
11
LT_EEPROMWRITESUCCESS
12
{
13
    "   \n"
14
    "   EEPROM-Daten\n"
15
    "   korrekt\n"
16
    "   geschrieben"
17
}
18
19
// Settings-Menue
20
MT_SETTING_PID_KP
21
{
22
    "PID: Kp\t:\t%3.1f\t [1]"
23
}
24
25
MT_SETTING_PID_KP0
26
{
27
    "PID: Kp0\t:\t%4.3f\t [0...1]"
28
}

Statt '{}' kann man sich auch '[]' oder '<< >>' vorstellen.

Oder man bastelt da eine Sprachkennung mit rein, z.B.
1
LT_EEPROMWRITEFAILED
2
de{
3
    "   Fehler 0x%04x\n"
4
    ...
5
}
6
en{
7
    "   error 0x%04x\n"
8
    ...
9
}
10
is{
11
    "   villumelding ...

Weil ich ein Mega-Faultier bin, frickle ich für den gawk-Aufruf eine 
extra Rule ins Makefile ...

gawk (früher hätte ich dazu lex und yacc bemüht) kann ich dir aber auf 
die Schnelle nicht beibringen. 
https://www.gnu.org/software/gawk/manual/gawk.pdf vermittelt aber einen 
sehr guten Start.

just my 2ct

von Walter T. (nicolas)


Lesenswert?

Wilhelm M. schrieb:
> Warum erstellst Du nicht einfach ein Zugriffsfunktion?

Welchen Vorteil habe ich dadurch?

von Wilhelm M. (wimalopaan)


Lesenswert?

Walter T. schrieb:
> Wilhelm M. schrieb:
>> Warum erstellst Du nicht einfach ein Zugriffsfunktion?
>
> Welchen Vorteil habe ich dadurch?

Na, den Vorteil von jeder Indirektion: Du kannst weitere Erfordernisse 
in der Funktion berücksichtigen, ohne etwas an der Aufrufstelle ändern 
zu müssen. Du kannst bspw. dort eine Spracheinstellung berücksichtigen.

von Daniel A. (daniel-a)


Lesenswert?

Man könnte auch alle Texte dort definieren, wo man sie braucht. Hier mal 
ein Beispiel, wo ich das c11 _Generic Feature verwende, und die Sprache 
per Macro setze. Der Compiler muss das dann im compile step auflösen. 
Wenn die Sprache fehlt, bricht er ab, und gibt an, wo es fehlt. Und 
falls man doch mal alle Sprachen zur Runtime brauchen sollte, kann man 
einfach das Macro anpassen.
Und als kleiner Bonus, bei Format Strings in printf, mit eingestellten 
Warnungen, warnt er sogar, wenn da was nicht passt!

https://godbolt.org/z/3crY18zT6
1
#ifndef LANG
2
#error "Please set the language using the LANG macro!"
3
#define LANG en
4
#endif
5
6
#define CONCAT(A,B) A ## B
7
#define CONCAT_EVAL(A,B) CONCAT(A, B)
8
9
#define I_C(...) _Generic((struct CONCAT_EVAL(l_,LANG)*)0, __VA_ARGS__ int*: 0)
10
#define I_L(LANG, ...) struct CONCAT_EVAL(l_,LANG)*: (__VA_ARGS__),
11
12
13
#include <stdio.h>
14
int main(){
15
  puts(I_C(
16
    I_L(de, "Miau!")
17
    I_L(en, "Meow!")
18
    I_L(jp, "Nya!")
19
  ));
20
}

gcc -std=c11 -DLANG=en beispiel.c -o beispiel

: Bearbeitet durch User
von Erich (Gast)


Lesenswert?

Walter T. schrieb:
> Gibt es eine Möglichkeit, die Konstanten (Deklarationen und
> Definitionen) mit Bordmitteln an einer Stelle zu pflegen

Du solltest eine Lösung implementieren,
wo die Texte  ÜBERHAUPT NICHT  über den Compiler laufen!

Eine simple Textdatei, wo jeder Textstring mit einer Kennung (Nummer) 
versehen wird.
Beispiel:

001:Messung läuft
002:Bitte warten
003:Abbruch!
004:usw.

Diese Textdatei wird dann auf eine feste "gerade" Adresse im 
Speicherbereich einkopiert, z.B. 0x8000 . Tool z.B. SRecord
Deinem Compiler bzw. Linker mußt du beibringen, wo die Startadresse und 
(maximale) Größe des Textes ist (aufrunden z.B. auf volle 16k).
Somit wäre in diesem Beispiel der Textbereich 0x8000 ... 0xBFFF
Musst eben passende Stelle für deinen uC finden.

In der SW brauchst du dann eine Textausgabefunktion mit den Parametern
- Zeile       am Display zur Anzeige des Textes
- Spalte      am Display zur Anzeige des Textes
- Textnummer  Nummer des auszugebenden Textes
also z.B. Display(10,1,002)   schreibe "Bitte warten" in Zeile 10, 
Spalte 1

Die Funktion Display(..) muß dann die Kennung des Textes Nr. 002 im 
Speicher finden anhand seines dort befindlichen Headers "002:"
Weiter ist nötig, das jeweilige Textende zu finden. Entweder ist das das 
<CR><LF> Zeichen welches automatisch vorhanden ist. Oder man führt ein 
definiertes Endezeichen zu jedem Text dazu, ein Zeichen das niemals 
benötigt wird. Z.B. das "\"
002:  Bitte warten  \

Hoffe diese Ausführungen helfen.
Hatte mal ein Industrie-Meßgerät betreut, da gab es 14 Sprachvarianten.
Fremdsprachenkundige Sekrätarin konnte die Textdatei(en) nach Vorlage 
editieren.

Gruss

von A. S. (Gast)


Lesenswert?

Walter T. schrieb:
> Es wird wohl
> ein unerfüllter Traum bleiben.

Hannes hat einen Einzeiler dazu gepostet.

Hannes J. schrieb:
> sed -e '1i\#ifndef X_H_\n#define X_H_' -e '/^$/d; s/^/extern /; s/
> *=.*/;/' -e '$a\#endif' x.c

Ob er klappt, weiß ich nicht, aber mehr braucht es nicht. Ich nutze awk 
(genauer mawk) für sowas, ist ähnlich. Einmal in "Regulare Expressions" 
einarbeiten und schon hat man einen Textfresser mit dem man gerade für C 
wirklich tolle Sachen machen kann (die in C++ nativ gehen aber nicht 
einfacher sind).

Einfach per batch, makefile oder manuell per Kommandozeile. Kein "file 
öffnen und lesen und interpretieren" sondern viele Kommandos zum 
Ersetzen oder Verarbeiten von Zeichenketten oder durch Leerzeichen 
getrennte Worte/Zahlen

Aufruf mit der Quelldatei (x.c) und umleitung (>) in die Zieldatei (x.h)

von Walter T. (nicolas)


Lesenswert?

A. S. schrieb:
> Walter T. schrieb:
>> Es wird wohl
>> ein unerfüllter Traum bleiben.
>
> Hannes hat einen Einzeiler dazu gepostet.

Das ist nicht das Problem.

Man will doch (natürlich) die .H-Datei eher halbautomatisch erstellen, 
d.h. die Deklarationen von nicht-static-Funktionen und -Variablen sollte 
automatisch erstellt (und bereinigt werden, wenn etwas verschwindet), 
andererseit gibt es natürlich auch immer noch Aspekte der Schnittstelle, 
die man händisch anpassen will, z.B. #includes oder Präprozessor Makros.

Aber auch das wäre vermutlich gar kein so großes Problem in Python oder 
so selbst zu schreiben.

Das Problem ist, dass ich das Plugin-Konzept meiner IDE (EmBitz) nicht 
verstehe, weil ich dazu keine Doku finde.

Aber vielleich wird das auch "blos" ein Rechtsklick-Tool für den 
normalen Dateimanager, nicht für die IDE. Mal sehen. Vielleicht habe ich 
nächste Woche mal Langeweile.

von Wilhelm M. (wimalopaan)


Lesenswert?

Walter T. schrieb:
> A. S. schrieb:
>> Walter T. schrieb:
>>> Es wird wohl
>>> ein unerfüllter Traum bleiben.
>>
>> Hannes hat einen Einzeiler dazu gepostet.
>
> Das ist nicht das Problem.

Brauchst Du doch gar nicht.

> Das Problem ist, dass ich das Plugin-Konzept meiner IDE (EmBitz) nicht
> verstehe, weil ich dazu keine Doku finde.

make

von Daniel A. (daniel-a)


Lesenswert?

Der Vorteil bei Verwendung von eigenen Makefiles ist, dass die 
verwendete IDE dann egal ist, man kann sie sogar weg lassen, wenn man 
will. Und man kann es machen & generieren lassen, was auch immer man 
will.

von EAF (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Doch grundsätzlich stimme ich Dir natürlich zu und vertrete das ja hier
> im Forum auch immer vehement, wofür ich ja dann auch mein Fett weg
> bekomme ;-)

Jetzt höre doch mal auf zu jammern!

Ich vertrete hier eine ähnliche Linie wie du, nämlich: Bevorzuge C++.
Bekomme ich hier Haue dafür?
Nein! (eher nein, als ja)

Nee, du bekommst hier gerne mal Haue, weil du dich manchmal wie ein 
arroganter Pinsel benimmst. Damit gehst du einigen auf den Keks. Auch 
mir.
Und jetzt bist du sogar auch noch ein jammernder Pinsel....

Merke:
Kompetenz ist nicht alles!

von Wilhelm M. (wimalopaan)


Lesenswert?

EAF schrieb:
> Wilhelm M. schrieb:
>> Doch grundsätzlich stimme ich Dir natürlich zu und vertrete das ja hier
>> im Forum auch immer vehement, wofür ich ja dann auch mein Fett weg
>> bekomme ;-)
>
> Jetzt höre doch mal auf zu jammern!

Du siehst den Smiley, oder?
Also: nix jammern.
Und: negative Bewertung nicht vergessen ;-)

von EAF (Gast)


Lesenswert?

Wilhelm M. schrieb:
> negative Bewertung nicht vergessen
Schon wieder am jammern!

Da gibts nichts zu vergessen!
Als Gast sehe ich weder Bewertungen, noch kann ich welche abgeben!

Beitrag #7214088 wurde vom Autor gelöscht.
von Wilhelm M. (wimalopaan)


Lesenswert?

EAF schrieb:
> Wilhelm M. schrieb:
>> negative Bewertung nicht vergessen
> Schon wieder am jammern!

Du hast den Smiley schon wieder übersehen und wohl auch absichtlich 
nicht in die Zitatzeile übernommen.

von EAF (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Du hast ....

Es hört gar nicht auf mit dem jammern....

Ein Tipp (Gruppendynamik):
Wenn man seine Weltanschauung auf andere projiziert, werden die anderen 
darauf reagieren. Allerdings nicht immer so, wie man sich das wünscht.

Und wer jammert, macht sich so selber zu einem Backpfeifengesicht.

von Wilhelm M. (wimalopaan)


Lesenswert?

EAF schrieb:
> Wilhelm M. schrieb:
>> Du hast ....
>
> Ein Tipp (Gruppendynamik):
> Wenn man seine Weltanschauung auf andere projiziert, werden die anderen
> darauf reagieren. Allerdings nicht immer so, wie man sich das wünscht.

Weltanschauung? Du scheinst Dich ja ziemlich angegriffen zu fühlen, wenn 
jemand einfach feststellt, dass Du nicht zitieren kannst bzw. willst. 
Wahrscheinlich auch ein Grund dafür, dass Du hier nur als Gast 
schreibst.

von S. R. (svenska)


Lesenswert?

Wilhelm M. schrieb:
> Weltanschauung?

Naja, deine Beiträge zu C++ und zukünftigen Standards tragen schon stark 
religiöse Züge in sich, inklusive des "ignorieren wir die Realität".

Zumindest oft genug, dass ich technische C++-Beiträge (also Code) von 
dir inzwischen grundsätzlich ignoriere. War für mich noch nie sinnvoll 
anwendbar.

von J. S. (jojos)


Lesenswert?

GCC hatte lange die hässliche Eigenart mehrfach Deklarationen in 
Headerfiles zu tolerieren. D.h. ein 'int something' konnte per header in 
mehreren Modulen angelegt werden und es war trotzdem eine globale 
Variable. Erst ab GCC10 gibt es Mecker vom Linker.

von Wilhelm M. (wimalopaan)


Lesenswert?

J. S. schrieb:
> GCC hatte lange die hässliche Eigenart mehrfach Deklarationen in
> Headerfiles zu tolerieren. D.h. ein 'int something' konnte per header in
> mehreren Modulen angelegt werden und es war trotzdem eine globale
> Variable. Erst ab GCC10 gibt es Mecker vom Linker.

Das ist normal und nennt sich "tentative definition", also eine 
Defintion ohne Initialisierung. Die kann / darf zusammen gefasst werden. 
Da das eben viele so nicht wissen, hat gcc dafür einen Schalter.

von J. S. (jojos)


Lesenswert?

Nur das es jetzt als Fehler bewertet wird und das vorherige Verhalten 
als Sonderfall, was dann Änderungen im Code oder Compilereinstellungen 
nötig macht.
https://gcc.gnu.org/gcc-10/porting_to.html

von Wilhelm M. (wimalopaan)


Lesenswert?

J. S. schrieb:
> Nur das es jetzt als Fehler bewertet wird und das vorherige Verhalten
> als Sonderfall, was dann Änderungen im Code oder Compilereinstellungen
> nötig macht.
> https://gcc.gnu.org/gcc-10/porting_to.html

Naja, die "tentative definitions" waren schon immer so, nur die meisten 
haben es nicht gewusst. Ein steter Quell von Fragen, daher dann die 
Änderung des Verhaltens des gcc.

von Wilhelm M. (wimalopaan)


Angehängte Dateien:

Lesenswert?

Im Anhang eine konkrete Umsetzung.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

J. S. schrieb:
> GCC hatte lange die hässliche Eigenart mehrfach Deklarationen in
> Headerfiles zu tolerieren.

Ist explizit im Standard als eine von zwei Implementierungsmöglichkeiten 
genannt worden. War wohl historisch so üblich, daher hat GCC das auch 
lange Zeit als Default gehabt.

Die Option dazu ist -fcommon (bzw. -fno-common).

Neuere GCC-Versionen haben die Voreinstellung von -fcommon auf 
-fno-common geändert.

: Bearbeitet durch Moderator
von Wilhelm M. (wimalopaan)


Lesenswert?

Jörg W. schrieb:
> J. S. schrieb:
>> GCC hatte lange die hässliche Eigenart mehrfach Deklarationen in
>> Headerfiles zu tolerieren.
>
> Ist explizit im Standard als eine von zwei Implementierungsmöglichkeiten
> genannt worden. War wohl historisch so üblich, daher hat GCC das auch
> lange Zeit als Default gehabt.

s.a. oben:

Beitrag "Re: Konstanten-Datei in C"

von A. S. (Gast)


Lesenswert?

Walter T. schrieb:
> Man will doch (natürlich) die .H-Datei eher halbautomatisch erstellen,
> d.h. die Deklarationen von nicht-static-Funktionen und -Variablen sollte
> automatisch erstellt (und bereinigt werden, wenn etwas verschwindet),
> andererseit gibt es natürlich auch immer noch Aspekte der Schnittstelle,
> die man händisch anpassen will, z.B. #includes oder Präprozessor Makros.

Das ist nicht wirklich ein Problem. Höchstens der Sprung ins kalte 
Wasser.

 * Es reicht ein Einzeiler.
 * automatischer Aufruf ist immer machbar. Die Wege sind nur 
verschieden.
 * Die erzeugte .h selber braucht keine #include-guards etc. Das kann 
eine "Wrapper".h leisten. Oder speziell gekennzeichnete Bereiche in der 
.c, je nach Textfresser. Oder Du machst aus dem Einzeiler einen 
Dreizeiler.

Wenn Du bei C bleiben willst (und das ist völlig OK, zumindest mache ich 
das auch), dann hast Du mit Textfressern viele Möglichkeiten, die Dir 
sauberen/lesbaren C-Code erlauben der Wartbar bleibt (Wartbar = 
Änderungen nur an einer Stelle)

von Jester (Gast)


Lesenswert?

A. S. schrieb:
> Walter T. schrieb:
>> Man will doch (natürlich) die .H-Datei eher halbautomatisch erstellen,
>> d.h. die Deklarationen von nicht-static-Funktionen und -Variablen sollte
>> automatisch erstellt (und bereinigt werden, wenn etwas verschwindet)

> Wenn Du bei C bleiben willst (und das ist völlig OK, zumindest mache ich
> das auch), dann hast Du mit Textfressern viele Möglichkeiten, die Dir
> sauberen/lesbaren C-Code erlauben der Wartbar bleibt (Wartbar =
> Änderungen nur an einer Stelle)

Das hatte ich ihm vor Tagen schon nahe gelegt:

Jester schrieb:
> Pflege das [in] ein EINE .def/.hc/.irgendwas ein - aus der du
> per Script .h und .c erzeugst (z.B. per awk, m4, ...).
> Rule in's Makefile rein und fertig.

Und später noch mal (sogar mit Beispiel und opt. Unterstützung von 
Multi-lingualität):

Jester schrieb:
> Schreibfaul wie ich bin, hätte ich mir ein locale.hc gebaut, und daraus
> dann gawk ein locale.de.c und locale.h bauen lassen
> [...]
> Weil ich ein Mega-Faultier bin, frickle ich für den gawk-Aufruf eine
> extra Rule ins Makefile ...

Du hattest es ihm dann auch noch mal schmackhaft gemacht:
A. S. schrieb:
> Ich nutze awk (genauer mawk) für sowas, ist ähnlich. Einmal in
> "Regulare Expressions" einarbeiten und schon hat man einen Textfresser
> mit dem man gerade für C wirklich tolle Sachen machen kann.

'Textfresser' wie awk will der Walter aber offensichtlich nicht.

Jester schrieb:
> just my 2ct

So - aber nun bin ich wirklich raus!

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.