Forum: PC-Programmierung String-Konstanten in C++


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Dennis S. (eltio)


Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,
Ziel ist es String-Konstanten in einem C++-Programm an einer zentralen 
Stelle auszulagern.

----------------

Beispiel: In C würde ich (mit char) MISRA-konform so prinzipiell 
folgendes machen:

msg.h
1
extern char* foo;
2
extern char* bar;

msg.c
1
const char* foo = "Hallo Welt!";
2
const char* bar = "Hello World!";

main.c
1
#include "msg.h"

----------------

Möglich wäre prinzipiell auch in C++:

msg.hpp
1
#include <string>
2
const string foo = "Hallo Welt!";
3
const string bar = "Hello World!";

main.cpp
1
#include "msg.hpp"

Mir gefällt es nicht Header in Header zu inkludieren. Außerdem sollte ja 
vermutlich keine Definition im Header sein.

Was ist "Best-Practice"?

Gruß

von Dirk B. (dirkb2)


Bewertung
4 lesenswert
nicht lesenswert
Dennis S. schrieb:
> Mir gefällt es nicht Header in Header zu inkludieren.

Warum nicht?
Im Header muss alles enthalten sein, damit du ihn benutzen kannst.
Wenn das schon in einem anderen Header steht, musst du ihn einbinden.
Dafür hat sich irgendwann mal jemand die Include-Guards ausgedacht.

von Dr. Sommer (Gast)


Bewertung
3 lesenswert
nicht lesenswert
Dennis S. schrieb:
> Mir gefällt es nicht Header in Header zu inkludieren

Dann solltest du kein C oder C++ benutzen, da macht man das nämlich 
ständig weil es ohne kaum geht.

In C++ geht es genau wie in C, schreib "extern const std::string foo;" 
in den Header, und 'const std::string foo ("asdf");' in die Source 
Datei.

Sauberer wäre es natürlich wenn die Konstante nicht global ist. Warum 
brauchst du die an so vielen Stellen?

Und wenn du schon pingelig bist (MISRA), schreibe nicht "using namespace 
std;" sondern schreibe das std:: aus. Siehe:

https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice

von Dennis S. (eltio)


Bewertung
-3 lesenswert
nicht lesenswert
Danke für eure Antworten.

Dirk B. schrieb:
> Warum nicht?
Eine nicht näher spezifizierbare Ahnung im Hinterkopf, gelesen zu haben, 
genau dies zu vermeiden.

Dr. Sommer schrieb:
> Dann solltest du kein C oder C++ benutzen, da macht man das nämlich
> ständig weil es ohne kaum geht.
Naja.. Gründe dagegen gibt es ja (beispielsweise längere Compilierzeit). 
Nun gilt es das kaum aus deiner Antwort genauer zu beleuchten.

> Sauberer wäre es natürlich wenn die Konstante nicht global ist. Warum
> brauchst du die an so vielen Stellen?
Ich brauche die nicht "an so vielen Stellen", sondern möchte sie aus 
Gründen der Übersichtlichkeit und Wartbarkeit (z.B.: Übersetzungen), an 
einer Stelle haben. Auch wenn hier nicht wichtig: es handelt sich um 
Ausgabemeldungen für den User.

> Und wenn du schon pingelig bist (MISRA), schreibe nicht "using namespace
> std;" sondern schreibe das std:: aus. Siehe:
Das ist jetzt aber sehr am Thema vorbei wenn du ehrlich bist...

Gruß

von Dennis S. (eltio)


Bewertung
0 lesenswert
nicht lesenswert
Dirk B. schrieb:
> Im Header muss alles enthalten sein, damit du ihn benutzen kannst.

Habe ich jetzt an einer anderen Stelle auch so gelesen.. scheint also 
nicht ganz fern der Praxis zu sein. ;-)

Gruß

von Dirk B. (dirkb2)


Bewertung
1 lesenswert
nicht lesenswert
Dennis S. schrieb:
> Eine nicht näher spezifizierbare Ahnung im Hinterkopf, gelesen zu haben,
> genau dies zu vermeiden.

Du sollst nicht blind alle möglichen Header auf Vorrat einbinden, 
sondern nur die benötigten.

Dennis S. schrieb:
> Naja.. Gründe dagegen gibt es ja (beispielsweise längere Compilierzeit).

Die Header werden spätestens im Code gebraucht. Also müssen sie (früher 
oder später) eingebunden werden.
Es gibt auch noch
1
#pragma once

von Dr. Sommer (Gast)


Bewertung
2 lesenswert
nicht lesenswert
Dennis S. schrieb:
> Eine nicht näher spezifizierbare Ahnung im Hinterkopf, gelesen zu haben,
> genau dies zu vermeiden.

Keine gute Quelle ;-)

