Warum gibt's sowas eigentlich nicht als Feature in C++?



  • Nexus schrieb:

    Nächstes Mal bin ich vorsichtiger, wenn ich die Möglichkeiten von C++ aufzeigen will. Ob dieses Template-Gefrickel wirklich der richtige Weg ist, bleibt nämlich nach wie vor fragwürdig. Oder anders gesagt: Nur weil etwas möglich ist, ist es nicht sinnvoll.

    Im Normalfall ist es ein Anzeichen schlechten Designs, zuerst das Vorhandensein einer Funktionalität abzufragen und dann abhängig von ihr darauf zu reagieren. Zumindest im grösseren Rahmen sollten solche expliziten Fallunterscheidungen vermieden werden. Und zwar aus ähnlichen Gründen wie virtuelle Funktionen einer if-else -Kaskade vorzuziehen sind.

    Im konkreten Fall hier versuchst du etwas nachzubauen, das wxWidgets bewusst nicht anbietet. Es ist nämlich eine wesentliche Designentscheidung, wenn Klassen keine Defaultkonstruktoren besitzen. Diese deutet darauf hin, dass bei der Konstruktion von Objekten bereits genügend Informationen vorhanden sein müssen, um eine sinnvolle Initialisierung vorzunehmen. Mit deinem Ansatz arbeitest du an diesem Design vorbei und erzeugst Dummy-Objekte, die du nachträglich füllen musst. Warum? Sowas ist nie gut.

    😃
    Ich wollte eigentlich
    1.So eine Art Feeling wie beispielsweise bei Java (da hab ich zb SWT verwendet) zu ermöglichen. Mir ist schon klar, dass die so erstellten Objekte gar nicht angezeigt werden. Bei SWT kann man beispielsweise einfach das Objekt erstellen und danach die Parameter setzen, die man möchte. (ist das bei Qt nicht auch so?)
    2. Vektoren für Pointer erstellen, die ihre Member von selbst Initialisieren (und zwar gescheit, dass man damit arbeiten kann, und eben nicht nur Default-Initialisiert).



  • wxSkip schrieb:

    1.So eine Art Feeling wie beispielsweise bei Java (da hab ich zb SWT verwendet) zu ermöglichen. Mir ist schon klar, dass die so erstellten Objekte gar nicht angezeigt werden. Bei SWT kann man beispielsweise einfach das Objekt erstellen und danach die Parameter setzen, die man möchte. (ist das bei Qt nicht auch so?)

    Warum Objekte nicht sinnvoll initialisieren, sondern Parameter erst im Nachhinein setzen? Das öffnet lediglich Fehlerquellen und umgeht das Konzept der Konstruktoren. Dass andere Bibliotheken oder Sprachen sich diesbezüglich anders verhalten, ist doch kein Argument. Java vererbt auch an Orten, wo es keinen Sinn macht.

    wxSkip schrieb:

    2. Vektoren für Pointer erstellen, die ihre Member von selbst Initialisieren (und zwar gescheit, dass man damit arbeiten kann, und eben nicht nur Default-Initialisiert).

    myVector.push_back(/* (Zeiger auf) vollständig initialisiertes Objekt */)
    

    Ich seh das Problem nicht... Die gewünschte Selbst-Initialisierung liefert dir auch nur wieder ein Dummy-Objekt, das du noch bearbeiten musst.



  • Nexus schrieb:

    wxSkip schrieb:

    2. Vektoren für Pointer erstellen, die ihre Member von selbst Initialisieren (und zwar gescheit, dass man damit arbeiten kann, und eben nicht nur Default-Initialisiert).

    myVector.push_back(/* (Zeiger auf) vollständig initialisiertes Objekt */)
    

    Ich seh das Problem nicht... Die gewünschte Selbst-Initialisierung liefert dir auch nur wieder ein Dummy-Objekt, das du noch bearbeiten musst.

    myVector.resize(15);
    

    Ich versuche gerade, eine Art Compiler zu schreiben für eine Sprache, in der das auch ohne Konstruktoren gehen soll (nicht nach dem Motto "was musste ich da jetzt schon wieder und in welcher Reihenfolge angeben und warum?"). Da wäre es für mich eine Erleichterung, das direkt so nach C++ übertragen zu können.



  • wxSkip schrieb:

    myVector.resize(15);
    

    Ja, das ist mir schon klar. Ich kann auch nicht std::sort() auf Listen anwenden. Weil beide Funktionen eben Voraussetzungen haben. resize() erfordert einen Standardkonstruktor, sort() Random-Access-Iteratoren.

    Aber darum geht es sowieso nicht. Ich habe ja erklärt, warum dein Vorgehen Nachteile mit sich bringt und wieso es im Prinzip ein Rückschritt in C++ ist, wo Kapselung und damit auch Klasseninvarianten einen hohen Stellenwert haben.

    wxSkip schrieb:

    nicht nach dem Motto "was musste ich da jetzt schon wieder und in welcher Reihenfolge angeben und warum?

    Das ist natürlich ein guter Grund. Nur ist die Frage, ob er die Nachteile wert ist? Du kannst zum Beispiel auch keine temporären Objekte mehr an Funktionen übergeben.

    Bei einem eigenen Projekt ist es auch wieder was Anderes, dann könntest du so ein Design wenigstens konsistent durchziehen. Aber bei einer bestehenden Bibliothek würde ich mir solche Eingriffe gut überlegen, da sie wie schon erwähnt Designüberlegungen des Entwicklers ausser Kraft setzen. Es gibt ja auch andere Möglichkeiten wie das Named Constructor Idiom, das zumindest in die Richtung geht.

    Aber ich denke, generell bist du in C++ schlecht aufgehoben, wenn du Funktionsargumente beim Aufruf unbedingt benennen willst. Auch wenn es Möglichkeiten wie Boost.Parameter gibt, sind diese etwas zwiespältig.



  • Nexus schrieb:

    resize() erfordert einen Standardkonstruktor

    Ja, und jetzt eben nicht mehr.

    Nexus schrieb:

    Du kannst zum Beispiel auch keine temporären Objekte mehr an Funktionen übergeben.

    Sorry, das musst du mir nochmal erklären.

    Nexus schrieb:

    Aber ich denke, generell bist du in C++ schlecht aufgehoben, wenn du Funktionsargumente beim Aufruf unbedingt benennen willst.

    Will ich vielleicht in meiner eigenen Programmiersprache, aber in C++ nicht. Ich will nur Zeiger automatisch auf ein gültiges Objekt zeigen lassen.



  • wxSkip schrieb:

    Ja, und jetzt eben nicht mehr.

    Ja, aber auch nur, weil du am Design herumbiegst. Du baust Funktionalität nach, die bewusst nicht angeboten wurde. Verstehst du nicht, was ich dir schon die ganze Zeit sagen will?

    wxSkip schrieb:

    Nexus schrieb:

    Du kannst zum Beispiel auch keine temporären Objekte mehr an Funktionen übergeben.

    Sorry, das musst du mir nochmal erklären.

    Ich kenne wxWidgets zu wenig. Aber ein mögliches Einsatzszenario wäre z.B. irgendeine Methode

    AddComponent(Component* newComponent);
    

    die so aufgerufen werden könnte:

    myButtons.AddComponent(new Button("Text", 60, 20));
    

    Dabei wird newButton gleich im Ausdruck erzeugt. Wenn du nun auf die Konstruktoren verzichtest, ist jeweils sowas nötig:

    Button* button = CreatePseudoEmptyButton();
    button->SetText("Text");
    button->SetSize(60, 20);
    myButtons.AddComponent(button);
    

    was neben der Umständlichkeit auch button in den umliegenden Scope einführt. Ist zwar kein grosses Problem, für mich wäre es allerdings unnötige eine Einschränkung.

    wxSkip schrieb:

    Ich will nur Zeiger automatisch auf ein gültiges Objekt zeigen lassen.

    Mit deinem Vorgehen erreichst du das genaue Gegenteil! Das Objekt ist zwar insofern "gültig", als der Zugriff kein undefiniertes Verhalten auslöst, aber logisch gesehen ist es in einem nicht sinnvollen Zustand. Eine Schaltfläche ohne Text und Grösse kannst du nicht brauchen. Es ist immer nachträglich eine "Initialisierung" nötig.

    Du hast mir immer noch nicht erklärt, warum du Buttons nicht erst dann einfügen kannst, wenn sie vollständig initialisiert sind. Was bringt dir ein resize() , das dir etliche Dummy-Objekte erzeugt, die du vor der Benutzung ohnehin nochmals bearbeiten musst?


  • Administrator

    Kleine Bemerkung am Rande; Die Signatur von resize lautet:
    void resize(size_type sz, T c = T());

    @Nexus,
    Ich kann dir sagen, wieso wxSkip das so will, weil er von wxWidgets schlechtes C++ Design gelernt hat.
    1. Müssen alle Widgets in wxWidgets per new konstruiert werden.
    2. Alle Widgets in wxWidgets haben einen Default-Konstruktor und eine Create-Funktion. Das Widget ist mit dem Default-Konstruktor in einem ungültigen Zustand, bis man die Create-Funktion aufgerufen hat. Hatten wir nicht einmal eine Diskussion über solche Init-Funktionen? Kann sie gerade nicht mehr finden.

    Grüssli



  • Nexus schrieb:

    wxSkip schrieb:

    Ja, und jetzt eben nicht mehr.

    Ja, aber auch nur, weil du am Design herumbiegst. Du baust Funktionalität nach, die bewusst nicht angeboten wurde. Verstehst du nicht, was ich dir schon die ganze Zeit sagen will?

    Ja, ich verstehe, dass du sagen willst, dass diese Funktionalität bewusst nicht angeboten wurde. Ich will sie in meiner Programmiersprache aber anbieten. Was soll ich sonst machen, außer das gleiche in C++ umzusetzen? Mir kommt es ja nicht auf jedes Geschwindigkeitsquäntchen an.

    Nexus schrieb:

    wxSkip schrieb:

    Nexus schrieb:

    Du kannst zum Beispiel auch keine temporären Objekte mehr an Funktionen übergeben.

    Sorry, das musst du mir nochmal erklären.

    Ich kenne wxWidgets zu wenig. Aber ein mögliches Einsatzszenario wäre z.B. irgendeine Methode

    AddComponent(Component* newComponent);
    

    die so aufgerufen werden könnte:

    myButtons.AddComponent(new Button("Text", 60, 20));
    

    Dabei wird newButton gleich im Ausdruck erzeugt. Wenn du nun auf die Konstruktoren verzichtest, ist jeweils sowas nötig:

    Button* button = CreatePseudoEmptyButton();
    button->SetText("Text");
    button->SetSize(60, 20);
    myButtons.AddComponent(button);
    

    was neben der Umständlichkeit auch button in den umliegenden Scope einführt. Ist zwar kein grosses Problem, für mich wäre es allerdings unnötige eine Einschränkung.

    wxSkip schrieb:

    Ich will nur Zeiger automatisch auf ein gültiges Objekt zeigen lassen.

    Mit deinem Vorgehen erreichst du das genaue Gegenteil! Das Objekt ist zwar insofern "gültig", als der Zugriff kein undefiniertes Verhalten auslöst, aber logisch gesehen ist es in einem nicht sinnvollen Zustand. Eine Schaltfläche ohne Text und Grösse kannst du nicht brauchen. Es ist immer nachträglich eine "Initialisierung" nötig.

    Du hast mir immer noch nicht erklärt, warum du Buttons nicht erst dann einfügen kannst, wenn sie vollständig initialisiert sind. Was bringt dir ein resize() , das dir etliche Dummy-Objekte erzeugt, die du vor der Benutzung ohnehin nochmals bearbeiten musst?

    Das resize() mit Dummy-Objekten verhindert mir eine Access violation beim Zugriff auf einen durch resize() erzeugten Pointer, genauer gesagt, das Objekt, auf den er zeigt.
    Ich glaube, ich sollte einmal ein Beispiel machen.
    Habe ich in meiner Programmiersprache einen Code:

    Fenster: fenster  //Default-Fenster, wird auf dem Desktop mit einer Default-Größe erstellt.
    Button[Zahl]: myvar  //std::vector<wxButton *> myvar, leer
    Button: knopf(Größe =  _Größe(100, 50); Ziel = fenster; BeiKlick = OnClick)  //Button-Variable "knopf", im Fenster "fenster". ID und Label werden per Default-Construction auf eine neue Zahl und "" gesetzt
    myvar[5] = knopf  //neinnein, kein Fehler, hier wird resize() benutzt
    myvar[4].Ziel = fenster  //so, und jetzt wird der andere Button angezeigt und keine Access violation (OK, er hat noch keine Größe)
    //außerden hat der andere Button keinen ungültigen Zustand, weil seine Create()-Funktion aufgerufen wurde!
    
    Ablauf: OnClick(ButtonKlickEreignis: erg)
    {
        //blah...
    }
    

    So, wie das mit den Arrays läuft, sieht man ja schon.
    Für mich ist es außerdem einfacher, für die Buttonerstellung diesen Code zu generieren:

    wxButton *knopf = create_class<wxButton>();
    knopf->SetParent(fenster);
    knopf->SetSize(wxSize(100, 50));
    knopf->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(App::OnClick));
    

    Wobei ich noch gar nicht gesagt habe, dass ich diesen Code generieren werde, ich wollte das SFINAE-Beispiel ursprünglich eigentlich nur für die Vektoren verwenden.



  • Dravere schrieb:

    weil er von wxWidgets schlechtes C++ Design gelernt hat.

    😞
    Und weil ich es für wxWidgets so benutzen muss?


  • Administrator

    wxSkip schrieb:

    Dravere schrieb:

    weil er von wxWidgets schlechtes C++ Design gelernt hat.

    😞
    Und weil ich es für wxWidgets so benutzen muss?

    Nö, eigentlich nicht. wxWidgets bietet schliesslich inzwischen auch direkt den Konstruktor an, welcher dann intern Create aufruft. Auch aus deinem gezeigten Code werde ich nicht schlau, wieso du diese Verzögerung einbauen müsstest.

    Grüssli



  • Dravere schrieb:

    wxSkip schrieb:

    Dravere schrieb:

    weil er von wxWidgets schlechtes C++ Design gelernt hat.

    😞
    Und weil ich es für wxWidgets so benutzen muss?

    Nö, eigentlich nicht. wxWidgets bietet schliesslich inzwischen auch direkt den Konstruktor an, welcher dann intern Create aufruft. Auch aus deinem gezeigten Code werde ich nicht schlau, wieso du diese Verzögerung einbauen müsstest.

    Grüssli

    Verstehst du nicht, warum ich in meiner create_class<T>()-Funktion nicht direkt den Konstruktor mit Defaultwerten aufrufe oder verstehst du nicht, wozu ich sie brauche?
    Zu 1.: Ja, das könnte auch mit dem Konstruktor gehen. Muss ich nochmal probieren.
    Zu 2.: Dann sag mir, was dir am Beispiel nicht gefällt.



  • Okay, das wäre dann die Version direkt mit Konstruktor:

    class Test_1
    {
        bool allright;
        public:
        Test_1() : allright(true) {}
    
        bool IsGood(){return allright;}
    };
    
    class Test_2
    {
        bool allright;
        public:
        Test_2() : allright(false) {}
        Test_2(int p1, int p2 = 0) : allright(true) {}
    
        bool IsGood(){return allright;}
    };
    
    class Test_3
    {
        bool allright;
        public:
        Test_3() : allright(false) {}
        Test_3(int p1, string p2, int p3 = 0) : allright(true) {}
    
        bool IsGood(){return allright;}
    };
    
    //SFINAE
    
    typedef struct {char x[2];} False_help;
    typedef char True_help;
    
    template<typename T> auto SFINAE_has_string_create(T *in) -> decltype(new T(0, string()));
    False_help SFINAE_has_string_create(...);
    
    template<typename T> True_help SFINAE_is_pointer(T *);
    False_help SFINAE_is_pointer(...);
    #define has_string_create_func(x) (sizeof(True_help) == sizeof(SFINAE_is_pointer(SFINAE_has_string_create((T*)0))))
    
    template<typename T, typename... Args> auto SFINAE_has_other_constructor(T *) -> decltype(new T(0));
    False_help SFINAE_has_other_constructor(...);
    
    #define has_other_create_func(x) (sizeof(True_help) == sizeof(SFINAE_is_pointer(SFINAE_has_other_constructor((T*)0))))
    
    template<typename T, bool has_other_create> struct Call_right_other_constructor //will be used if create_needs_string == true
    {
        static T *exec(){return new T(0);}
    };
    
    template<typename T> struct Call_right_other_constructor<T, false>
    {
        static T *exec(){return new T();}
    };
    
    template<typename T, bool has_string_create> struct Create_with_string_if_true //will be used if has_create == true
    {
        static T *exec(){return new T(0, string());}
    };
    
    template<typename T> struct Create_with_string_if_true<T, false>
    {
        static T *exec(){return Call_right_other_constructor<T, has_other_create_func(T)>::exec();}
    };
    
    template<typename T> T *create_class()
    {
        return Create_with_string_if_true<T, has_string_create_func(T)>::exec(); //if the type has a Create()-function, it will be called
    }
    
    int main()
    {
        Test_1 *x1 = create_class<Test_1>();
        Test_2 *x2 = create_class<Test_2>();
        Test_3 *x3 = create_class<Test_3>();
    
        cout << boolalpha << x1->IsGood() << endl << x2->IsGood() << endl << x3->IsGood() << endl;
    }
    

    Und die wxWidgets-Version:

    //SFINAE
    
    typedef struct {char x[2];} False_help;
    typedef char True_help;
    
    template<typename T> auto SFINAE_has_string_create(T *in) -> decltype(new T(NULL, wxID_ANY, wxEmptyString));
    False_help SFINAE_has_string_create(...);
    
    template<typename T> True_help SFINAE_is_pointer(T *);
    False_help SFINAE_is_pointer(...);
    #define has_string_create_func(x) (sizeof(True_help) == sizeof(SFINAE_is_pointer(SFINAE_has_string_create((T*)0))))
    
    template<typename T, typename... Args> auto SFINAE_has_other_constructor(T *) -> decltype(new T(NULL, wxID_ANY));
    False_help SFINAE_has_other_constructor(...);
    
    #define has_other_create_func(x) (sizeof(True_help) == sizeof(SFINAE_is_pointer(SFINAE_has_other_constructor((T*)0))))
    
    template<typename T, bool has_other_create> struct Call_right_other_constructor //will be used if create_needs_string == true
    {
        static T *exec(){return new T(NULL, wxID_ANY);}
    };
    
    template<typename T> struct Call_right_other_constructor<T, false>
    {
        static T *exec(){return new T();}
    };
    
    template<typename T, bool has_string_create> struct Create_with_string_if_true //will be used if has_create == true
    {
        static T *exec(){return new T(NULL, wxID_ANY, wxEmptyString);}
    };
    
    template<typename T> struct Create_with_string_if_true<T, false>
    {
        static T *exec(){return Call_right_other_constructor<T, has_other_create_func(T)>::exec();}
    };
    
    template<typename T> T *create_class()
    {
        return Create_with_string_if_true<T, has_string_create_func(T)>::exec(); //if the type has a Create()-function, it will be called
    }
    

  • Administrator

    Nur um dies mal klarzustellen:
    Deine Sprache wird zu C++ Code übersetzt, welcher dann von einem C++ Kompiler nochmals übersetzt wird? Ist also sowas wie ein Pre-Preprocessor?

    Dann zu deinem Konstrukt:
    Grundsätzlich willst du schauen, ob es eine Create -Funktion hat und danach schauen, was für eine das ist, bzw. ob ein std::string an dritter Stelle auftaucht. Du gehst aber davon aus, dass es keine sonstigen Überladungen gibt, wieso? Denn die gibt es auch noch. Was machst du z.B. bei einem wxChoice ? Oder wenn zukünftig etwas eingeführt wird? Und was machst du, wenn der Defaultwert bei einem Widget nicht derselbe sein soll, wie bei einem anderen Widget?
    Wieso verwendest du nicht gleich Type-Traits?

    template<typename T>
    struct wxTypeTraits
    {
      static T* create() { return new T(); }
    };
    
    template<>
    struct wxTypeTraits<wxButton>
    {
      static wxButton* create() { return new wxButton(NULL, wxID_ANY); }
    };
    
    // usw.
    

    Deutlich leserlicher und auch anpassungfähiger.

    Allerdings empfinde ich auch diese Lösung als fragwürdig, wenn du schon einen Pre-Preprocessor schreibst. Schliesslich musst du in deinem Pre-Preprocessor einen Handler einbauen, welcher dir den entsprechenden C++ Code erzeugt. In diesem Handler kannst du eigentlich bereits auf die Fälle unterschiedlich reagieren, in dem du eben unterschiedliche Handler registrierst. Aus dem Button in deinem präsentierten Pre-Preprocessor Code muss schliesslich auch ein wxButton erstellt werden. Registriere also ein Handler auf Button, welcher auch den richtigen C++ Quellcode erzeugt. Es gibt auch noch einen anderen Grund, wieso du dies in deinem Pre-Preprocessor tun solltest. C++ Kompiler sind eher langsam, was das kompilieren von Template-Metaprogrammierung angeht.

    Grüssli



  • Dravere schrieb:

    Nur um dies mal klarzustellen:
    Deine Sprache wird zu C++ Code übersetzt, welcher dann von einem C++ Kompiler nochmals übersetzt wird? Ist also sowas wie ein Pre-Preprocessor?

    Ja, ich wollte einfach nicht nach Assembler übersetzen, das war mir dann doch etwas zu schwierig. Ich würde es aber trotzdem Compiler nennen, da es doch eine andere Programmiersprache ist (nicht zu vergleichen mit Compileranweisungen).

    Dravere schrieb:

    Dann zu deinem Konstrukt:
    Grundsätzlich willst du schauen, ob es eine Create -Funktion hat und danach schauen, was für eine das ist, bzw. ob ein std::string an dritter Stelle auftaucht. Du gehst aber davon aus, dass es keine sonstigen Überladungen gibt, wieso? Denn die gibt es auch noch. Was machst du z.B. bei einem wxChoice ? Oder wenn zukünftig etwas eingeführt wird? Und was machst du, wenn der Defaultwert bei einem Widget nicht derselbe sein soll, wie bei einem anderen Widget?

    Ja, beziehungsweise jetzt gleich beim Konstruktor.
    Ich gehe davon aus, dass es bei wxWidgets keinen anderen Konstruktor gibt (habe jetzt zwar nicht alle Typen durchgeschaut, aber sonst nehme ich halt eine andere Lösung). Wenn es ihn gibt, kann ich ihn immer noch verwenden. So wie hier funktioniert das auch bei wxChoice : http://docs.wxwidgets.org/trunk/classwx_choice.html#4b8a1ceecde2d839156e4afe9fa8feca.
    Wenn etwas zukünftig anders eingeführt wird, dann füge ich das halt entweder hinzu oder ändere mein Konzept.
    Meine 2-3 Defaultwerte sind ja immer Parent, ID und ggf. Beschriftung/Titel. Da wüsste ich nicht, wann da andere Defaultwerte angebracht wären.

    Dravere schrieb:

    Wieso verwendest du nicht gleich Type-Traits?

    template<typename T>
    struct wxTypeTraits
    {
      static T* create() { return new T(); }
    };
    
    template<>
    struct wxTypeTraits<wxButton>
    {
      static wxButton* create() { return new wxButton(NULL, wxID_ANY); }
    };
    
    // usw.
    

    Deutlich leserlicher und auch anpassungfähiger.

    Daran habe ich auch schon gedacht, aber es vorerst ausgeschlossen, weil es für mich einfach mehr Aufwand bedeuten würde. Außerdem müsste ich hier auf jeden Fall etwas ändern, falls eine neue GUI-Klasse eingeführt wird.
    (vielleicht war die ganze Mühe ja auch umsonst, falls mit wxWidgets 3.0 die Defaultkonstruktoren eingeführt werden, bei denen das Objekt dann voll verwendungsfähig ist 😃 )

    Dravere schrieb:

    Allerdings empfinde ich auch diese Lösung als fragwürdig, wenn du schon einen Pre-Preprocessor schreibst. Schliesslich musst du in deinem Pre-Preprocessor einen Handler einbauen, welcher dir den entsprechenden C++ Code erzeugt. In diesem Handler kannst du eigentlich bereits auf die Fälle unterschiedlich reagieren, in dem du eben unterschiedliche Handler registrierst. Aus dem Button in deinem präsentierten Pre-Preprocessor Code muss schliesslich auch ein wxButton erstellt werden. Registriere also ein Handler auf Button, welcher auch den richtigen C++ Quellcode erzeugt. Es gibt auch noch einen anderen Grund, wieso du dies in deinem Pre-Preprocessor tun solltest. C++ Kompiler sind eher langsam, was das kompilieren von Template-Metaprogrammierung angeht.

    Hm, mir ist gerade eingefallen, man könnte ja beim eigenen Vector-Typ irgendwie die Defaultparameter für den Konstruktor mitgeben... muss mal schauen.
    Ansonsten, meinst du die Instantiierung für ein paar Vectortypen wäre so aufwändig?

    Dravere schrieb:

    Grüssli

    mfg 😉


  • Administrator

    wxSkip schrieb:

    [So wie hier funktioniert das auch bei wxChoice : http://docs.wxwidgets.org/trunk/classwx_choice.html#4b8a1ceecde2d839156e4afe9fa8feca.

    In der Dokumentation der Stable-Version sieht das ein wenig anders aus:
    http://docs.wxwidgets.org/stable/wx_wxchoice.html#wxchoice

    wxSkip schrieb:

    Meine 2-3 Defaultwerte sind ja immer Parent, ID und ggf. Beschriftung/Titel. Da wüsste ich nicht, wann da andere Defaultwerte angebracht wären.

    Gerade in Sachen Titel/Beschriftung sehe ich da Möglichkeiten, dass nicht unbedingt ein leerer String sinnvoll sein könnte.

    wxSkip schrieb:

    Dravere schrieb:

    Allerdings empfinde ich auch diese Lösung als fragwürdig, wenn du schon einen Pre-Preprocessor schreibst. Schliesslich musst du in deinem Pre-Preprocessor einen Handler einbauen, welcher dir den entsprechenden C++ Code erzeugt. In diesem Handler kannst du eigentlich bereits auf die Fälle unterschiedlich reagieren, in dem du eben unterschiedliche Handler registrierst. Aus dem Button in deinem präsentierten Pre-Preprocessor Code muss schliesslich auch ein wxButton erstellt werden. Registriere also ein Handler auf Button, welcher auch den richtigen C++ Quellcode erzeugt. Es gibt auch noch einen anderen Grund, wieso du dies in deinem Pre-Preprocessor tun solltest. C++ Kompiler sind eher langsam, was das kompilieren von Template-Metaprogrammierung angeht.

    Hm, mir ist gerade eingefallen, man könnte ja beim eigenen Vector-Typ irgendwie die Defaultparameter für den Konstruktor mitgeben... muss mal schauen.
    Ansonsten, meinst du die Instantiierung für ein paar Vectortypen wäre so aufwändig?

    Was meinst du mit "Instantiierung für ein paar Vectortypen"? Der Teil mit Template-Metaprogrammierung wird in deinem Code bei create_class<T> ausgeführt. Also immer wenn der Kompiler ein create_class antrifft, muss er eine entsprechende Template-Auswertung durchführen. Für sowas sind die Kompiler aber nicht wirklich optimiert.

    Grüssli



  • Dravere schrieb:

    wxSkip schrieb:

    [So wie hier funktioniert das auch bei wxChoice : http://docs.wxwidgets.org/trunk/classwx_choice.html#4b8a1ceecde2d839156e4afe9fa8feca.

    In der Dokumentation der Stable-Version sieht das ein wenig anders aus:
    http://docs.wxwidgets.org/stable/wx_wxchoice.html#wxchoice

    Die Stable-Version verwende ich zwar nicht, aber Danke, das wäre wirklich ein Indiz, um mal genauer hinzuschauen. 👍

    Dravere schrieb:

    wxSkip schrieb:

    Meine 2-3 Defaultwerte sind ja immer Parent, ID und ggf. Beschriftung/Titel. Da wüsste ich nicht, wann da andere Defaultwerte angebracht wären.

    Gerade in Sachen Titel/Beschriftung sehe ich da Möglichkeiten, dass nicht unbedingt ein leerer String sinnvoll sein könnte.

    wxSkip schrieb:

    Dravere schrieb:

    Allerdings empfinde ich auch diese Lösung als fragwürdig, wenn du schon einen Pre-Preprocessor schreibst. Schliesslich musst du in deinem Pre-Preprocessor einen Handler einbauen, welcher dir den entsprechenden C++ Code erzeugt. In diesem Handler kannst du eigentlich bereits auf die Fälle unterschiedlich reagieren, in dem du eben unterschiedliche Handler registrierst. Aus dem Button in deinem präsentierten Pre-Preprocessor Code muss schliesslich auch ein wxButton erstellt werden. Registriere also ein Handler auf Button, welcher auch den richtigen C++ Quellcode erzeugt. Es gibt auch noch einen anderen Grund, wieso du dies in deinem Pre-Preprocessor tun solltest. C++ Kompiler sind eher langsam, was das kompilieren von Template-Metaprogrammierung angeht.

    Hm, mir ist gerade eingefallen, man könnte ja beim eigenen Vector-Typ irgendwie die Defaultparameter für den Konstruktor mitgeben... muss mal schauen.
    Ansonsten, meinst du die Instantiierung für ein paar Vectortypen wäre so aufwändig?

    Was meinst du mit "Instantiierung für ein paar Vectortypen"? Der Teil mit Template-Metaprogrammierung wird in deinem Code bei create_class<T> ausgeführt. Also immer wenn der Kompiler ein create_class antrifft, muss er eine entsprechende Template-Auswertung durchführen. Für sowas sind die Kompiler aber nicht wirklich optimiert.

    Grüssli

    Ich meine damit, dass ich ja eigentlich vorhabe, mein create-class<T>() bloß in einem Pointer-Vektor zu verwenden. D.h. das Ding sollte eigentlich nur einmal pro Vektortyp instantiiert werden.

    mfg



  • Ich habe jetzt mal versucht, dem Vector die Default-Konstruktor-Parameter zu übergeben, allerdings habe ich das nur mit Integralen Typen geschafft und außerdem wäre es sicherlich mehr Templateinstantiierung als die andere Möglichkeit. Bliebe nur noch die Möglichkeit mit der Generierung unterschiedlicher Typen abhängig vom Konstruktor, die mir aber nicht so gefällt.
    Nach Durchschauen von ein paar wxWidgets-Typen muss ich mir die ganze Sache aber nochmal überlegen, denn da gibt es durchaus ein paar, die nicht in mein Konzept passen.



  • Auch wenn es schon 3 Monate her ist, will ich alle, die sich nach schönem Code sehnen, vielleicht etwas beruhigen 😃 . Zwar habe ich mir vor einiger Zeit noch ein paar SFINAE-Defines geschrieben, die mir das Hinzufügen eines neuen Konstruktors in einer Zeile ermöglichen würden, doch jetzt bin ich auf eine m.E. bessere Idee gekommen, wie ich die Defaultparameter direkt an den Vektor übergeben könnte.

    Dazu hier ein Beispiel (ohne Vektor, aber mit Konstruktoraufruf):

    #include <iostream>
    
    //diese Klasse ist nur korrekt initialisiert, wenn man nicht den Defaultkonstruktor benutzt
    class test1
    {
        private:
        bool is_initialized_correctly;
    
        public:
        test1() : is_initialized_correctly(false){}
        test1(std::string str, int foo, char *bar, size_t s = 0) : is_initialized_correctly(true){}
    
        bool IsGood(){return is_initialized_correctly;}
    };
    
    //ich wollte es erst mit einer nicht verschachtelten Klasse versuchen, habe es aber nicht hingekriegt
    template<typename T, typename... ConstructorParameters>
        class type_container
    {
        public:
        template<ConstructorParameters (*...constructor_functions)()> class encapsulator
        {
            T obj;
    
            public:
            encapsulator() : obj(constructor_functions()...){}
            ~encapsulator(){}
    
            T &GetObj(){return obj;}
        };
    };
    
    std::string GetDefaultString()
    {
        return "";
    }
    
    int GetDefaultInt()
    {
        return 0;
    }
    
    char *GetDefaultCharP()
    {
        return NULL;
    }
    
    int main()
    {
        type_container<test1, std::string, int, char *>::encapsulator<GetDefaultString, GetDefaultInt, GetDefaultCharP> enc_var;
        std::cout << boolalpha << enc_var.GetObj().IsGood() << "\n";  //Ausgabe: true
    }
    

    Übrigens habe ich mich noch nicht zwischen wxWidgets und Qt entschieden, da von wxWidgets 3.0 bislang jede Spur fehlt 😞 .



  • Dravere schrieb:

    Nur um dies mal klarzustellen:
    Deine Sprache wird zu C++ Code übersetzt, welcher dann von einem C++ Kompiler nochmals übersetzt wird? Ist also sowas wie ein Pre-Preprocessor?

    So ganz nicht, oder? Er nutzt den vorhandenen PreProccessor als Component Script Interpreter.
    Ist auf jeden fall ziemlich clever das so weit auszulehnen.

    Mal ne frage wxSkip:
    A) Warum bastelst du Dir nicht gleich n ausführbaren interpreter, der im pre-build ausgeführt wird?
    😎 Wenn es Dir nicht um geschwindigkeit geht, warum bastelst du dir denn nicht ne run-time die ein dein script intepretiert und ausführt?

    greetz



  • @zeusosc Ich verstehe dich nicht ganz, wahrscheinlich, weil du mich nicht ganz verstanden hast. Das Ganze wird ein Compiler, der Quellcode von meiner Quellsprache nach C++ übersetzt und dann mithilfe des gcc (wahrscheinlich auch unter Windows, aus Kompatibilitätsgründen) eine Executable draus generiert oder halt Object-Files und automatische Header-Info-Dateien. Pre-Preprocessor ist dann doch ein unschöner Name, bloß weil mir Assembler zu schwierig war und ich momentan noch die Optimierungen dem GCC überlasse 😃 .


Anmelden zum Antworten