7 Stimmen

Wie lassen sich die Auswirkungen von implementierungsabhängigen Sprachmerkmalen in C++ begrenzen?

Der folgende Text ist ein Auszug aus dem Buch von Bjarne Stroustrup, The C++ Programming Language:

Abschnitt 4.6:

Einige Aspekte der grundlegenden Typen von C++, wie z. B. die Größe eines int, sind durch die Implementierung definiert (Abschnitt C.2). Ich weise auf diese Abhängigkeiten hin und empfehle oft, sie zu vermeiden oder Schritte zu unternehmen, um ihre Auswirkungen zu minimieren. Warum sollten Sie sich die Mühe machen? Menschen, die auf einer Vielzahl von Systemen programmieren oder eine Vielzahl von Compilern verwenden, sind sehr daran interessiert, weil sie sonst gezwungen sind, Zeit damit zu verschwenden, obskure Fehler zu finden und zu beheben. Diejenigen, die behaupten, dass sie sich nicht um die Portabilität kümmern, tun dies in der Regel, weil sie nur ein einziges System verwenden und glauben, dass sie sich die Einstellung leisten können, dass "die Sprache das ist, was mein Compiler implementiert". Dies ist eine enge und kurzsichtige Sichtweise. Wenn Ihr Programm ein Erfolg ist, wird es wahrscheinlich portiert werden, so dass jemand Probleme im Zusammenhang mit implementierungsabhängigen Funktionen finden und beheben muss. Außerdem müssen Programme oft mit anderen Compilern für dasselbe System kompiliert werden, und selbst eine zukünftige Version Ihres Lieblingscompilers könnte einige Dinge anders machen als der aktuelle. Es ist viel einfacher, die Auswirkungen von Implementierungsabhängigkeiten zu kennen und zu begrenzen, wenn ein Programm geschrieben wird, als zu versuchen, das Durcheinander hinterher zu entwirren.

Es ist relativ einfach, die Auswirkungen von implementierungsabhängigen Sprachmerkmalen zu begrenzen.

Meine Frage lautet: Wie lassen sich die Auswirkungen von implementierungsabhängigen Sprachmerkmalen begrenzen? Bitte erwähnen Sie implementierungsabhängige Sprachmerkmale und zeigen Sie dann, wie man ihre Auswirkungen begrenzen kann.

4voto

workmad3 Punkte 24502

Nun, die erwähnten Variablengrößen sind ein ziemlich bekanntes Problem, das üblicherweise dadurch umgangen wird, dass man typisierte Versionen der Grundtypen bereitstellt, die genau definierte Größen haben (die normalerweise im Typedef-Namen angegeben sind). Dies geschieht mit Hilfe von Präprozessormakros, um den Code auf verschiedenen Plattformen unterschiedlich sichtbar zu machen. Z.B.:

#ifdef __WIN32__
typedef int int32;
typedef char char8;
//etc
#endif
#ifdef __MACOSX__
//different typedefs to produce same results
#endif

Andere Probleme werden in der Regel auf die gleiche Weise gelöst (z.B. durch die Verwendung von Präprozessor-Tokens zur Durchführung einer bedingten Kompilierung)

4voto

Anonymous Punkte 17529