Dennis S. schrieb:
> Naja.. Gründe dagegen gibt es ja (beispielsweise längere Compilierzeit).
> Nun gilt es das kaum aus deiner Antwort genauer zu beleuchten.

In Headern keine Includes zu benutzen würde ja bedeuten, dass sich alle 
Programmstrukturen nur aus fundamentalen Typen zusammensetzen. Das würde 
eine ziemlich magere Struktur bewirken. Auf unnötige Inkludes sollte man 
aber schon verzichten. Die Compilier Zeit leider darunter auch nicht so 
dramatisch; Metaprogrammierung ist da z.B. viel schlimmer.

Dennis S. schrieb:
> Auch wenn hier nicht wichtig: es handelt sich um Ausgabemeldungen für
> den User.

Wie wär's, die in eine Klasse zu packen statt sie global zu machen?

Dennis S. schrieb:
> Das ist jetzt aber sehr am Thema vorbei wenn du ehrlich bist...

Da du offensichtlich Anfänger bist hatte ich vermutet, dass du dich über 
Hinweise auf weitere Probleme freuen würdest.

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
PS: wenn Compile-Zeit so wichtig ist nimm Java, das ist da viel 
schneller als C oder C++ und hat außerdem gar keine Header 
Problematik...

von Dennis S. (eltio)


Bewertung
1 lesenswert
nicht lesenswert
Dr. Sommer schrieb:
> Keine gute Quelle ;-)
Dem schließe ich mich an. :-D

Dr. Sommer schrieb:
> In Headern keine Includes zu benutzen würde ja bedeuten, dass sich alle
> Programmstrukturen nur aus fundamentalen Typen zusammensetzen.
Das ist eine sehr einleuchtende Begründung.. vielen Dank.

Dr. Sommer schrieb:
> Wie wär's, die in eine Klasse zu packen statt sie global zu machen?
Das wäre auch denkbar. Ich probiere beide Wege aus!

Dr. Sommer schrieb:
> Da du offensichtlich Anfänger bist hatte ich vermutet, dass du dich über
> Hinweise auf weitere Probleme freuen würdest.
Das ist natürlich so, entschuldige. Ich wollte nur (vielleicht etwas zu 
vehement) verhindern, dass der Thread, wie hier schon oft erlebt, völlig 
aus dem Ruder läuft.

Gruß

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Bewertung
1 lesenswert
nicht lesenswert
Dennis S. schrieb:
> Möglich wäre prinzipiell auch in C++:

Und auch in C++ ist es weithin möglich, String Literale zu verwenden. 
Wenn Du die Konstanten nicht als std::string brauchst, bzw. später nicht 
andauernd dorthin konvertieren musst, kannst Du Dir überlegen, auch 
weiterhin String Literale zu verwenden (insbsondere, wenn es eine 
embedded Anwendung ist).

Die Nutzung einer "zentralen" Stelle, mag irgend wie intuitiv sein, wird 
aber sehr schnell unübersichtlich, wenn ein Projekt größer wird.

Ansonsten:

msg.hpp
1
#ifndef MSG_H
2
#define MSG_H
3
#include <string>
4
5
extern const std::string foo;
6
extern const std::string bar;
7
8
#endif

msg.cpp
1
#include "msg.h"
2
3
const std::string foo = "Hallo Welt!";
4
const std::string bar = "Hello World!";

> Was ist "Best-Practice"?

- Symbole so local wie möglich zu halten. Evtl. auch darauf verzichten, 
für die Konstante "Es ist gerade ein Fehler aufgetreten, möchten Sie die 
Foobar wiederholen?" ein eigenes Symbol mit dem Namen ERROR_232 oder 
inform_the_user_about_an_error_and_ask_if_he_wants_to_retry_foobar 
anzulegen.
- Auf Lesbarkeit achten und dabei den Kopf einschalten.
- Niemals auf Regeln hören, in denen die Wörter "Niemals", "immer" oder 
"sauber" vorkommen!

mfg Torsten

von Programmiersprachentheaterintendant (Gast)


Bewertung
-3 lesenswert
nicht lesenswert
>> Naja.. Gründe dagegen gibt es ja (beispielsweise längere Compilierzeit).

Aha. Da nimmt man Compilers welche in jeder Versionsiteration den 
Quellcode mehr und besser auf NoNos abklopfen können, gönnt ihnen aber 
das Mehr an Zeit dies zu tun nicht? Auf immer schneller werdenden 
Entwicklungsworkstations?

Wenn Build Time zum Zeitfresser wird, ist es höchste Zeit sich die 
Makefiles (oder was stattdessen eingesetzt wird) vorzuknöpfen und die 
Abhängigkeitsregeln an den zu übersetzenden Quellcode 
anzupassen/richtigzustellen. Auch die im eigenen Quellcode gewählte 
Verteilung der Dinge ist da oft ein Problem.
BTDT und Faktor 5++ rausgeholt: plötzlich greift nämlich die 
Parallelisierbarkeit make -j


