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
externchar*foo;
2
externchar*bar;
msg.c
1
constchar*foo="Hallo Welt!";
2
constchar*bar="Hello World!";
main.c
1
#include"msg.h"
----------------
Möglich wäre prinzipiell auch in C++:
msg.hpp
1
#include<string>
2
conststringfoo="Hallo Welt!";
3
conststringbar="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ß
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.
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
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ß
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ß
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
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.
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ß
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
externconststd::stringfoo;
6
externconststd::stringbar;
7
8
#endif
msg.cpp
1
#include"msg.h"
2
3
conststd::stringfoo="Hallo Welt!";
4
conststd::stringbar="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
>> 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?
WennBuild 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... %-}
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."
Programmiersprachentheaterintendant schrieb:> WennBuild 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.
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?
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ß
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.
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ß
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).
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.
> 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?
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.
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.
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.
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