From d67c62b02f9cd8acbdfa0e5e7ae4e62cba6800b4 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 21 Oct 2023 23:42:31 +0200 Subject: [PATCH] Scheduler: solve difficulties with member function signature The approach to provide the ExecutionCtx seems to work out well; after some investigation I found a solution how to code a generic signature-check for "any kind of function-like member"... (the trick is to pass a pointer or member-pointer, which happens to be syntactically the same and can be handled with our existing function signature helper after some minor tweaks) --- research/try.cpp | 173 ++++++++++++++++----------------- src/lib/meta/duck-detector.hpp | 2 +- src/lib/meta/function.hpp | 41 +++++++- src/vault/gear/activity.hpp | 26 ++--- wiki/thinkPad.ichthyo.mm | 159 ++++++++++++++++++++++++++++-- 5 files changed, 286 insertions(+), 115 deletions(-) diff --git a/research/try.cpp b/research/try.cpp index 4dc56f707..c4ca20f0f 100644 --- a/research/try.cpp +++ b/research/try.cpp @@ -46,17 +46,15 @@ // 03/20 - investigate type deduction bug with PtrDerefIter // 01/21 - look for ways to detect the presence of an (possibly inherited) getID() function // 08/22 - techniques to supply additional feature selectors to a constructor call +// 10/23 - search for ways to detect signatures of member functions and functors uniformly /** @file try.cpp - * Investigate techniques to supply additional descriptive ctor arguments in a type safe way. - * The idea is to provide friend functors, which might tweak or reset internal settings; - * these functors are packaged into free standing friend functions with intuitive naming, - * which, on call-site, look like algebraic expressions/data-types. - * - * If desired, this mechanism can be mixed-in and integrated into a constructor call, - * thus optionally allowing for arbitrary extra qualifiers, even with extra arguments. - * @see builder-qualifier-support.hpp (wrapped as support lib) + * Investigate how to detect the signature of a _function-like member,_ irrespective + * if referring to a static function, a member function or a functor member. Turns out this + * can be achieved in a syntactically uniform way by passing either a pointer or member pointer. + * @see vault::gear::_verify_usable_as_ExecutionContext + * @see lib::meta::isFunMember */ typedef unsigned int uint; @@ -67,100 +65,101 @@ typedef unsigned int uint; #include "lib/test/diagnostic-output.hpp" #include "lib/util.hpp" -#include +#include "lib/meta/function.hpp" - - - -template -class PropertyQualifierSupport +struct Stat { - protected: - using Manipulator = std::function; - - struct Qualifier - : Manipulator - { - using Manipulator::Manipulator; - }; + static long fun (double, char*) {return 42; } + }; - template - friend void qualify(TAR& target, Qualifier& qualifier, QUALS& ...qs) +struct Funi + { + std::function fun; + short gun; + }; + +struct Dyna + { + long fun (double, char*) const {return 42; } + }; + + +using lib::meta::_Fun; + + + /** @deprecated this is effectively the same than using decltype */ + template + struct Probe + : _Fun

