From 982f7026342b80ad68c2055d6d92895ba6579e21 Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Fri, 31 May 2024 22:10:44 +0800 Subject: [PATCH 1/6] Implement operator dispatch prototype --- README.md | 55 ++-- proxy.h | 506 ++++++++++++++++++++++++----- tests/CMakeLists.txt | 1 + tests/proxy_creation_tests.cpp | 96 +++--- tests/proxy_dispatch_tests.cpp | 538 +++++++++++++++++++++++++++++++ tests/proxy_invocation_tests.cpp | 52 +-- tests/proxy_reflection_tests.cpp | 26 +- 7 files changed, 1062 insertions(+), 212 deletions(-) create mode 100644 tests/proxy_dispatch_tests.cpp diff --git a/README.md b/README.md index 0c712850..6594e5f9 100644 --- a/README.md +++ b/README.md @@ -22,13 +22,13 @@ The majority of the library is defined in namespace `pro`. Some macros are provi ```cpp // Specifications of abstraction -namespace spec { +PRO_DEF_MEM_DISPATCH(MemDraw, Draw); +PRO_DEF_MEM_DISPATCH(MemArea, Area); -PRO_DEF_MEMBER_DISPATCH(Draw, void(std::ostream& out)); -PRO_DEF_MEMBER_DISPATCH(Area, double() noexcept); -PRO_DEF_FACADE(Drawable, PRO_MAKE_DISPATCH_PACK(Draw, Area)); - -} // namespace spec +struct Drawable : pro::facade_builder + ::add_convention + ::add_convention + ::build {}; // Implementation class Rectangle { @@ -45,7 +45,7 @@ class Rectangle { }; // Client - Consumer -std::string PrintDrawableToString(pro::proxy p) { +std::string PrintDrawableToString(pro::proxy p) { std::stringstream result; result << "shape = "; p.Draw(result); // Polymorphic call @@ -54,11 +54,11 @@ std::string PrintDrawableToString(pro::proxy p) { } // Client - Producer -pro::proxy CreateRectangleAsDrawable(int width, int height) { +pro::proxy CreateRectangleAsDrawable(int width, int height) { Rectangle rect; rect.SetWidth(width); rect.SetHeight(height); - return pro::make_proxy(rect); + return pro::make_proxy(rect); } ``` @@ -66,15 +66,16 @@ Here is another demo showing how to define overloads in a dispatch: just to add ```cpp // Specifications of abstraction -namespace spec { - -PRO_DEF_MEMBER_DISPATCH(Log, void(const char*), void(const char*, const std::exception&)); -PRO_DEF_FACADE(Logger, Log); +PRO_DEF_MEM_DISPATCH(MemLog, Log); -} // namespace spec +struct Logger : pro::facade_builder + ::add_convention + ::build {}; // Client - Consumer -void MyVerboseFunction(pro::proxy logger) { +void MyVerboseFunction(pro::proxy logger) { logger.Log("hello"); try { throw std::runtime_error{"runtime error!"}; @@ -101,27 +102,29 @@ int main() { } ``` -By design, the body of a dispatch could be any code. While member function is one useful pattern supported by macro `PRO_DEF_MEMBER_DISPATCH`, free function is also supported with another macro `PRO_DEF_FREE_DISPATCH`. The following example uses `PRO_DEF_FREE_DISPATCH` and `std::invoke` to implement similar function wrapper as `std::function` and `std::move_only_function` and supports multiple overloads. Note that `.Call` can be omitted when only 1 dispatch is defined in a facade: +By design, the body of a dispatch could be any code. While member function is one useful pattern supported by macro `PRO_DEF_MEM_DISPATCH`, free function and operators are also supported with macros `PRO_DEF_FREE_DISPATCH` and `PRO_DEF_OPERATOR_DISPATCH`. The following example uses `PRO_DEF_OPERATOR_DISPATCH` to implement similar function wrapper as `std::function` and `std::move_only_function` and supports multiple overloads: ```cpp // Specifications of abstraction -namespace spec { +PRO_DEF_OPERATOR_DISPATCH(OpCall, "()"); template -PRO_DEF_FREE_DISPATCH(Call, std::invoke, Overloads...); -template -PRO_DEF_FACADE(MovableCallable, Call); -template -PRO_DEF_FACADE(CopyableCallable, Call, pro::copyable_ptr_constraints); +struct MovableCallable : pro::facade_builder + ::add_convention + ::build {}; -} // namespace spec +template +struct CopyableCallable : pro::facade_builder + ::support_copy + ::add_facade> // Facade inheritance + ::build {}; // MyFunction has similar functionality as std::function but supports multiple overloads // MyMoveOnlyFunction has similar functionality as std::move_only_function but supports multiple overloads template -using MyFunction = pro::proxy>; +using MyFunction = pro::proxy>; template -using MyMoveOnlyFunction = pro::proxy>; +using MyMoveOnlyFunction = pro::proxy>; int main() { auto f = [](auto&&... v) { @@ -139,7 +142,7 @@ int main() { } ``` -Please find more details and discussions in the spec. The complete version of the "drawable" demo could be found in [tests/proxy_integration_tests.cpp](tests/proxy_integration_tests.cpp) (also available on [Compiler Explorer](https://godbolt.org/z/4cK8PPTE1)). +Please find more details and discussions in the spec. The complete version of the "drawable" demo could be found in [tests/proxy_integration_tests.cpp](tests/proxy_integration_tests.cpp) (also available on [Compiler Explorer](https://godbolt.org/z/zro9dd3Eo)). ## Minimum requirements for compilers diff --git a/proxy.h b/proxy.h index 7eac149f..e1a8277e 100644 --- a/proxy.h +++ b/proxy.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,10 @@ struct conditional_traits { static constexpr bool applicable = A; }; using applicable_traits = conditional_traits; using inapplicable_traits = conditional_traits; +template struct lazy_eval_traits : std::type_identity {}; +template +using lazy_eval_t = typename lazy_eval_traits::type; + template