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 einstd::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 einstatic_cast<std::uint64_t>(object.method())
wenn du denstd::uint64_t
-Wert auslesen willst. Das erlaubt ein bisschen mehr Freiheiten, wieT::method()
implementiert wird. Natürlich nur, wenn du keine andere, gleichnamige Funktion aufrufen willst, falls es sich z.B. um ein (nachstd::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 nochconst
/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 Typdecltype(<expression>)
geprüft, wobei cv-Qualifikationen entfernt werden, so dass eigentlich auchremove_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 mirconvertible_to
+ Cast in meinen gewünschten Typ erstmal immer Default. Aber es kann sein, dass du z.B. eineint
-Methode anders behandeln möchtest, dann macht das natürlich so Sinn (Edit: Hah! Grad gemerkt, dass ich jetzt schon zum dritten Mal mitconvertible_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 mirconvertible_to
+ Cast in meinen gewünschten Typ erstmal immer Default. Aber es kann sein, dass du z.B. eineint
-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.