Visual C++ 6 mit Freud und Leid

Als ich anfangs der 2000er mit VB6 meine ersten “richtigen” Programme anfing, war stets das gesamte Visual Studio auch mit C++ installiert.

Gemacht habe ich damit aber nicht viel … schließlich verstand ich dieses “seltsame C++” sowieso nicht und VB war deshalb “viel besser”.

Heute bekommt dieser “seltsamste” aller C++ Compiler einen besonderen Platz bei mir eingeräumt. Und das GATE Projekt nimmt ihn offiziell in seine Supportliste auf.


Das Studio 98 kam zu einer sehr blöden Zeit auf den Markt. C++ wurde 1998 standardisiert und erklärte viele bis dahin gebräuchlichen Verhaltensweisen als “falsch”. Microsoft C++ 6 war Teil von Studio 98 und wurde damit vor der Standardisierung entwickelt, war damit Nicht-Standard-Konform aber wurde dennoch bis 2002 als primärer Windows-Compiler eingesetzt und formal erst im Jahr 2010 endgültig abgelöst.
(Wer erinnert sich noch an die Meldung “10 is the new 6”, als MSVC10 rauskam?)

Das ist natürlich gefundenes Fressen für alle Windows-Kritiker, die somit zu Recht behaupten konnten, dass Windows auf extrem wackeligen Beinen steht.

Die Workarounds in BOOST und anderen Bibliotheken für diesen Sonder-Compiler sind schon fast legendär, so “pervers” waren sie geschrieben.

Typische VC6 Problemchen

Über folgende Probleme bin ich schon mehrmals gestolpert:

  • new wirft im Fehlerfall keine Exception sondern gibt einen NULL-Pointer zurück. Das kann man sogar durch einen angepassten operator new fixen, aber hier streiten sich dann gerne die Libs, wer den Operator zuerst überladen darf.
  • template Funktionen mit void-Return Value erzeugen Fehler.
    Folgender Code kompiliert nicht:
     1template<class R> R foo()
     2{
     3  return R();
     4}
     5  
     6int main()
     7{
     8  foo<void>();
     9}
    

    Nun gut, man könnte meinen, dass es auch wenig Sinn macht einen “void” zurückgeben, aber spätestens seit dem C++03 Update und in MSVC 2003 wird dieser Code gültig. Man braucht ihn vor allem für Delegates oder andere Formen von Methoden- oder Funktions-Dispatchern.

  • Variadic Macros existieren nicht.
    Ärgerlich, weil manche Debugging-Macros ähnlich wie printf mehrere Argumente so aneinanderreihen.
  • Makros in Makros brauchen teils weitere Makros um zu funktionieren.
    Ein Makro kann nicht ein anderes Parameter-freies Makro einbauen, nein, man braucht ein drittes Makro, dem man das gewollte Makro als Parameter übergibt, welches das Makro selbst wieder zurückgibt (bzw. dessen Auswertung).
  • char ist entweder zu signed char oder zu unsigned char identisch und kein eigener Typ. Tatsächlich sind bei template Instanzen die Typen char, signed char und unsigend char insgesamt 3 unterschiedliche Typen. Nur nicht in VC6. Da sind char und signed char identisch, außer man setzt eine Compileroption, die char einem unsigned char gleichsetzt.
  • Zwei Kopierkonstruktoren mit konstantem und nicht-konstantem Argument führen zu Fehlern. Zugegeben, das ist in C++ generell seltsam, dass man die Kopierkonstruktoren
    1type(type const& src);
    2type(type& src);
    

    definieren kann. Welcher genommen wird, ist oft schwer nachvollziehbar, weil sich der konstante auch um die nicht-konstanten Aufrufe kümmern darf. VC6 erfindet aber bei einem type(type& src); ohne den anderen einen selbstgestrickten memcpy basiereten type(type const& src); dazu, und dann ist die Kacke meist am Dampfen.

  • for Schleifen dürfen in C keine Typendeklarationen einführen.
    Ein for(int i = 0; ...) ist falsch, int i; muss vor der Schleife stehen.
  • long long existiert nicht, anstatt dessen haben wir __int64
    Das bereitet oft die meisten Schmerzen und eine Vielzahl an Libs beinhaltet deshalb Codes wie:
    1#if defined(_MSC_VER)
    2  typedef unsigned __int64        my_ui64_t;
    3  typedef signed __int64                my_i64_t;
    4#else
    5  typedef unsigned long long        my_ui64_t;
    6  typedef signed long long        my_i64_t;
    7#endif
    

… und sicher gibt es noch viel mehr, das mich entweder nicht betroffen haben, oder was ich schon verdrängt habe.

Fazit

Aus C++ Sicht ist der VC6 heute immernoch der größte Alptraum aller Zeiten, und das liegt an vielen Codes, die damals entstanden sind und unter neuen Compilern Probleme bereiteten.
Deshalb wird er so gut wir gar nicht mehr offiziell unterstützt.

Da das Projekt aber auf dem älteren C89 Standard aufsetzt und C++ nur einen dünnen Layer darüber darstellt, möchte ich dennoch versuchen diesen Dinosaurier am Leben zu erhalten.
Wenn ich einmal meine heutigen Codes in die Vergangenheit schicken kann, sind diese vermutlich die einzigen, die damals kompilierbar gewesen wären. Und irgendwie reizt mich dieser Gedanke.

Wenn man also ein paar Makros und typedefs einsetzt und brav alle Typen am Beginn der Funktion deklariert, kann man dynamisch eingebundene Windows 10 - Features unter einem Windows 98 mit MSVC6 kompilieren und am Ende in der Azur Cloud ausführen … Na, das nenn’ ich mal eine Challenge!


Wenn sich eine triviale Erkenntnis mit Dummheit in der Interpretation paart, dann gibt es in der Regel Kollateralschäden in der Anwendung.
frei zitiert nach A. Van der Bellen
... also dann paaren wir mal eine komplexe Erkenntnis mit Klugheit in der Interpretation!