> - Niemals auf Regeln hören, in denen die Wörter "Niemals", "immer" oder "sauber" 
vorkommen!

Schau an: eine solche Regel...   %-}

von Alles Humbug (Gast)


Bewertung
-3 lesenswert
nicht lesenswert
Torsten R. schrieb:
> - Niemals auf Regeln hören, in denen die Wörter "Niemals", "immer" oder
> "sauber" vorkommen!

Also auf sowas hier schon mal nicht hören:

"Dr. Sommer (Gast)
Sauberer wäre es natürlich wenn die Konstante nicht global ist."

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Bewertung
1 lesenswert
nicht lesenswert
Programmiersprachentheaterintendant schrieb:

> Wenn Build Time zum Zeitfresser wird, ist es höchste Zeit sich die
> Makefiles (oder was stattdessen eingesetzt wird) vorzuknöpfen und die
> Abhängigkeitsregeln an den zu übersetzenden Quellcode
> anzupassen/richtigzustellen.

Die allermeisten unter C++ verwendeten Build-System sollten diese 
Abhängigkeiten für das Übersetzen aus dem Quellcode selbst ermitteln 
können.

Den Tipp, auf überflüssige includes zu versichten, ist aber sicher 
korrekt und hilfreich. Die Verwendung von "zentrallen Stellen" führt in 
den meisten Fällen eher zu headern, die letzendlich in jeder translation 
unit landen und damit den build verlangsamen.

von Vlad T. (vlad_tepesch)


Bewertung
0 lesenswert
nicht lesenswert
Dennis S. schrieb:
> Möglich wäre prinzipiell auch in C++:
>
> msg.hpp
> #include <string>
> const string foo = "Hallo Welt!";
> const string bar = "Hello World!";
>
> main.cpp
> #include "msg.hpp"
>

sollte dies nicht bei mehrfacher includierung doppelte Symbole im Linker 
geben?

: Bearbeitet durch User
von Dr. Sommer (Gast)


Bewertung
1 lesenswert
nicht lesenswert
Vlad T. schrieb:
> sollte dies nicht bei mehrfacher includierung doppelte Symbole im Linker
> geben?
Ja, es fehlt das "extern".

von Dennis S. (eltio)


Bewertung
0 lesenswert
nicht lesenswert
Vlad T. schrieb:
> sollte dies nicht bei mehrfacher includierung doppelte Symbole im Linker
> geben?
Doch nicht bei der Verwendung von Include-Guards?

Dr. Sommer schrieb:
> Ja, es fehlt das "extern".
Das würde nicht funktionieren, weil in der Header-Datei dann keine 
Definition mehr vorhanden wäre?!

Gruß

von Dr. Sommer (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Dennis S. schrieb:
> Doch nicht bei der Verwendung von Include-Guards?

Doch, die sind Wirkungslos wenn der selbe Header von mehreren 
.cpp-Dateien (Translation Units) inkludiert wird.

Dennis S. schrieb:
> Das würde nicht funktionieren, weil in der Header-Datei dann keine
> Definition mehr vorhanden wäre?!
Die Definition sollte man dann natürlich in die main.cpp packen. 
Definitionen von Variablen in Headern gehen nur, wenn der Header von 
genau einer .cpp-Datei inkludiert wird. Da das eher unpraktikabel ist, 
sollte man es lieber gar nicht tun.

von Dennis S. (eltio)


Bewertung
-1 lesenswert
nicht lesenswert
Programmiersprachentheaterintendant schrieb:
> Auf immer schneller werdenden Entwicklungsworkstations?
Tja.. ich warte noch auf den Rechner, der automatisch mit jeder 
Software-Version des Compilers schneller wird.. ;-)

Davon abgesehen: "Ignorier einfach potentielle Probleme (== binde 
einfach alle Header-Dateien ein) und lass ein neues Tool das machen (== 
nutze Make / CMake)" halte ich für eine kurzsichtige Heransgehensweise.

Gruß

von Mikro 7. (mikro77)


Bewertung
0 lesenswert
nicht lesenswert
Vlad T. schrieb:
>> #include <string>
>> const string foo = "Hallo Welt!";
>> const string bar = "Hello World!";
>
> sollte dies nicht bei mehrfacher includierung doppelte Symbole im Linker
> geben?

Hatte ich auch gedacht. Der Linker scheint allerdings die identischen 
Symbole zu identifizieren. Solange sie const sind, wird gelinkt. Sind 
sie nicht const, gibt es eine Fehlermeldung (gcc 5.4.0).

