Vorschlag für den nächsten C++-Standard: Statische Konstruktoren und Destruktoren



  • Kellerautomat schrieb:

    Das sind doch alles nur hässliche Workarounds...

    Der statische Konstruktor wäre auch kein Kind von Schönheit. Erst recht nicht, wenn man das static initilization fiasko im Hinterkopf behält.

    Seien wir mal ehrlich: non-movable, non-copyable Objekte sind schwiergig. Gloabel Variablen sind grundsätzlich unschön. Beides zusammen wird dann allmählich eklig, ist aber gottseidank selten. Das machen Workarounds in der Sprache auch nicht schöner.

    Davon abgesehen wage ich zu behaupten, dass die call_once Funktionalität genau für solche Zwecke geschaffen wurde - und Dinge wie Threadsicherheit gleich mit verarztet.



  • pumuckl schrieb:

    Der statische Konstruktor wäre auch kein Kind von Schönheit. Erst recht nicht, wenn man das static initilization fiasko im Hinterkopf behält.

    Ich habe nicht so Recht verstanden, was du meinst, aber es lässt sich bestimmt zufriedenstellend lösen.

    pumuckl schrieb:

    Gloabel Variablen sind grundsätzlich unschön.

    Das mag schon sein, aber wenn ich sie brauche, dann ist das nunmal so. Ich sehe auch nicht, warum einem die Sprache in diesem Fall nicht helfen sollte.



  • Kellerautomat schrieb:

    pumuckl schrieb:

    Gloabel Variablen sind grundsätzlich unschön.

    Das mag schon sein, aber wenn ich sie brauche, dann ist das nunmal so.

    Aber das Design lässt sich immer so umsetzen, dass man sie nicht braucht. Es gibt wirklich nur sehr wenige Fälle ( std::cin, std::cout, std::cerr , Qts qApp , usw.) wo es wirklich Sinn macht, eine Variable global zu deklarieren.



  • Komm du mir ja nicht mit Design - du hast keine Ahnung.



  • Aber das ganze gibt es doch schon, es nennt sich globale Variable:

    namespace {
       struct staticCtor {
          staticCtor() {
             WSAStartup();
          }
          ~staticCtor() {
             WSACleanup();
          }
       } o;
    }
    

    Das ganze scheitert einfach nur daran, dass es keine definierte Initialisierungsreihenfolge. Das Problem löst sich mit einem static Ctor auch nicht. Aber die funktionalität ist da.

    Wenn es ein Modulkonzept gibt, dann lässt sich darüber reden wie man sowas implementiert. Aber für das aktuelle ÜE-Konzept von C++ bringt es nichts.



  • Eine Initialisierungsreihenfolge lässt sich im Übrigen auch festlegen - daran solls nicht scheitern.



  • Kellerautomat schrieb:

    Lösung mit statischem Konstruktor:

    std::ofstream of;
     
    std::ostream& get_stream() { return of; }
     
    static : of(filename)
    {
    	set_time_formatting(of, "%Y/%m/%d %H:%M:%S.%f");
    }
    

    Viel kürzer, übersichtlicher, und drückt meine Intention direkt aus.

    Hmm ich weiß nicht, ob man "globale Objekte" jetzt wirklich besser unterstützen sollte. Gibt ja genug Probleme mit der Initialisierungsreihenfolge. Wie wär es damit:

    std::ostream& get_stream()
    {
      static std::ostream& os_ref = []()->std::ostream&
      {
        static std::ostream os (filename);
        set_time_formatting(of, "%Y/%m/%d %H:%M:%S.%f");
        return os;
      }();
      return os_ref;
    }
    

    ?

    (beide static s sind hier wichtig)



  • Kellerautomat schrieb:

    Eine Initialisierungsreihenfolge lässt sich im Übrigen auch festlegen - daran solls nicht scheitern.

    Wie willst du die denn festlegen? Mach mal einen Vorschlag.



  • Genau wie bei Klassenmembern: Definitionsreihenfolge.



  • Kellerautomat schrieb:

    Genau wie bei Klassenmembern: Definitionsreihenfolge.

    Ah, dann hast du an etwas anderes gedacht, nämlich an die Definitionsreihenfolge innerhalb einer Übersetzungseinheit. Die ist ja, soweit ich weiß, auch schon festgelegt. Aber es ist nicht festgelegt, in welcher Reihenfolge die Objekte unterschiedlicher Übersetzungseinheiten dran kommen. Siehe static initialization order fiasco. Deswegen sind "static function locals" mit ihrer "lazy initialization" da viel interessanter.



  • Eine Initialisierungsreihenfolge zwischen UE's festzulegen ergibt überhaupt keinen Sinn. UE's sind schließlich unabhängig voneinander.



  • Heißt wenn du get_stream in einem anderen "statischen globalen Konstruktor" aufrufst, ist der Stream u.U. noch gar nicht initialisiert.. (Bei allen anderen Methoden ist das kein Problem.)



  • Na und? Ist doch egal...



  • Kellerautomat schrieb:

    Na und? Ist doch egal...

    Bis auf dass dein Programm halt wegen einer Access Violation abschmiert.

    Aber bis auf diese Kleinigkeit ists egal, ja.



  • Ich verwende sie doch gar nicht vor main(), also wo liegt euer Problem? Gleich mal voreilig beschweren, obwohl man die Verwendung nicht kennt?



  • Kellerautomat schrieb:

    Ich verwende sie doch gar nicht vor main(), also wo liegt euer Problem? Gleich mal voreilig beschweren, obwohl man die Verwendung nicht kennt?

    Es ging um Übersetzungseinheiten und Initialisierungsreihenfolge. Und nicht ob jetzt ein konkreter Code funktioniert - deine Idee ist Broken By Design. Das ist der Punkt.



  • Tjoah, UB ist eben UB. Ein guter Compiler warnt vor sowas.



  • Kellerautomat schrieb:

    Tjoah, UB ist eben UB. Ein guter Compiler warnt vor sowas.

    Vor manchen kann auch ein Compiler nicht warnen.

    Ganz davon abgesehen können die Auswirkungen recht langwierige Suchen erfordern (Wir hatten in einem Projekt einen solchen Fall, den Fehler haben wir erst Wochen später entdeckt).



  • Das Problem gabs aber auch schon vorher und ist auch nicht das, welches ich durch dieses Feature lösen möchte.



  • Kellerautomat schrieb:

    Eine Initialisierungsreihenfolge zwischen UE's festzulegen ergibt überhaupt keinen Sinn. UE's sind schließlich unabhängig voneinander.

    "UEs sind unabhängig voneinander" ist die rosarote Theorie. Die weniger rosarote Praxis sieht anders aus.
    Daher würde es schon Sinn ergeben.

    Bloss dass es nicht unbedingt trivial umzusätzen wäre.


Anmelden zum Antworten