{ - qualifier(target); - qualify(target, qs...); - } - - friend void qualify(TAR&){ } + Probe(P&&){} + }; - public: - // default construct and copyable - }; + template()> + struct has_SIGx + : std::is_same::Sig> + { +// has_SIGx() = default; +// has_SIGx(FUN, _Fun){ } + }; - -class Feat - : PropertyQualifierSupport + template + struct has_SIGx + : std::false_type + { +// has_SIGx() = default; +// has_SIGx(FUN, _Fun){ } + }; + + + + template + constexpr inline auto + isFunMember (FUN) { - - friend Qualifier bla(); - friend Qualifier blubb(string); - - public: - Feat() = default; - - template - Feat(Qualifier qual, QS... qs) - : Feat{} - { - qualify(*this, qual, qs...); - } - - operator string () const - { - return "Feat{"+prop_+"}"; - } - - private: - string prop_{"∅"}; - }; - - -Feat::Qualifier -bla() -{ - return Feat::Qualifier{[](Feat& feat) - { - feat.prop_ = "bla"; - }}; -} - -Feat::Qualifier -blubb(string murks) -{ - return Feat::Qualifier{[=](Feat& feat) - { - feat.prop_ += ".blubb("+murks+")"; - }}; -} - + return has_SIGx{}; + } + +#define ARSERT_MEMBER_FUNCTOR(_EXPR_, _SIG_) \ + static_assert (isFunMember<_SIG_>(_EXPR_), \ + "Member " STRINGIFY(_EXPR_) " unsuitable, expect function signature: " STRINGIFY(_SIG_)); int main (int, char**) { - Feat f0; - SHOW_EXPR(f0); + using F1 = decltype(Stat::fun); + using F2 = decltype(Funi::fun); + using F3 = decltype(&Dyna::fun); - Feat f1(bla()); - SHOW_EXPR(f1); + SHOW_TYPE(F1) + SHOW_TYPE(F2) + SHOW_TYPE(F3) - Feat f2(blubb("Ψ")); - SHOW_EXPR(f2); + using F1a = decltype(&Stat::fun); + using F2a = decltype(&Funi::fun); + using F2b = decltype(&Funi::gun); + SHOW_TYPE(F1a) + SHOW_TYPE(F2a) + SHOW_TYPE(F2b) - Feat f3(bla(),blubb("↯")); - SHOW_EXPR(f3); + SHOW_TYPE(_Fun::Sig) + SHOW_TYPE(_Fun::Sig) + SHOW_TYPE(_Fun::Sig) + + SHOW_TYPE(_Fun::Sig) + SHOW_TYPE(_Fun::Sig) - Feat f4(blubb("💡"), bla()); // Note: evaluated from left to right, bla() overwrites prop - SHOW_EXPR(f4); + SHOW_EXPR(_Fun::value) + SHOW_EXPR(_Fun::value) + cout << "\n--------\n"; + + SHOW_EXPR(bool(isFunMember(&Stat::fun))) + SHOW_EXPR(bool(isFunMember(&Funi::fun))) + SHOW_EXPR(bool(isFunMember(&Funi::gun))) + SHOW_EXPR(bool(isFunMember(&Dyna::fun))) + + ARSERT_MEMBER_FUNCTOR (&Stat::fun, long(double,char*)); + ARSERT_MEMBER_FUNCTOR (&Dyna::fun, long(double,char*)); cout << "\n.gulp.\n"; return 0; diff --git a/src/lib/meta/duck-detector.hpp b/src/lib/meta/duck-detector.hpp index c7398cda8..a3fef919f 100644 --- a/src/lib/meta/duck-detector.hpp +++ b/src/lib/meta/duck-detector.hpp @@ -164,7 +164,7 @@ * Defines a metafunction (template), allowing to detect * the presence of a member function with the specific * signature, as defined by the parameters. - * @note this check is not sensible to overloads, + * @note this check is not sensitive to overloads, * due to the explicitly given argument types */ #define META_DETECT_FUNCTION(_RET_TYPE_,_FUN_NAME_,_ARGS_) \ diff --git a/src/lib/meta/function.hpp b/src/lib/meta/function.hpp index c64be5f23..db71df0dd 100644 --- a/src/lib/meta/function.hpp +++ b/src/lib/meta/function.hpp @@ -175,6 +175,12 @@ namespace meta{ : _Fun { }; + /** allow also to probe _plain member fields,_ which may hold a functor */ + template + struct _Fun + : _Fun + { }; + @@ -187,11 +193,17 @@ namespace meta{ * or std::function instance, or λ instance or language function * reference or function pointer */ - template + template()> struct has_Sig : std::is_same::Sig> { }; + /** catch-all to prevent compilation failure for anything not function-like. */ + template + struct has_Sig + : std::false_type + { }; + /** * Macro for a compile-time check to verify the given * generic functors or lambdas expose some expected signature. @@ -203,6 +215,33 @@ namespace meta{ + /** + * Helper to pick up a member field for verification + * @tparam SIG signature of the _function like_ entity expected + * @tparam FUN address- or member-pointer, e.g. `&Class::member` + * @return suitably parametrised \ref has_Sig instance (which is bool convertible) + * @remark intended for use with generic types, when expecting a _somehow invokable_ + * member, irrespective if a static function, member function or functor object + */ + template + constexpr inline auto + isFunMember (FUN) + { + return has_Sig{}; + } + + /** + * Macro for a compile-time check to verify some member is present + * and comprises something invokable with a specific signature. + * @remark typically used with _generic types_ or bindings + */ +#define ASSERT_MEMBER_FUNCTOR(_EXPR_, _SIG_) \ + static_assert (lib::meta::isFunMember<_SIG_>(_EXPR_), \ + "Member " STRINGIFY(_EXPR_) " unsuitable, expect function signature: " STRINGIFY(_SIG_)); + + + + /** Placeholder marker for a special argument position to be supplied later */ diff --git a/src/vault/gear/activity.hpp b/src/vault/gear/activity.hpp index 1f74e1174..54cc6a81b 100644 --- a/src/vault/gear/activity.hpp +++ b/src/vault/gear/activity.hpp @@ -182,21 +182,13 @@ namespace gear { constexpr void _verify_usable_as_ExecutionContext () { -#define ASSERT_MEMBER_FUNCTOR(_EXPR_, _SIG_) \ - static_assert (lib::meta::has_Sig(), \ - "Execution-Context: " STRINGIFY(_EXPR_) " expect function with signature: " STRINGIFY(_SIG_)); + ASSERT_MEMBER_FUNCTOR (&EXE::post, Proc(Time, Activity&, EXE&)); + ASSERT_MEMBER_FUNCTOR (&EXE::work, void(Time, size_t)); + ASSERT_MEMBER_FUNCTOR (&EXE::done, void(Time, size_t)); + ASSERT_MEMBER_FUNCTOR (&EXE::tick, Proc(Time)); - - ASSERT_MEMBER_FUNCTOR (EXE::post, Proc(Time, Activity&, EXE&)); - ASSERT_MEMBER_FUNCTOR (EXE::work, void(Time, size_t)); - ASSERT_MEMBER_FUNCTOR (EXE::done, void(Time, size_t)); - ASSERT_MEMBER_FUNCTOR (EXE::tick, Proc(Time)); - - ASSERT_MEMBER_FUNCTOR (EXE::getWaitDelay, Offset()); - ASSERT_MEMBER_FUNCTOR (EXE::getSchedTime, Time()); - - -#undef ASSERT_MEMBER_FUNCTOR + ASSERT_MEMBER_FUNCTOR (&EXE::getWaitDelay, Offset()); + ASSERT_MEMBER_FUNCTOR (&EXE::getSchedTime, Time()); } }//(End)namespace activity @@ -604,7 +596,7 @@ namespace gear { activity::Proc Activity::activate (Time now, EXE& executionCtx) { -// activity::_verify_usable_as_ExecutionContext(); + activity::_verify_usable_as_ExecutionContext(); switch (verb_) { case INVOKE: @@ -651,7 +643,7 @@ namespace gear { activity::Proc Activity::dispatch (Time now, EXE& executionCtx) { -// activity::_verify_usable_as_ExecutionContext(); + activity::_verify_usable_as_ExecutionContext(); switch (verb_) { case NOTIFY: @@ -681,7 +673,7 @@ namespace gear { activity::Proc Activity::notify (Time now, EXE& executionCtx) { -// activity::_verify_usable_as_ExecutionContext(); + activity::_verify_usable_as_ExecutionContext(); switch (verb_) { case GATE: diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 960b40ee6..06ac81e2d 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -57321,6 +57321,121 @@ + + + + + + + + + + +