Ein paar Ideen:

  • Leider müssen Sie Makros verwenden, um einige plattformspezifische oder compilerspezifische Probleme zu vermeiden. Sie können sich die Header der Boost-Bibliotheken ansehen, um zu sehen, dass es sehr leicht umständlich werden kann, schauen Sie sich zum Beispiel die Dateien an:

  • Die Integer-Typen neigen dazu, auf verschiedenen Plattformen unübersichtlich zu sein, Sie müssen Ihre eigenen Typendefinitionen definieren oder etwas wie Boost cstdint.hpp

  • Wenn Sie sich für eine Bibliothek entscheiden, prüfen Sie, ob diese auf der gegebenen Plattform unterstützt wird

  • Verwenden Sie die Bibliotheken mit guter Unterstützung und klar dokumentierter Plattformunterstützung (zum Beispiel Boost)

  • Man kann von einigen C++-Implementierungsproblemen abstrahieren, indem man sich stark auf Bibliotheken wie Qt verlässt, die eine "Alternative" im Sinne von Typen und Algorithmen bieten. Sie versuchen auch, die Kodierung in C++ portabler zu machen. Funktioniert das? Ich bin mir nicht sicher.

  • Nicht alles lässt sich mit Makros erledigen. Ihr Build-System muss in der Lage sein, die Plattform und das Vorhandensein bestimmter Bibliotheken zu erkennen. Viele würden vorschlagen autotools für die Projektkonfiguration, ich hingegen empfehle CMake (eher nette Sprache, nicht mehr M4 )

  • Endianness und Alignment könnten ein Problem sein, wenn Sie sich auf niedriger Ebene einmischen (d.h. reinterpret_cast y Freunde Dinge gleich (Freunde war ein böses Wort im C++-Kontext)).

  • eine Menge Warnflags für den Compiler einbauen, für gcc würde ich zumindest empfehlen -Wall -Wextra . Aber es gibt noch viel mehr, siehe die Dokumentation des Compilers oder diese Frage .

  • müssen Sie auf alles achten, was implementierungsdefiniert und implementierungsabhängig ist. Wenn Sie die Wahrheit, nur die Wahrheit und nichts als die Wahrheit wollen, dann gehen Sie zur ISO-Norm.

3voto

jmucchiello Punkte 18129

Die offensichtlichste Abhängigkeit von der Implementierung ist die Größe der Integer-Typen. Es gibt viele Möglichkeiten, dies zu handhaben. Der naheliegendste Weg ist die Verwendung von Typedefs, um Integer-Typen in verschiedenen Größen zu erstellen:

 typedef signed   short  int16_t;
 typedef unsigned short  uint16_t;

Der Trick dabei ist, sich für eine Konvention zu entscheiden und sich an diese zu halten. Die Wahl der Konvention ist der schwierige Teil: INT16, int16, int16_t, t_int16, Int16, usw. C99 hat die Datei stdint.h, die den int16_t-Stil verwendet. Wenn Ihr Compiler diese Datei hat, verwenden Sie sie.

Ebenso sollten Sie bei der Verwendung anderer Standarddefinitionen wie size_t, time_t usw. pedantisch sein.

Der andere Trick besteht darin zu wissen, wann man diese typedef nicht verwenden sollte. Eine Schleifensteuerungsvariable, die zum Indizieren eines Arrays verwendet wird, sollte nur rohe int-Typen annehmen, damit die Kompilierung den besten Code für Ihren Prozessor erzeugt. for (int32_t i = 0; i < x; ++i) könnte auf einem 64-Bit-Prozessor eine Menge unnötigen Code erzeugen, genau wie die Verwendung von int16_t auf einem 32-Bit-Prozessor.

2voto

Arafangion Punkte 10934

Eine gute Lösung ist die Verwendung gemeinsamer Überschriften, die bei Bedarf typedeff'ed Typen definieren.

Das Einbinden von sys/types.h ist zum Beispiel ein ausgezeichneter Weg, um damit umzugehen, ebenso wie die Verwendung portabler Bibliotheken.

2voto

Suma Punkte 31551

Hierfür gibt es zwei Ansätze:

  • eigene Typen mit bekannter Größe definieren und diese anstelle von eingebauten Typen verwenden (wie typedef int int32 #if-ed für verschiedene Plattformen)
  • Techniken zu verwenden, die nicht von der Schriftgröße abhängen

Die erste Variante ist sehr beliebt, aber die zweite führt, wenn möglich, zu einem saubereren Code. Dies schließt ein:

  • nicht davon ausgehen, dass Zeiger in int umgewandelt werden können
  • gehen Sie nicht davon aus, dass Sie die Bytegröße einzelner Typen kennen, verwenden Sie immer sizeof, um dies zu überprüfen
  • beim Speichern von Daten in Dateien oder bei der Übertragung über das Netz Techniken verwenden, die bei wechselnden Datengrößen übertragbar sind (wie das Speichern/Laden von Textdateien)

Ein aktuelles Beispiel hierfür ist das Schreiben von Code, der sowohl für x86- als auch für x64-Plattformen kompiliert werden kann. Der gefährliche Teil hier ist Zeiger und size_t Größe - bereit sein, es kann 4 oder 8 je nach Plattform sein, wenn Casting oder Differenzierung Zeiger, cast nie zu int, verwenden intptr_t und ähnliche typisierte Typen statt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X