Optionale Parameter?



  • SeppJ schrieb:

    1. Einzelfunktionen
    2. Funktionsüberladung
    3. Aneinandergekettete überladene Operatoren
    4. Parameterobjekt
    5. Irgendwas cleveres mit Templates
    6. Ellipse

    Ok, sind vielleicht doch nicht unendlich viele, für Mathematiker ist aber 6 schon viel größer als 1 😃 . Aber 6. ist das was du beschreibst und Möglichkeiten 1.-5. sind allesamt in fast jeder Situation besser.

    Nein, 6. ist NICHT das was ich beschreibe. Die Ellipse bedeutet nur beliebig viele Parameter beliebigen Typs. Ich habe ein klares Typmuster (key,value) und die Anzahl der Parameterpaare ist fest. Der Benutzer meiner Funktion soll sogar
    gezwungen sein, sich ueber jeden einzelnen Parameter Gedanken zu machen.
    Drum scheiden alle deine Punkte aus, ausser Punkt 4. Wie wuerde sowas mit Parameterobjekt aussehen? Die Values der Keys haben ja unterschiedliche Werte (z.B. ON, 1.3, ON, 22, OFF, OFF, OFF, ON, "Hallo" etc.)



  • Du kannst das Named-Parameter Idiom noch mit Funktionsüberladung und typisierten Tag-Konstanten für on- und off kombinieren:

    struct OnType{} const ON;
    struct OffType{} const OFF;
    
    class C
    {
      public: 
        C() : a(-999), b('C') {}  
        C & setA(OnType, int i) { a = i; return *this; }  //uses value
        C & setA(OffType) { a = 0; return *this; } //uses some default
    
        C & setB(OnType, char c) { b = c; return *this; } //uses value
        C & setB(OffType) { b = ' '; return *this; } //uses some default
    
        static C create() {
          return C();
        }
    private:
        int a;
        char b;
    
    };
    //...
    C x(C::create().setA(OFF).setB(ON, 'k'));
    

    Aber schön ist das nicht...
    Wenn alle Werte auf mal gesetzt werden müssen, musst Du wohl für alle möglichen Kombination Überladungen bereit stellen. 😃



  • Ich hab das zwar noch nie benutzt, aber es klingt nach einem Einsatzgebiet für boost::optional.



  • ACK, wollte auch gerade boost::optional vorschlagen.



  • Optionator schrieb:

    Wie wuerde sowas mit Parameterobjekt aussehen?

    Eine (Imho noch immer unschöne Variante):

    // Variante mit Boost (ungetestet)
    #include <boost/optional.hpp>
    using namespace boost;
    
    // Mit Zwang bei jeden Anzugeben...
    // (Wobei ich da kein Fan von bin, und eher eine Struktur/Klasse mit den
    // optionalen Parametern deklarieren würde...)
    void foo(
      optional<bool> parameter1,
      optional<int> parameter2)
    {
        // Wenn Parameter1 mit einem entsprechenden Wert gesetzt ist...
        if(parameter1.is_initialized())
            // ... TuEtwasMit(parameter1.get())
        // ...
    }
    
    int main()
    {
        foo(optional<bool>(), 1);
    }
    

    Man kann sich natürlich boost::optional oder vergleichbares selbst bauen.



  • Optionator schrieb:

    Nein, 6. ist NICHT das was ich beschreibe. Die Ellipse bedeutet nur beliebig viele Parameter beliebigen Typs. Ich habe ein klares Typmuster (key,value) und die Anzahl der Parameterpaare ist fest. Der Benutzer meiner Funktion soll sogar
    gezwungen sein, sich ueber jeden einzelnen Parameter Gedanken zu machen.
    Drum scheiden alle deine Punkte aus, ausser Punkt 4. Wie wuerde sowas mit Parameterobjekt aussehen? Die Values der Keys haben ja unterschiedliche Werte (z.B. ON, 1.3, ON, 22, OFF, OFF, OFF, ON, "Hallo" etc.)

    std::pair<bool, T> ?

    void foo(std::pair<bool, float>, std::pair<bool, int>, std::pair<bool, std::string>, ....)
    

    oder verstehe ich was falsch?


  • Mod

    Optionator schrieb:

    Nein, 6. ist NICHT das was ich beschreibe.

    Man kann es aber damit machen. Und ich sage ja selbst, dass das nicht hübsch ist. Nachteil ist nämlich, dass Fehler erst zur Laufzeit bemerkt werden.

    Der Benutzer meiner Funktion soll sogar
    gezwungen sein, sich ueber jeden einzelnen Parameter Gedanken zu machen.
    Drum scheiden alle deine Punkte aus, ausser Punkt 4.

    Wenn du meinst 🙄 . Ich finde deine Begründung nicht einsichtig (anders gesagt: Sie ist keine und ich glaube du stellst dich bloß stur) und schlage dir vor, dies noch einmal abzuwägen. Aber wenn es unbedingt 4 sein muss, hier ein Vorschlag, ist nur auf die Schnelle und daher noch verbesserungsfähig:

    class temperature
    {
     double data;
     bool valid;
    public:
     temperature(): valid(false){}
     temperature(double data): data(data), valid(true){}
     bool is_valid(){return valid;}
    };
    
    class lighting
    {
     double data;
     bool valid;
    public:
     lighting(): valid(false){}
     lighting(double data): data(data), valid(true){}
     bool is_valid(){return valid;}
    };
    
    // ...
    
    void set_parameters(temparature t, lighting l)
    {
     if (t.is_valid())
      {
        // Setze Temperatur
      }
     if (l.is_valid())
      {
        // Setze Beleuchtung
      }
    }
    
    // ...
    
    // Beides:
    set_parameters(temperature(5.0), lighting(1.0));
    // Nur Temperatur:
    set_parameters(temperature(5.0), lighting());
    

    Alternativ auch ein globales Objekt no_temperature und dafür den Defaultkonstruktor rausnehmen, wenn dir die Syntax besser gefällt:

    set_parameters(no_temperature, lighting(5.0));
    


  • SeppJ schrieb:

    Nein, 6. ist NICHT das was ich beschreibe.

    Man kann es aber damit machen. Und ich sage ja selbst, dass das nicht hübsch ist.
    [/quote]
    Nein, eben nicht. Man kann NICHT damit das machen, was ich will. Ich will, dass der Benutzer alle Parameter (die alle einen FESTEN TYPEN haben) der Funktion durchgeht und sich fuer jeden einzelnen ueberlegt, ob er ihn setzen will oder nicht. Bei einer Ellipse sieht er nicht mal Typen und kann 3 Billionen Parameter irgend eines Typs uebergeben. Aber ICH bin stur, ne? 🙄

    @BigNeal: Das Problem mit den pairs ist, dass der 2. Parameter des pairs eben nicht optional ist (den brauch ich ja nicht, wenn der 1. Wert false ist).

    Werd mir mal boost::optional anschauen.



  • Optionator schrieb:

    Ich will, dass der Benutzer alle Parameter (die alle einen FESTEN TYPEN haben) der Funktion durchgeht und sich fuer jeden einzelnen ueberlegt, ob er ihn setzen will oder nicht.

    Du solltest aber dennoch abwägen ob dies nicht übertrieben ist (Ich würde solche Schnittstellen jedenfalls nicht benutzen wollen, zumal sie imho weder lesbar noch sinnvoll zu warten sind). Ich glaube sogar das du den gegenteiligen Effekt (Thema Reizüberflutung) erreichen wirst.



  • du sagst doch selber man soll sich gedanken machen müssen

    function(ON, 1.3f, ON, 22, OFF, OFF, ON, "hallo", OFF, ON, 55.332f);

    foo(make_pair(true,1.3f),   // <-- hier muss ich mir gedanken machen was ich übergeben will
        make_pair(true, 22),   // hier auch
        make_pair(false, 0),   // hier muss ich überlegen, dass ich nichts übergeben will, also kann man auch noch eine "0" schreiben.
        make_pair(false, ""),
        make_pair(true, "hallo"),
        make_pair(false, 0),
        make_pair(true, 55.332f));
    

    ich sehe das problem so nicht ganz
    und sonst schreib eine funktion wie make_pair mit einem default wert für second



  • Optionator schrieb:

    Ich will, dass der Benutzer alle Parameter (die alle einen FESTEN TYPEN haben) der Funktion durchgeht und sich fuer jeden einzelnen ueberlegt, ob er ihn setzen will oder nicht.

    Wenn jemand ernsthaft dazu gezwungen ist, mit diesem API zu arbeiten (und denjenigen bedaure ich jetzt schon), wirst du damit rechnen müssen, dass er sich einen flexibleren und benutzerfreundlicheren Wrapper drumherum bastelt. Ich jedenfalls würde das tun.


  • Mod

    Optionator schrieb:

    Bei einer Ellipse sieht er nicht mal Typen und kann 3 Billionen Parameter irgend eines Typs uebergeben. Aber ICH bin stur, ne? 🙄

    Gerade bei einer Ellipse muss er sich doch Gedanken machen, eben WEIL so viel schiefgehen kann. Da in der Schnittstelle selbst keine Hilfestellung zu finden ist, muss schließlich genau dokumentiert sein, welche Parameter in welcher Reihenfolge kommen und man muss ständig die Anleitung daneben liegen haben.

    Wobei ich es genau deshalb eben nicht nehmen würde. Aber ich verstehe einfach nicht, wie du nicht sehen kannst, dass dies genau das ist was du beschreibst.



  • Mit dieser "API" arbeiten wir intern und wir wollen das so. Das brauch ich jetzt nicht gross erlaeutern, da ihr den kompletten Kontext sowieso nicht kennt.

    @BigNeal: Das "Problem" ist doch offensichtlich. Ein foo(ON, 2, OFF, OFF, OFF, ON. 33.3f) waere deutlich schoener als ein foo(ON, 2, OFF, "", OFF, 0.0f, OFF, 0, ON, 33.3f);



  • Wenn ich sowas vorgesetzt bekomme, dann sind die einzigen Gedanken die ich mir mache, dort überall no rein zu schreiben, um das Standardverhalten zu bekommen, welches ich auch ohne unnötige Parameter erwarten würde. Wenn mir das Standardverhalten nicht zusagt, muss ich auf jeden Fall in die Doku schauen und die interessanten Parameter raus suchen und entsprechend setzten. Auch hier stört mich das ewige no, no, no eher, als das es etwas bringen würde. Kurzum entweder ich will einen Parameter verändern, dann mache ich dass selbstständig oder nicht, dann interessiert mich auch nicht die Liste. Wenn die Parameter zur Ausführung notwendig sind, dann kannst du das auch vorher abtesten, ob alle gesetzt wurden. Den Anwender aber zu zwingen sich mit Optionen auseinander zu setzten, die ihn gar nicht interessieren, macht keinen Sinn.
    Übrigens ist die Variante nicht nur nervig, sondern auch extrem Fehleranfällig. Weil du Parameter gleichen Typs leicht an die falsche Stelle schreiben kannst.



  • Optionator schrieb:

    Mit dieser "API" arbeiten wir intern und wir wollen das so.

    Was macht ihr denn, wenn man erst zur Laufzeit weiß, ob man einen Parameter setzen will? Ist man dann zu einem

    if(willsetzen)
       aufruf
    else
       aufruf_mit_anderer_signatur_weil_1_parameter_weniger
    

    gezwungen? Oder ist das nur für den interaktiven Einsatz gedacht?

    Optionator schrieb:

    Ein foo(ON, 2, OFF, OFF, OFF, ON. 33.3f) waere deutlich schoener als ein foo(ON, 2, OFF, "", OFF, 0.0f, OFF, 0, ON, 33.3f);

    Wenn du das wirklich schön findest, dann mach es doch einfach mit Überladung. Du brauchst dann eben 2^n Überladungen für n Parameter. Aber eine fiese Schnittstelle sollte auch fies zu inplementieren sein.



  • Paul Müller schrieb:

    Wenn ich sowas vorgesetzt bekomme, dann sind die einzigen Gedanken die ich mir mache, dort überall no rein zu schreiben, um das Standardverhalten zu bekommen, welches ich auch ohne unnötige Parameter erwarten würde. Wenn mir das Standardverhalten nicht zusagt, muss ich auf jeden Fall in die Doku schauen und die interessanten Parameter raus suchen und entsprechend setzten. Auch hier stört mich das ewige no, no, no eher, als das es etwas bringen würde. Kurzum entweder ich will einen Parameter verändern, dann mache ich dass selbstständig oder nicht, dann interessiert mich auch nicht die Liste. Wenn die Parameter zur Ausführung notwendig sind, dann kannst du das auch vorher abtesten, ob alle gesetzt wurden. Den Anwender aber zu zwingen sich mit Optionen auseinander zu setzten, die ihn gar nicht interessieren, macht keinen Sinn.
    Übrigens ist die Variante nicht nur nervig, sondern auch extrem Fehleranfällig. Weil du Parameter gleichen Typs leicht an die falsche Stelle schreiben kannst.

    Es ist genau das Gegenteil der Fall. Im Moment haben wir staendig Fehler, weil sich Entwickler zu wenig Gedanken ueber Pipeline States machen. Drum haben wir uns entschiedene eine OPTIONALE Hilfsfunktion anzubieten, mit der man die wichtigsten States setzen kann. Dies dient nur dazu die ewig auftretenden Fluechtigkeitsfehler zu vermeiden, im dem sich jeder Entwickler genau ueberlegt, welchen Zustand bestimmte wichtige States haben sollen.

    Is mir schon oft in diesem Forum aufgefallen, dass immer gleich ein paar Superkluge (so sehen sie sich zumindest selber) daherkommen und meinen sie muessten einen belehren und sagen was gut ist und nicht. Obwohl sie meistens keinerlei Ahnung von den eigentlichen Gruenden haben. Es gibt nicht nur ON und OFF etc. In Wirklichkeit ist es deutlich komplexer, aber ich habe es eben auf den KERN des Problems vereinfacht. Spart euch in Zukunft vielleicht mal die Metatipps und ewigen Belehrungen und versucht einfach nur die FRAGE an sich zu beantworten.


  • Mod

    Optionator schrieb:

    Is mir schon oft in diesem Forum aufgefallen, dass immer gleich ein paar Superkluge (so sehen sie sich zumindest selber) daherkommen und meinen sie muessten einen belehren und sagen was gut ist und nicht. Obwohl sie meistens keinerlei Ahnung von den eigentlichen Gruenden haben. Es gibt nicht nur ON und OFF etc. In Wirklichkeit ist es deutlich komplexer, aber ich habe es eben auf den KERN des Problems vereinfacht. Spart euch in Zukunft vielleicht mal die Metatipps und ewigen Belehrungen und versucht einfach nur die FRAGE an sich zu beantworten.

    Lieber nicht. Es ist mir schon oft in diesem Forum aufgefallen, dass immer jemand nach einer ganz genauen Lösung sucht die Sie für die einzig mögliche halten. Obwohl sie meistens keinerlei Ahnung von den verfügbaren Sprachmitteln haben. Die wenigen Fälle, die tatsächlich mal genau diese eine Lösung suchen, können auch mit ein paar Metatipps leben.



  • Also ich würde Ellipse sagen...

    Etwa so:

    foo(bool b, ...)
    {
    	va_list va;
    	va_start(va, b);
    	if(b)
    		float1=va_arg(va, float);
    	if(va_arg(va, bool))
    		int1=va_arg(va, int);
    	if(va_arg(va, [s]float[/s]bool))
    		float2=va_arg(va, float);
    	/* ..... */
    	va_end(va);
    }
    

    Oder halt in 'ne Schleife...



  • Optionator schrieb:

    Im Moment haben wir staendig Fehler, weil sich Entwickler zu wenig Gedanken ueber Pipeline States machen. Drum haben wir uns entschiedene eine OPTIONALE Hilfsfunktion anzubieten, mit der man die wichtigsten States setzen kann.

    Schreit nach einem simplen Config-Objekt.
    Wozu muss ich OFF setzen? Ich setze nur die Sachen die ich brauche, alles andere ist OFF. Siehe Tachyons Post auf Seite 2.

    Dein Ansatz ist einfach falsch. Deshalb sind alle Lösungen nur krüppelig.



  • Shade Of Mine schrieb:

    Optionator schrieb:

    Im Moment haben wir staendig Fehler, weil sich Entwickler zu wenig Gedanken ueber Pipeline States machen. Drum haben wir uns entschiedene eine OPTIONALE Hilfsfunktion anzubieten, mit der man die wichtigsten States setzen kann.

    Schreit nach einem simplen Config-Objekt.
    Wozu muss ich OFF setzen? Ich setze nur die Sachen die ich brauche, alles andere ist OFF. Siehe Tachyons Post auf Seite 2.

    Dein Ansatz ist einfach falsch. Deshalb sind alle Lösungen nur krüppelig.

    👍
    Mr. Optionator tritt hier anonym auf und trollt rum.
    Sieht mir fast so aus wie die 1000. Kiddie-Grafik-Engine, die im Endeffekt eine einzige Krüppelei zu sein scheint.


Anmelden zum Antworten