+ damit kann man von jedem »Invocable« eine Signatur abgreifen +

+ + + + + + + + + + + + + + + + + + + + + + + + + +

+ ...denn man verwendet diese Metafunktion ja stets explizit mit einem gegebenen Typ, und im Falle eines Ausdrucks muß man noch einen decltype() darum wickeln. Mögliche Probleme: +

+
    +
  • + der Ausdruck in decltype() ist syntaktisch gar nicht valide (z.B. Scope::member  bei einer Member-Funktion) +
  • +
  • + der sich ergebende Typ ist keine Funktion, und deshalb sind die nested-typedefs (Sig, Args...) nicht vorhanden ⟹ compile Fehler bzw. SFINAE +
  • +
+ +
+ + + + + + + + + + + + + + +

+ Beispiel: explore(elements).transform(....irgendwas....) +

+ +
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + +

+ has_Sig sollte keine Compile-Fehler auslösen, wenn der gegebene Typ überhaupt nicht Funktions-artig ist (⟹ denn dann ist die Aussage trivialer Weise wiederlegt; was gar keine Signatur hat, kann auch nicht eine bestimmte Signatur haben). Realisieren kann man das über den bool-Check, den ich vor einiger Zeit bereits in lib::meta::_Fun eingebaut habe (im Zusammenhang mit IterExplorer) +

+ +
+
+ + + + + + +

+ Warum Macro? damit man per STRINGIFY() einen lesbareren Hinweis in die Static-Assertion bekommt +

+ +
+
+
+
+
@@ -80285,7 +80400,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -80297,9 +80412,39 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
...also solange ich nur mit Unit-Tests gearbeitet habe

+
+
+ + + + + + +
    +
  • + von einer statischen Funktion kan man direkt den Typ aufgreifen, qualifiziert durch den Scope: decltype( Scope::fun ) +
  • +
  • + von einem regulären Member-Function kann man den Typ nach einem Pseudo-Zugriff erfassen: decltype( std::declval<Scope>().fun ) +
  • +
  • + bei einem Functor-Member geht das nicht, aber man kann hier entweder statisch qualifzieren (wie 1.Fall), oder einen Pointer nehmen: decltype( &Scope::fun ) +
  • +
+
+ + + + + + + + + +
@@ -80320,8 +80465,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
der Scheduler-Service überhaupt

- - + @@ -80331,8 +80475,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
...nämlich die Fähigkeit, einen Activity-chain zu einem geplanten Zeitpunkt oder auf Signal hin auszuführen — und diese Fähigkeit muß selbstverständlich der Sprache selber zu Gebote stehen, damit sie komplexe Aktionsmuster flexibel ausdrücken kann

- -
+
@@ -81876,8 +82019,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
es genügte, an diesen Stellen die Ausführung der abstrahierten Aktionen zu loggen

- - +
@@ -81896,8 +82038,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
der Scheduler selber kann diese Rolle generisch übernehmen

- - +