METAProgramming: Rückgabewert einer Methode absichern



  • Hab das jetzt so übernommen. Concepts gefällt mir. Endlich mal ein schöner Anwendungsfall dafür 🙂

    Kann mir jemand noch sagen, ob man in einem Concept auch mit "or"-Verknüpfungen arbeiten kann, wenn man z.B. einen von zwei unterschiedlichen Typen erzwingen will ?



  • Man kann die Clauses im require statement mit && oder || verknüpfen.

    Mit require wird vieles beim Template Metaprogramming erheblich simpler zu formulieren und geradliniger zu implementieren.



  • Für Beispiele siehe z.b. https://en.cppreference.com/w/cpp/language/constraints
    Conjunctions (&&) und Disjunctions(||)



  • Ah. Der Begriff "Conjunction" hat mir gefehlt zum Googeln 😃

    Ich hatte es auf diese Art versucht:

        template <typename T>
        concept isvalidtype = requires(const T &object)
        {
            { object.method() } -> ( std::same_as<std::uint64_t> || std::same_as<std::string> );
        };
    

    Aber dann mach ichs halt auf die andere Art 🙂



  • Ich habe es jetzt ungefähr so:

    
    
        template <typename T>
        concept is_valid_method1 = requires(const T &object)
        {
            { object.method1() } -> std::same_as<std::uint64_t>;
        };
    
        template <typename T>
        concept is_float_method2 = requires(const T &object)
        {
            { object.method2() } -> std::floating_point;
        };
    
        template <typename T>
        concept is_string_method2 = requires(const T &object)
        {
            { object.method2() } -> std::same_as<std::string>;
        };
    
    
        template <typename T>
        concept isvalidtype = ( is_valid_method1<T> && ( is_float_method2<T> || is_string_method2<T> ) );
    

    Ein finales "Concept" welches sich aus einzelnen zusammensetzt. Würde man das so machen, oder geht das noch kürzer ohne die lesbarkeit zu verlieren?



  •     template <typename T>
        concept ValidValueType = std::floating_point<T> || std::same_as<T,std::string>;
    
        template <typename T>
        concept is_val = requires(const T &object)
        {
            { object.method1() } -> std::same_as<std::uint64_t>;
            { object.method2() } -> ValidValueType;
        };
    

    Ok so sieht es schon besser aus.



  • @It0101 Eventuell ist std::same_as<std::uint64_t> ein wenig zu restriktiv, das gilt z.B. nicht, wenn die Methode ein std::uint64_t-Referenz zurückgibt. Das könnte man mit so einem Concept beheben (dass es in der Form glaube ich nicht in der Standardbibliothek gibt):

    template <typename T, typename U>
    concept same_as_without_cvref = std::same_as<std::remove_cvref_t<T>,  std::remove_cvref_t<U>>;
    

    Oder aber - was ich in solchen Fällen persönlich bevorzuge - du prüfst auf std::convertible_to<std::uint64_t> und machst ein static_cast<std::uint64_t>(object.method()) wenn du den std::uint64_t-Wert auslesen willst. Das erlaubt ein bisschen mehr Freiheiten, wie T::method() implementiert wird. Natürlich nur, wenn du keine andere, gleichnamige Funktion aufrufen willst, falls es sich z.B. um ein (nach std::uint64_t konvertierbaren) std::uint32_t handelt und das zu Mehrdeutigkeiten führen würde.

    Was hier wirklich Sinn macht, solltest du jedoch letztendlich selbst wissen 🙂



  • @Finnegan sagte in METAProgramming: Rückgabewert einer Methode absichern:

    same_as_without_cvref

    Auf genau das Problem bin ich auch gestoßen. Letztendlich geht es mir ja nur um den Wert selbst. (const-)referenzen sind ebenfalls zulässig.

    Ich war erst bei "std::remove_reference" was aber irgendwie nicht gefruchtet hat. Evtl. hab ich es auch falsch eingesetzt. Danke für dein Beispiel. 😁



  • @It0101 sagte in METAProgramming: Rückgabewert einer Methode absichern:

    @Finnegan sagte in METAProgramming: Rückgabewert einer Methode absichern:

    same_as_without_cvref

    Auf genau das Problem bin ich auch gestoßen. Letztendlich geht es mir ja nur um den Wert selbst. (const-)referenzen sind ebenfalls zulässig.

    Ich war erst bei "std::remove_reference" was aber irgendwie nicht gefruchtet hat. Evtl. hab ich es auch falsch eingesetzt. Danke für dein Beispiel. 😁

    std::remove_cvref_t entfernt auch noch const/volatile, so dass nur noch der nackte Type übrig bleibt. is_same macht auch bei unterschiedlichen cv-Qualifikationen einen Unterschied, allerdings wird im Type Constraint (nach dem ->) der Typ decltype(<expression>)
    geprüft, wobei cv-Qualifikationen entfernt werden, so dass eigentlich auch remove_reference_t reichen sollte.

    Ansonsten: convertible_to ist zu liberal für deine Anforderungen? Ich kenn den Rest des Codes nicht, aber bei solchen Dingen wäre bei mir convertible_to + Cast in meinen gewünschten Typ erstmal immer Default. Aber es kann sein, dass du z.B. eine int-Methode anders behandeln möchtest, dann macht das natürlich so Sinn (Edit: Hah! Grad gemerkt, dass ich jetzt schon zum dritten Mal mit convertible_to rumnerve, das wird wohl tatsächlich einen guten Grund haben, dass du das nicht verwendest... sorry 🙂 ).



  • @Finnegan sagte in METAProgramming: Rückgabewert einer Methode absichern:

    Ansonsten: convertible_to ist zu liberal für deine Anforderungen? Ich kenn den Rest des Codes nicht, aber bei solchen Dingen wäre bei mir convertible_to + Cast in meinen gewünschten Typ erstmal immer Default. Aber es kann sein, dass du z.B. eine int-Methode anders behandeln möchtest, dann macht das natürlich so Sinn.

    Ja genau. Ich möchte explizit einen 64Bit-Typ erzwingen. Es handelt sich in meinem Anwendungsfall um einen UTC-Timestamp in Millisekunden seit Epoche, daher möchte ich, um Fehler durch den Nutzer zu vermeiden, direkt den großen Typ erzwingen, den ich auch intern verwende. Daher fällt "convertible_to" raus. 🙂
    Aber dennoch danke für den Hinweis. 😉


Anmelden zum Antworten