operator new



  • Hey,

    ist es erlaubt in einer globalen operator new Überladung eine Ausgabe über die Streams zu machen? Mein VS 2010 Kompilat steigt im Releasemodus nämlich mit Speicherzugriffsverletzungen aus, und das wäre schon ein ziemlich übler Bug.



  • ich vermute mal, dass die streams Speicher mit new holen und dann hast Du einen Zyklus. Step doch mal mit dem Debugger durch.



  • weissnichtgenau schrieb:

    ich vermute mal, dass die streams Speicher mit new holen und dann hast Du einen Zyklus. Step doch mal mit dem Debugger durch.

    Ne. Das Problem scheint zu sein, dass cout noch gar nicht initialisiert ist. Ich kann leider nicht sehen was vorher passiert, weil das alles vor main passiert. Allerdings erhält cout erst nach der 4. Anforderung vernünftige Werte. (Wenn man bis dahin wartet, erhält man wohl irgendeine zyklische Abhängigkeit.)

    Wobei halt mal interessant wäre, was der Standard dazu sagt.



  • Der Standard sagt, dass die Initialisierungsreihenfolge globaler Objekte in unterschiedlichen ÜE undefiniert ist. D.h. Du kannst nicht davon ausgehen, dass cin/cout/cerr vor main schon benutzbar sind.



  • LordJaxom schrieb:

    Der Standard sagt, dass die Initialisierungsreihenfolge globaler Objekte in unterschiedlichen ÜE undefiniert ist. D.h. Du kannst nicht davon ausgehen, dass cin/cout/cerr vor main schon benutzbar sind.

    Die zyklische Abhängigkeit (die dann in einem Absturz endet) bekomme ich aber auch, wenn ich in der main eine globale Variable auf true setze. (Und erst dann die Ausgabe mache.)

    Edit:
    Auf IDEONE.com funktioniert das übrigens wunderbar, auch ohne "main"-Test.



  • LordJaxom schrieb:

    Der Standard sagt, dass die Initialisierungsreihenfolge globaler Objekte in unterschiedlichen ÜE undefiniert ist. D.h. Du kannst nicht davon ausgehen, dass cin/cout/cerr vor main schon benutzbar sind.

    Normalerweise stimmt das, für die Standardstreams gibt es aber spezielle Anforderungen in 27.4.1 (C++11).

    The objects are constructed and the associations are established at some time prior to or during the first time an object of class ios_base::Init is constructed, and in any case before the body of main begins execution. 293 The objects are not destroyed during program execution.294 The results of including <iostream> in a translation unit shall be as if <iostream> defined an instance of ios_base::Init with static storage duration. Similarly, the entire program shall behave as if there were at least one instance of ios_base::Init with static storage duration.

    Fußnoten:

    1. If it is possible for them to do so, implementations are encouraged to initialize the objects earlier than required.
    2. Constructors and destructors for static objects can access these objects to read input from stdin or write output to stdout
      or stderr.


  • solltest Du den gcc benutzen gibt es sicherlich doku die beschreibt, was in den einzelnen _init Schritten (IIRC init1 bis init9) geschieht. Ich kenne das nur von C auf dem AVR, dort ist das aber sehr gut beschrieben.



  • Vielleicht allokiert cout vor der Ausgabe einen Buffer, was zu einer erneuten Ausgabe und damit auch Allokierung führt, die wiederum zu einer Ausgabe inkl. Allokierung führt ... und schon wieder ein Zyklus.

    Das ist halt typisch an C++; man weiss nie was passiert. Nimm lieber fprintf, das dürfte sicher gehen.



  • @Bashar: Die entsprechende Stelle in C++03 ist 27.3 und enthält den Passus in der Form nicht. Ich vermute, dass MSVC 2010 diesen Teil von C++11 schlicht noch nicht implementiert.

    Wenn ich das alles richtig lese, sollte sich das Problem aber durch das Einfügen von

    namespace {
      std::ios_base::Init const make_sure_cout_and_stuff_exists;
    }
    

    an geeigneter Stelle beheben lassen, oder? Denkbar auch eine funktionslokale statische Konstante, aber wenn der operator new dann nicht benutzt wird, bevor Threads gestartet werden, hat man unter Umständen ein data race.



  • seldon schrieb:

    @Bashar: Die entsprechende Stelle in C++03 ist 27.3 und enthält den Passus in der Form nicht.

    Aus C++98:

    The objects are constructed, and the associations are established at some time prior to or during first time an object of class basic_ios<charT, traints>::Init is constructed, and in any case before the body of main begins execution. The objects are not destroyed during program execution.

    Also ist der Unterschied wohl, dass Einbinden von <iostream> kein Init-Objekt erzeugen muss.

    Ich vermute, dass MSVC 2010 diesen Teil von C++11 schlicht noch nicht implementiert.

    Naja, was im Standard steht und was die Realität ist muss nicht übereinstimmen, Abweichungen kann es dabei in beide Richtungen geben. Manche Sachen sind Konsens und funktionieren in allen Implementierungen, stehen aber nicht so im Standard. Ein Beispiel dafür ist das berüchtigte Hello-World, das nur <iostream> einbindet, während laut Standard noch <ostream> benötigt wird.

    Man hat sich bei der Entwicklung der IOStream-Library sehr viele Gedanken über die Initialisierungsreihenfolge der Standard-Streams gemacht. Ich finde es schwer vorstellbar, dass das an MSVC völlig vorbeigegangen sein soll. Irgendeine Lösung werden sie dafür gefunden haben.

    MSDN schreibt z.B.:

    All the objects declared in this header share a peculiar property — you can assume they are constructed before any static objects you define, in a translation unit that includes <iostream>. Equally, you can assume that these objects are not destroyed before the destructors for any such static objects you define. (The output streams are, however, flushed during program termination.) Therefore, you can safely read from or write to the standard streams before program startup and after program termination.

    This guarantee is not universal, however. A static constructor may call a function in another translation unit. The called function cannot assume that the objects declared in this header have been constructed, given the uncertain order in which translation units participate in static construction. To use these objects in such a context, you must first construct an object of class ios_base::Init.

    Wenn ich das alles richtig lese, sollte sich das Problem aber durch das Einfügen von

    namespace {
      std::ios_base::Init const make_sure_cout_and_stuff_exists;
    }
    

    an geeigneter Stelle beheben lassen, oder?

    So lese ich das auch.



  • bluffer schrieb:

    Das ist halt typisch an C++; man weiss nie was passiert. Nimm lieber fprintf, das dürfte sicher gehen.

    Die printf Familie hat genau das gleiche Problem, nur dass sie etwa eine halbe Sekunde länger durchhält. 😉



  • seldon schrieb:

    namespace {
      std::ios_base::Init const make_sure_cout_and_stuff_exists;
    }
    

    an geeigneter Stelle beheben lassen, oder?

    Ändern tut es leider nichts. 😞



  • Wie sieht denn dein Code aus?



  • Michael E. schrieb:

    Wie sieht denn dein Code aus?

    Äh.. so?
    http://ideone.com/YYXn6

    .. (Und ja, es fehlt nothrow und ich habe zu viele Autos. ;))



  • Den Fehler kann ich mit VS 2010 Release Build nicht reproduzieren.



  • Michael E. schrieb:

    Den Fehler kann ich mit VS 2010 Release Build nicht reproduzieren.

    Interessant. Ich habe das mal bei einem anderen Projekt getestet, und da lief es sofort. Habe also mit den Einstellungen rumgespielt, und ich denke den Fehler gefunden zu haben. Teste mal mit /MT statt /MD.



  • Jo, dann krachts bei mir auch.



  • Michael E. schrieb:

    Jo, dann krachts bei mir auch.

    Gut. Also wenn das tatsächlich standardkonform sein sollte, halte ich das für einen ziemlich üblen Bug. Weiß jemand ob das schon bekannt ist oder so?


Log in to reply