von mh (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Mikro 7. schrieb:
> Vlad T. schrieb:
>>> #include <string>
>>> const string foo = "Hallo Welt!";
>>> const string bar = "Hello World!";
>>
>> sollte dies nicht bei mehrfacher includierung doppelte Symbole im Linker
>> geben?
>
> Hatte ich auch gedacht. Der Linker scheint allerdings die identischen
> Symbole zu identifizieren. Solange sie const sind, wird gelinkt. Sind
> sie nicht const, gibt es eine Fehlermeldung (gcc 5.4.0).

Dazu den letzte Satz in folgendem Textausschnitt beachten:

aus http://en.cppreference.com/w/cpp/language/definition
1
One Definition Rule
2
3
Only one definition of any variable, function, class type, enumeration type, concept (since C++20) or template is allowed in any one translation unit (some of these may have multiple declarations, but only one definition is allowed).
4
5
One and only one definition of every non-inline function or variable that is odr-used (see below) is required to appear in the entire program (including any standard and user-defined libraries). The compiler is not required to diagnose this violation, but the behavior of the program that violates it is undefined.
6
...

In diesem konkreten Fall ist auch das odr-used zu beachten, da foo und 
bar in dem gezeigten Ausschnitt nicht benutzt werden.

von Mikro 7. (mikro77)


Bewertung
0 lesenswert
nicht lesenswert
mh schrieb:
>
1
> One and only one definition of every non-inline function or variable 
2
> that is odr-used (see below) is required to appear in the entire program 
3
> (including any standard and user-defined libraries). The compiler is not 
4
> required to diagnose this violation, but the behavior of the program 
5
> that violates it is undefined.
6
> ...
7
>
>
> In diesem konkreten Fall ist auch das odr-used zu beachten, da foo und
> bar in dem gezeigten Ausschnitt nicht benutzt werden.

Unter welchen Umständen könnte das o.g. const foo odr-used werden?

von mh (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Mikro 7. schrieb:
> Unter welchen Umständen könnte das o.g. const foo odr-used werden?

Der Link in meinem Post enthält auch eine Erklärung zu odr-used. Ich 
kann verstehen, wenn man den "Formally" Teil nicht versteht. Aber der 
Abschnitt fängt mit "Informally" an und ist ziemlich einfach:
1
ODR-use
2
Informally, an object is odr-used if its value is read (unless it is a compile time constant) or written, its address is taken, or a reference is bound to it; a reference is odr-used if ...

Also als konkrete, aber nicht vollständige, Antwort auf deine Frage: 
Wenn im Programm die Adresse von foo oder bar benutzt wird, oder eine 
Referenz auf eins der Objekte zeigt.

von Mikro 7. (mikro77)


Bewertung
0 lesenswert
nicht lesenswert
Wie oben geschrieben: gcc akzeptiert den std::string mehrfach (wenn er 
const ist). Konkret wird er im Executable bspw. zweimal instantiiert. 
Nämlich einmal für jeden der Zugriffe aus den unterschiedlichen 
Compilation Units.

Ich bin mir jetzt aber nicht ganz klar darüber was du mitteilen 
möchtest.

von tictactoe (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Mikro 7. schrieb:
> Wie oben geschrieben: gcc akzeptiert den std::string mehrfach (wenn er
> const ist). Konkret wird er im Executable bspw. zweimal instantiiert.
> Nämlich einmal für jeden der Zugriffe aus den unterschiedlichen
> Compilation Units.

Das muss so sein. Globale const-Variablen sind automatisch auch 
"internal linkage", d.h. als wären sie static deklariert. Dem kann man 
durch eine zusätzliche Deklaration mit extern entgegenwirken, wenn 
notwendig.

von Eric B. (beric)


Bewertung
0 lesenswert
nicht lesenswert
Torsten R. schrieb:
> - Symbole so local wie möglich zu halten. Evtl. auch darauf verzichten,
> für die Konstante "Es ist gerade ein Fehler aufgetreten, möchten Sie die
> Foobar wiederholen?" ein eigenes Symbol mit dem Namen ERROR_232 oder
> inform_the_user_about_an_error_and_ask_if_he_wants_to_retry_foobar
> anzulegen.

Grundsätzlich gebe ich dir Recht, dass man Symbole so lokal wie möglich 
halten soll, aber gerade bei Text-Konstanten macht es durchaus Sinn die 
alle zentral zu verwalten, damit es z.B. einfacher wird das Programm in 
verschiedene Sprachen zu übersetzen.
Dann würde ich aber die Texte in z.B. eine Excel Tabelle verwalten und 
mir daraus C/C++ Code generieren lassen.

> - Niemals auf Regeln hören, in denen die Wörter "Niemals", "immer" oder
> "sauber" vorkommen!

Immer doch! :-D

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.