{"id":34567,"date":"2024-09-02T14:52:50","date_gmt":"2024-09-02T14:52:50","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/cppblog\/?p=34567"},"modified":"2024-09-02T14:52:50","modified_gmt":"2024-09-02T14:52:50","slug":"announcing-the-proxy-3-library-for-dynamic-polymorphism","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cppblog\/announcing-the-proxy-3-library-for-dynamic-polymorphism\/","title":{"rendered":"Announcing the Proxy 3 Library for Dynamic Polymorphism"},"content":{"rendered":"<p>We are thrilled to announce that <a href=\"https:\/\/github.com\/microsoft\/proxy\">Proxy 3<\/a>, our latest and greatest solution for polymorphism in C++, is now <a href=\"https:\/\/github.com\/microsoft\/proxy\/releases\/tag\/3.0.0-rc1\">feature complete<\/a>! Since the library was <a href=\"https:\/\/devblogs.microsoft.com\/cppblog\/proxy-runtime-polymorphism-made-easier-than-ever\/\">initially open-sourced<\/a>, we have heard much positive feedback and received many brilliant feature requests. Big thanks to all of you who have contributed any code or idea that made the library better!<\/p>\n<h2>Our Mission<\/h2>\n<p>&#8220;Proxy&#8221; is a modern C++ library that helps you use polymorphism (a way to use different types of objects interchangeably) without needing inheritance.<\/p>\n<p>&#8220;Proxy&#8221; was created by Microsoft engineers and has been used in the Windows operating system since 2022. For many years, using inheritance was the main way to achieve polymorphism in C++. However, new programming languages like <a href=\"https:\/\/doc.rust-lang.org\/book\/ch10-02-traits.html\">Rust<\/a> offer better ways to do this. We have improved our understanding of object-oriented programming and decided to use <em>pointers<\/em> in C++ as the foundation for &#8220;Proxy&#8221;. Specifically, the &#8220;Proxy&#8221; library is designed to be:<\/p>\n<ul>\n<li><strong>Portable<\/strong>: &#8220;Proxy&#8221; was implemented as a single-header library in standard C++20. It can be used on any platform while the compiler supports C++20. The majority of the library is <a href=\"https:\/\/learn.microsoft.com\/cpp\/standard-library\/cpp-standard-library-overview\">freestanding<\/a>, making it feasible for embedded engineering or kernel design of an operating system.<\/li>\n<li><strong>Non-intrusive<\/strong>: An implementation type is no longer required to inherit from an abstract binding.<\/li>\n<li><strong>Well-managed<\/strong>: &#8220;Proxy&#8221; provides a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Garbage_collection_(computer_science)\">GC<\/a>-like capability that manages the lifetimes of different objects efficiently without the need for an actual garbage collector.<\/li>\n<li><strong>Fast<\/strong>: With typical compiler optimizations, &#8220;Proxy&#8221; produces high-quality code that is as good as or better than hand-written code. In many cases, &#8220;Proxy&#8221; performs better than traditional inheritance-based approaches, especially in managing the lifetimes of objects.<\/li>\n<li><strong>Accessible<\/strong>: Learned from user feedback, accessibility has been significantly improved in &#8220;Proxy 3&#8221; with intuitive syntax, good IDE compatibility, and accurate diagnostics.<\/li>\n<li><strong>Flexible<\/strong>: Not only member functions, the &#8220;abstraction&#8221; of &#8220;Proxy&#8221; allows <em>any<\/em> expression to be polymorphic, including free functions, operators, conversions, etc. Different abstractions can be freely composed on demand. Performance tuning is supported for experts to balance between extensibility and performance.<\/li>\n<\/ul>\n<h2>The New Look<\/h2>\n<p>Comparing to our previous releases, there are some major improvements in the syntax and utilities. Let&#8217;s get started with some examples!<\/p>\n<h3>Hello World<\/h3>\n<pre><code class=\"language-cpp\">#include &lt;iostream&gt;\r\n#include &lt;string&gt;\r\n\r\n#include \"proxy.h\"\r\n\r\nstruct Streamable : pro::facade_builder\r\n    ::add_convention&lt;pro::operator_dispatch&lt;\"&lt;&lt;\", true&gt;, std::ostream&amp;(std::ostream&amp; out) const&gt;\r\n    ::build {};\r\n\r\nint main() {\r\n  std::string str = \"Hello World\";\r\n  pro::proxy&lt;Streamable&gt; p1 = &amp;str;\r\n  std::cout &lt;&lt; \"p1 = \" &lt;&lt; *p1 &lt;&lt; \"\\n\";  \/\/ Prints: \"p1 = Hello World\"\r\n\r\n  pro::proxy&lt;Streamable&gt; p2 = std::make_unique&lt;int&gt;(123);\r\n  std::cout &lt;&lt; \"p2 = \" &lt;&lt; *p2 &lt;&lt; \"\\n\";  \/\/ Prints: \"p2 = 123\"\r\n\r\n  pro::proxy&lt;Streamable&gt; p3 = pro::make_proxy&lt;Streamable&gt;(3.14);\r\n  std::cout &lt;&lt; \"p3 = \" &lt;&lt; *p3 &lt;&lt; \"\\n\";  \/\/ Prints: \"p3 = 3.14\"\r\n}<\/code><\/pre>\n<p>Here is a step-by-step explanation:<\/p>\n<ul>\n<li><code>#include &lt;iostream&gt;<\/code>: For <a href=\"https:\/\/learn.microsoft.com\/cpp\/standard-library\/iostream#cout\"><code>std::cout<\/code><\/a>.<\/li>\n<li><code>#include &lt;string&gt;<\/code>: For <a href=\"https:\/\/learn.microsoft.com\/cpp\/standard-library\/basic-string-class\"><code>std::string<\/code><\/a>.<\/li>\n<li><code>#include \"proxy.h\"<\/code>: For the &#8220;Proxy&#8221; library. Most of the facilities of the library are defined in namespace <code>pro<\/code>. If the library is consumed via <a href=\"https:\/\/learn.microsoft.com\/vcpkg\/get_started\/overview\">vcpkg<\/a> or <a href=\"https:\/\/conan.io\/\">conan<\/a>, this line should be changed into <code>#include &lt;proxy\/proxy.h&gt;<\/code>.<\/li>\n<li><code>struct Streamable : pro::facade_builder ... ::build {}<\/code>: Defines a facade type <code>Streamable<\/code>. The term &#8220;facade&#8221; is how the &#8220;Proxy&#8221; library models runtime abstraction. Specifically,\n<ul>\n<li><code>pro::facade_builder<\/code>: Provides capability to build a facade type at compile-time.<\/li>\n<li><code>add_convention<\/code>: Adds a generalized &#8220;calling convention&#8221;, defined by a &#8220;dispatch&#8221; and several &#8220;overloads&#8221;, to the build context.<\/li>\n<li><code>pro::operator_dispatch&lt;\"&lt;&lt;\", true&gt;<\/code>: Specifies a dispatch for operator <code>&lt;&lt;<\/code> expressions where the primary operand (<code>proxy<\/code>) is on the right-hand side (specified by the second template parameter <code>true<\/code>). Note that polymorphism in the &#8220;Proxy&#8221; library is defined by expressions rather than member functions, which is different from C++ virtual functions or other OOP languages.<\/li>\n<li><code>std::ostream&amp;(std::ostream&amp; out) const<\/code>: The signature of the calling convention, similar with <a href=\"https:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/2021\/p0288r9.html\"><code>std::move_only_function<\/code><\/a>. <code>const<\/code> specifies that the primary operand is <code>const<\/code>.<\/li>\n<li><code>build<\/code>: Builds the context into a facade type.<\/li>\n<\/ul>\n<\/li>\n<li><code>pro::proxy&lt;Streamable&gt; p1 = &amp;str<\/code>: Creates a <code>proxy<\/code> object from a raw pointer of <code>std::string<\/code>. <code>p1<\/code> behaves like a raw pointer, and does not have ownership of the underlying <code>std::string<\/code>. If the lifetime of <code>str<\/code> ends before <code>p1<\/code>, <code>p1<\/code> becomes dangling.<\/li>\n<li><code>std::cout &lt;&lt; *p1<\/code>: This is how it works. It prints &#8220;Hello World&#8221; because the calling convention is defined in the facade <code>Streamable<\/code>, so it works as if by calling <code>std::cout &lt;&lt; str<\/code>.<\/li>\n<li><code>pro::proxy&lt;Streamable&gt; p2 =<\/code><a href=\"https:\/\/learn.microsoft.com\/cpp\/standard-library\/memory-functions#make_unique\"><code>std::make_unique<\/code><\/a><code>&lt;int&gt;(123)<\/code>: Creates a <a href=\"https:\/\/learn.microsoft.com\/cpp\/standard-library\/unique-ptr-class\"><code>std::unique_ptr<\/code><\/a><code>&lt;int&gt;<\/code> and converts to a <code>proxy<\/code>. Different from <code>p1<\/code>, <code>p2<\/code> has ownership of the underlying <code>int<\/code> because it is instantiated from a value of <code>std::unique_ptr<\/code>, and will call the destructor of <code>std::unique_ptr<\/code> when <code>p2<\/code> is destroyed, while <code>p1<\/code> does not have ownership of the underlying <code>int<\/code> because it is instantiated from a raw pointer. <code>p1<\/code> and <code>p2<\/code> are of the same type <code>pro::proxy&lt;Streamable&gt;<\/code>, which means you can have a function that returns <code>pro::proxy&lt;Streamable&gt;<\/code> without exposing any information about the implementation details to its caller.<\/li>\n<li><code>std::cout &lt;&lt; *p2<\/code>: Prints &#8220;123&#8221; with no surprise.<\/li>\n<li><code>pro::proxy&lt;Streamable&gt; p3 = pro::make_proxy&lt;Streamable&gt;(3.14)<\/code>: Creates a <code>proxy<\/code> from a <code>double<\/code> without specifying the underlying pointer type. Specifically,\n<ul>\n<li>Similar with <code>p2<\/code>, <code>p3<\/code> also has ownership of the underlying <code>double<\/code> value, but can effectively avoid heap allocation.<\/li>\n<li>Since the size of the underlying type (<code>double<\/code>) is known to be small (on major 32- or 64-bit platforms), <code>pro::make_proxy<\/code> realizes the fact at compile-time and guarantees no heap allocation.<\/li>\n<li>Library &#8220;Proxy&#8221; explicitly defines when heap allocation occurs or not to avoid users falling into performance hell, which is different from <a href=\"https:\/\/learn.microsoft.com\/cpp\/standard-library\/function-class\"><code>std::function<\/code><\/a> and other existing polymorphic wrappers in the standard.<\/li>\n<\/ul>\n<\/li>\n<li><code>std::cout &lt;&lt; *p3<\/code>: Prints &#8220;3.14&#8221; with no surprise.<\/li>\n<li>When <code>main<\/code> returns, <code>p2<\/code> and <code>p3<\/code> will destroy the underlying objects, while <code>p1<\/code> does nothing because it holds a raw pointer that does not have ownership of the underlying <code>std::string<\/code>.<\/li>\n<\/ul>\n<h3>More Expressions<\/h3>\n<p>In addition to the operator expressions demonstrated in the previous example, the library supports almost all forms of expressions in C++ and can make them polymorphic. Specifically,<\/p>\n<ul>\n<li>The <code>PRO_DEF_MEM_DISPATCH<\/code> macro: Defines a dispatch type for member function call expressions.<\/li>\n<li>The <code>PRO_DEF_FREE_DISPATCH<\/code> macro: Defines a dispatch type for free function call expressions.<\/li>\n<li>The <code>pro::operator_dispatch<\/code> class template: Dispatch type for operator expressions.<\/li>\n<li>The <code>pro::conversion_dispatch<\/code> class template: Dispatch type for conversion expressions.<\/li>\n<\/ul>\n<p>Note that some facilities are provided as macros, because C++ templates today do not support generating a function with an arbitrary name. Here is another example that makes member function call expressions polymorphic:<\/p>\n<pre><code class=\"language-cpp\">#include &lt;iostream&gt;\r\n#include &lt;sstream&gt;\r\n\r\n#include \"proxy.h\"\r\n\r\nPRO_DEF_MEM_DISPATCH(MemDraw, Draw);\r\nPRO_DEF_MEM_DISPATCH(MemArea, Area);\r\n\r\nstruct Drawable : pro::facade_builder\r\n    ::add_convention&lt;MemDraw, void(std::ostream&amp; output)&gt;\r\n    ::add_convention&lt;MemArea, double() noexcept&gt;\r\n    ::support_copy&lt;pro::constraint_level::nontrivial&gt;\r\n    ::build {};\r\n\r\nclass Rectangle {\r\n public:\r\n  Rectangle(double width, double height) : width_(width), height_(height) {}\r\n  Rectangle(const Rectangle&amp;) = default;\r\n\r\n  void Draw(std::ostream&amp; out) const {\r\n    out &lt;&lt; \"{Rectangle: width = \" &lt;&lt; width_ &lt;&lt; \", height = \" &lt;&lt; height_ &lt;&lt; \"}\";\r\n  }\r\n  double Area() const noexcept { return width_ * height_; }\r\n\r\n private:\r\n  double width_;\r\n  double height_;\r\n};\r\n\r\nstd::string PrintDrawableToString(pro::proxy&lt;Drawable&gt; p) {\r\n  std::stringstream result;\r\n  result &lt;&lt; \"entity = \";\r\n  p-&gt;Draw(result);\r\n  result &lt;&lt; \", area = \" &lt;&lt; p-&gt;Area();\r\n  return std::move(result).str();\r\n}\r\n\r\nint main() {\r\n  pro::proxy&lt;Drawable&gt; p = pro::make_proxy&lt;Drawable, Rectangle&gt;(3, 5);\r\n  std::string str = PrintDrawableToString(p);\r\n  std::cout &lt;&lt; str &lt;&lt; \"\\n\";  \/\/ Prints: \"entity = {Rectangle: width = 3, height = 5}, area = 15\"\r\n}<\/code><\/pre>\n<p>Here is a step-by-step explanation:<\/p>\n<ul>\n<li><code>#include &lt;iostream&gt;<\/code>: For <a href=\"https:\/\/learn.microsoft.com\/cpp\/standard-library\/iostream#cout\"><code>std::cout<\/code><\/a>.<\/li>\n<li><code>#include &lt;sstream&gt;<\/code>: For <a href=\"https:\/\/learn.microsoft.com\/cpp\/standard-library\/basic-stringstream-class\"><code>std::stringstream<\/code><\/a>.<\/li>\n<li><code>#include \"proxy.h\"<\/code>: For the &#8220;Proxy&#8221; library.<\/li>\n<li><code>PRO_DEF_MEM_DISPATCH(MemDraw, Draw)<\/code>: Defines a dispatch type <code>MemDraw<\/code> for expressions of calling member function <code>Draw<\/code>.<\/li>\n<li><code>PRO_DEF_MEM_DISPATCH(MemArea, Area)<\/code>: Defines a dispatch type <code>MemArea<\/code> for expressions of calling member function <code>Area<\/code>.<\/li>\n<li><code>struct Drawable : pro::facade_builder ... ::build {}<\/code>: Defines a facade type <code>Drawable<\/code>. Specifically,\n<ul>\n<li><code>add_convention<\/code>: Adds calling conventions to the build context.<\/li>\n<li><code>support_copy&lt;pro::constraint_level::nontrivial&gt;<\/code>: Specifies the underlying pointer type shall be copyable, which also makes the resulting <code>proxy<\/code> type copyable.<\/li>\n<\/ul>\n<\/li>\n<li><code>class Rectangle<\/code>: An implementation of <code>Drawable<\/code>.<\/li>\n<li>Function <code>PrintDrawableToString<\/code>: Converts a <code>Drawable<\/code> into a <code>std::string<\/code>. Note that this is a function rather than a function template, which means it can generate <a href=\"https:\/\/en.wikipedia.org\/wiki\/Application_binary_interface\">ABI<\/a> in a larger build system.<\/li>\n<li><code>pro::proxy&lt;Drawable&gt; p = pro::make_proxy&lt;Drawable, Rectangle&gt;(3, 5)<\/code>: Creates a <code>proxy&lt;Drawable&gt;<\/code> object containing a <code>Rectangle<\/code>.<\/li>\n<li><code>std::string str = PrintDrawableToString(p)<\/code>: Converts <code>p<\/code> into a <code>std::string<\/code>, implicitly creates a copy of <code>p<\/code>.<\/li>\n<li><code>std::cout &lt;&lt; str<\/code>: Prints the string.<\/li>\n<\/ul>\n<h3>Other Useful Features<\/h3>\n<p>In addition to the features mentioned above, here is a curated list of the most popular features based on user feedback:<\/p>\n<ul>\n<li><strong>Overloading<\/strong>: <code>facade_builder::add_convention<\/code> is more powerful than demonstrated above. It can take any number of overload types and perform standard overload resolution when invoking a <code>proxy<\/code>.<\/li>\n<li><strong>Facade composition<\/strong>: <code>facade_builder::add_facade<\/code> allows flexible composition of different abstractions.<\/li>\n<li><strong>Weak dispatch<\/strong>: When an object does not implement a convention, and we do not want it to trigger a hard compile error, it is allowed to define a &#8220;weak dispatch&#8221; with macro <code>PRO_DEF_WEAK_DISPATCH<\/code> from an existing dispatch type and a default implementation.<\/li>\n<li><strong>Allocator awareness<\/strong>: Function template <code>allocate_proxy<\/code> is able to create a <code>proxy<\/code> from a value with any custom allocator. In C++11, <a href=\"https:\/\/learn.microsoft.com\/cpp\/standard-library\/function-class\"><code>std::function<\/code><\/a> and <a href=\"https:\/\/learn.microsoft.com\/cpp\/standard-library\/packaged-task-class\"><code>std::packaged_task<\/code><\/a> had constructors that accepted custom allocators for performance tuning, but these were <a href=\"https:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/2016\/p0302r1.html\">removed in C++17<\/a> because &#8220;the semantics are unclear, and there are technical issues with storing an allocator in a type-erased context and then recovering that allocator later for any allocations needed during copy assignment&#8221;. These issues do not apply to <code>allocate_proxy<\/code>.<\/li>\n<li><strong>Configurable constraints<\/strong>: <code>facade_builder<\/code> provides full support for constraints configuration, including memory layout (by <code>restrict_layout<\/code>), copyability (by <code>support_copy<\/code>), relocatability (by <code>support_relocation<\/code>), and destructibility (by <code>support_destruction<\/code>).<\/li>\n<li><strong>Reflection<\/strong>: <code>proxy<\/code> supports type-based compile-time reflection for runtime queries, specifically with <code>facade_builder::add_reflection<\/code> and function template <code>proxy_reflect<\/code>.<\/li>\n<\/ul>\n<h2>Summary<\/h2>\n<p>We hope this library could empower more C++ users outside Microsoft to write polymorphic code easier. The full documentation of Proxy 3 will be published soon. Please stay tuned for more technical details. We are also actively working on several ISO C++ proposals for further standardization.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Next Generation Polymorphism in C++<\/p>\n","protected":false},"author":98503,"featured_media":35994,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-34567","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cplusplus"],"acf":[],"blog_post_summary":"<p>Next Generation Polymorphism in C++<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/34567","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/users\/98503"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/comments?post=34567"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/34567\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media\/35994"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media?parent=34567"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/categories?post=34567"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/tags?post=34567"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}