Optionale Parameter?



  • Bis auf die Syntax geht das schon (ob es so gutes Design ist, sei mal dahin gestellt):

    void processBunchOfParameters(bool shallSetTemperature, double temperature, bool shallSetLighting, double lighting);
    

    Aufruf:

    processBunchOfParameters(false, 0.0, true, 42.0);
    

  • Mod

    Optionator schrieb:

    Mir ist klar, dass das so in C++ nicht geht.

    Natürlich geht das. (Aber nicht hübsch. Stichwort: Ellipse)

    Wie koennte man das einigermassen schoen nachbauen?

    Willst du das wirklich? Das klingt eher nach konfusem Design. Da gibt es so unendlich viele bessere Möglichkeiten. Etwas mehr Information über das Problem würde helfen, die beste zu finden.



  • Das "named parameter idiom" ist schon ganz nett. Das Problem ist nur, dass dabei die Parameter alle optional sind. Meine Parameter (ich nenne sie mal Keys) muessen ALLE gesetzt werden (ON, OFF und evtl. noch andere Werte) und nur wenn ein Key OFF ist, muss kein Wert angegeben werden.



  • SeppJ schrieb:

    Optionator schrieb:

    Mir ist klar, dass das so in C++ nicht geht.

    Natürlich geht das. (Aber nicht hübsch. Stichwort: Ellipse)

    Die Ellipse macht was ganz anderes.

    SeppJ schrieb:

    Willst du das wirklich? Das klingt eher nach konfusem Design. Da gibt es so unendlich viele bessere Möglichkeiten. Etwas mehr Information über das Problem würde helfen, die beste zu finden.

    Na dann nen doch einfach mal 10 aus deinem unendlichen Pool. Ich will einfach eine maechtige Funktion zum Setzen verschiedener States einer Rendering Pipeline in einem SceneGraph. Und manche Parameter brauchen eben Werte (z.B. AlphaTest=0.2, wenn ich AlphaTest aber deaktiviere, brauche ich keinen Wert)


  • Mod

    Optionator schrieb:

    SeppJ schrieb:

    Optionator schrieb:

    Mir ist klar, dass das so in C++ nicht geht.

    Natürlich geht das. (Aber nicht hübsch. Stichwort: Ellipse)

    Die Ellipse macht was ganz anderes.

    Aha, wenn du meinst 🙄 . Du musst es ja wissen, du bist schließlich der Fragesteller und ich nur der Antwortgeber...[/ironie]

    SeppJ schrieb:

    Willst du das wirklich? Das klingt eher nach konfusem Design. Da gibt es so unendlich viele bessere Möglichkeiten. Etwas mehr Information über das Problem würde helfen, die beste zu finden.

    Na dann nen doch einfach mal 10 aus deinem unendlichen Pool. Ich will einfach eine maechtige Funktion zum Setzen verschiedener States einer Rendering Pipeline in einem SceneGraph. Und manche Parameter brauchen eben Werte (z.B. AlphaTest=0.2, wenn ich AlphaTest aber deaktiviere, brauche ich keinen Wert)

    Das ist es ja eben: Wieso willst du eine solch mächtige Funktion? KISS.

    Also:
    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.



  • Optionator schrieb:

    Wie koennte man das einigermassen schoen nachbauen?

    Ist dir klar, dass man so ein Gebilde nur mit hartcodierten, niemals mit variablen Parametern aufrufen kann?



  • Da die Parameterzahl in Wirklichkeit immer gleich zu sein scheint, Paar aus Boolean und Value, würde ich ein Parameterobjekt einführen. Dieses kann dann verschiedene Zustände haben (nicht gesetzt; int; float; string; ...).



  • 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);


Anmelden zum Antworten