{"id":30853,"date":"2022-08-15T15:00:41","date_gmt":"2022-08-15T15:00:41","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/cppblog\/?p=30853"},"modified":"2022-08-17T09:19:58","modified_gmt":"2022-08-17T09:19:58","slug":"proxy-runtime-polymorphism-made-easier-than-ever","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cppblog\/proxy-runtime-polymorphism-made-easier-than-ever\/","title":{"rendered":"proxy: Runtime Polymorphism Made Easier Than Ever"},"content":{"rendered":"<p><code>proxy<\/code> is an open-source, cross-platform, single-header C++ library, making runtime polymorphism easier to implement and faster, empowered by our breakthrough innovation of Object-oriented Programming (OOP) theory in recent years. Consider three questions:<\/p>\n<ol>\n<li>Do you want to facilitate architecture design and matainance by writing non-intrusive polymorphic code in C++ as easily as in Rust or Golang?<\/li>\n<li>Do you want to facilitate lifetime management of polymorphic objects as easily as in languages with runtime Garbage Collection (GC, like Java or C#), <em>without<\/em> compromising performance?<\/li>\n<li>Have you tried other polymorphic programming libraries in C++ but found them deficient?<\/li>\n<\/ol>\n<p>If so, this library is for you. You can find the implementation at <a href=\"https:\/\/github.com\/microsoft\/proxy\">our GitHub repo<\/a>, integrate with your project using <a href=\"https:\/\/github.com\/microsoft\/vcpkg\">vcpkg<\/a> (search for <code>proxy<\/code>), or learn more about the theory and technical specifications from <a href=\"https:\/\/wg21.link\/p0957\">P0957<\/a>.<\/p>\n<h2>Overview<\/h2>\n<p>In C++ today, there are certain architecture and performance limitations in existing mechanisms of polymorphism, specifically, virtual functions (based on inheritance) and various polymorphic wrappers (with value semantics) in the standard. As a result, <code>proxy<\/code> can largely replace the existing &#8220;virtual mechanism&#8221; to implement your vision in runtime polymorphism, while having no intrusion on existing code, with even better performance.<\/p>\n<p>All the facilities of the library are defined in namespace <code>pro<\/code>. The 3 major class templates are <code>dispatch<\/code>, <code>facade<\/code> and <code>proxy<\/code>. Here is a demo showing how to use this library to implement runtime polymorphism in a different way from the traditional inheritance-based approach:<\/p>\n<pre><code class=\"language-cpp\">\/\/ Abstraction\r\nstruct Draw : pro::dispatch&lt;void(std::ostream&amp;)&gt; {\r\n  template &lt;class T&gt;\r\n  void operator()(const T&amp; self, std::ostream&amp; out) { self.Draw(out); }\r\n};\r\nstruct Area : pro::dispatch&lt;double()&gt; {\r\n  template &lt;class T&gt;\r\n  double operator()(const T&amp; self) { return self.Area(); }\r\n};\r\nstruct DrawableFacade : pro::facade&lt;Draw, Area&gt; {};\r\n\r\n\/\/ Implementation (No base class)\r\nclass Rectangle {\r\n public:\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  void SetWidth(double width) { width_ = width; }\r\n  void SetHeight(double height) { height_ = height; }\r\n  double Area() const { return width_ * height_; }\r\n\r\n private:\r\n  double width_;\r\n  double height_;\r\n};\r\n\r\n\/\/ Client - Consumer\r\nstd::string PrintDrawableToString(pro::proxy&lt;DrawableFacade&gt; p) {\r\n  std::stringstream result;\r\n  result &lt;&lt; \"shape = \";\r\n  p.invoke&lt;Draw&gt;(result);  \/\/ Polymorphic call\r\n  result &lt;&lt; \", area = \" &lt;&lt; p.invoke&lt;Area&gt;();  \/\/ Polymorphic call\r\n  return std::move(result).str();\r\n}\r\n\r\n\/\/ Client - Producer\r\npro::proxy&lt;DrawableFacade&gt; CreateRectangleAsDrawable(int width, int height) {\r\n  Rectangle rect;\r\n  rect.SetWidth(width);\r\n  rect.SetHeight(height);\r\n  return pro::make_proxy&lt;DrawableFacade&gt;(rect);  \/\/ No heap allocation is expected\r\n}<\/code><\/pre>\n<h2>Configure your project<\/h2>\n<p>To get started, set the language level of your compiler to at least C++20 and get the header file (<a href=\"https:\/\/github.com\/microsoft\/proxy\/blob\/main\/proxy.h\">proxy.h<\/a>). You can also install the library via <a href=\"https:\/\/github.com\/microsoft\/vcpkg\/\">vcpkg<\/a>, which is a C++ library management software invented by Microsoft, by searching for &#8220;proxy&#8221;.<\/p>\n<p>To integrate with CMake, 3 steps are required:<\/p>\n<ol>\n<li>Set up the vcpkg manifest by adding &#8220;proxy&#8221; as a dependency in your <code>vcpkg.json<\/code> file:\n<pre><code class=\"language-json\">{\r\n\"name\": \"&lt;project_name&gt;\",\r\n\"version\": \"0.1.0\",\r\n\"dependencies\": [\r\n{\r\n  \"name\": \"proxy\"\r\n}\r\n]\r\n}<\/code><\/pre>\n<\/li>\n<li>Use <code>find_package<\/code> and <code>target_link_libraries<\/code> commands to reference to the library <code>proxy<\/code> in your <code>CMakeLists.txt<\/code> file:\n<pre><code class=\"language-cmake\">find_package(proxy CONFIG REQUIRED)\r\ntarget_link_libraries(&lt;target_name&gt; PRIVATE msft_proxy)<\/code><\/pre>\n<\/li>\n<li>Run CMake with vcpkg toolchain file:\n<pre><code class=\"language-cmake\">cmake &lt;source_dir&gt; -B &lt;build_dir&gt; -DCMAKE_TOOLCHAIN_FILE=&lt;vcpkg_dir&gt;\/scripts\/buildsystems\/vcpkg.cmake<\/code><\/pre>\n<\/li>\n<\/ol>\n<h2>What makes the &#8220;proxy&#8221; so charming<\/h2>\n<p>As a polymorphic programming library, <code>proxy<\/code> has various highlights, including:<\/p>\n<ol>\n<li>being <em>non-intrusive<\/em><\/li>\n<li>allowing lifetime management <em>per object<\/em>, complementary with smart pointers<\/li>\n<li>high-quality code generation<\/li>\n<li>supporting flexible composition of abstractions<\/li>\n<li>optimized syntax for Customization Point Objects (CPO) and modules<\/li>\n<li>supporting general-purpose static reflection<\/li>\n<li>supporting expert performance tuning<\/li>\n<li>high-quality diagnostics.<\/li>\n<\/ol>\n<p>In this section, we will briefly introduce each of the highlights listed above with concrete examples.<\/p>\n<h3>Highlight 1: Being non-intrusive<\/h3>\n<p>Designing polymorphic types with inheritance usually requires careful architecting. If the design is not thought through enough early on, the components may become overly complex as more and more functionality is added, or extensibility may be insufficient if polymorphic types are coupled too closely. On the other hand, some libraries (including the standard library) may not have proper polymorphic semantics even if they, by definition, satisfy same specific constraints. In such scenarios, users have no alternative but to design and maintain extra middleware themselves to add polymorphism support to existing implementations.<\/p>\n<p>For example, some programming languages provide base types for containers, which makes it easy for library authors to design APIs without binding to a specific data structure at runtime. However, this is not feasible in C++ because most of the standard containers are not required to have a common base type. I do not think this is a design defect of C++, on the contrary, I think it is reasonable not to overdesign for runtime abstraction before knowing the concrete requirements both for the simplicity of the semantics and for runtime performance. With <code>proxy<\/code>, because it is non-intrusive, if we want to abstract a mapping data structure from indices to strings for localization, we may define the following facade:<\/p>\n<pre><code class=\"language-cpp\">struct at : pro::dispatch&lt;std::string(int)&gt; {\r\n  template &lt;class T&gt;\r\n  auto operator()(T&amp; self, int key) { return self.at(key); }\r\n};\r\nstruct ResourceDictionaryFacade : pro::facade&lt;at&gt; {};<\/code><\/pre>\n<p>It could proxy any potential mapping data structure, including but not limited to <code>std::map&lt;int, std::string&gt;<\/code>, <code>std::unordered_map&lt;int, std::string&gt;<\/code>, <code>std::vector&lt;std::string&gt;<\/code>, etc.<\/p>\n<pre><code class=\"language-cpp\">\/\/ Library\r\nvoid DoSomethingWithResourceDictionary(pro::proxy&lt;ResourceDictionaryFacade&gt; p) {\r\n  try {\r\n    std::cout &lt;&lt; p.invoke(1) &lt;&lt; std::endl;\r\n  } catch (const std::out_of_range&amp; e) {\r\n    std::cout &lt;&lt; \"No such element: \" &lt;&lt; e.what() &lt;&lt; std::endl;\r\n  }\r\n}\r\n\r\n\/\/ Client\r\nstd::map&lt;int, std::string&gt; var1{{1, \"Hello\"}};\r\nstd::vector&lt;std::string&gt; var2{\"I\", \"love\", \"Proxy\", \"!\"};\r\nDoSomethingWithResourceDictionary(&amp;var1);  \/\/ Prints \"Hello\"\r\nDoSomethingWithResourceDictionary(&amp;var2);  \/\/ Prints \"love\"\r\nDoSomethingWithResourceDictionary(std::make_shared&lt;std::unordered_map&lt;int, std::string&gt;&gt;());  \/\/ Prints \"No such element: {implementation-defined error message}\"<\/code><\/pre>\n<p>Overall, inheritance-based polymorphism has certain limitations in usability. As <a href=\"https:\/\/www.youtube.com\/watch?v=QGcVXgEVMJg\">Sean Parent commented on NDC 2017<\/a>: <em>The requirements of a polymorphic type, by definition, comes from its use, and there are no polymorphic types, only polymorphic use of similar types. Inheritance is the base class of evil<\/em>.<\/p>\n<h3>Highlight 2: Evolutionary lifetime management<\/h3>\n<p>It is such a pain to manage lifetime of objects in large systems written in C++. Because C++ does not have built-in GC support due to performance considerations, users need to beware of lifetime management of every single object. Although we have smart pointers since C++11 (i.e., <code>std::unique_ptr<\/code> and <code>std::shared_ptr<\/code>), and various 3rd-party fancy pointers like <code>boost::interprocess::offset_ptr<\/code>, they are not always sufficient for polymorphic use with inheritance. By using the <code>proxy<\/code> complementary with smart pointers, clients could care less about lifetime management as if there is runtime GC, but without compromising performance.<\/p>\n<p>Before using any polymorphic object, the first step is always to create it. In other programming languages like Java or C#, we can <code>new<\/code> an object at any time and runtime GC will take care of lifetime management when it becomes unreachable, at the cost of performance. But how should we implement it in C++? Consider the <code>drawable<\/code> example in the &#8220;Overview&#8221; section: given there are 3 <code>drawable<\/code> types in a system: <code>Rectangle<\/code>, <code>Circle<\/code>, and <code>Point<\/code>. Specifically,<\/p>\n<ul>\n<li><code>Rectangle<\/code>s have width, height, transparency, and area<\/li>\n<li><code>Circle<\/code>s have radius, transparency, and area<\/li>\n<li><code>Point<\/code>s do not have any property; its area is always zero<\/li>\n<\/ul>\n<p>A library function <code>MakeDrawableFromCommand<\/code> shall be defined as a factory function responsible for creating a <code>drawable<\/code> instance by parsing the command line.<\/p>\n<p>Here is how we usually define the types with inheritance:<\/p>\n<pre><code class=\"language-cpp\">\/\/ Abstraction\r\nclass IDrawable {\r\n public:\r\n  virtual void Draw(std::ostream&amp; out) const = 0;\r\n  virtual double Area() const = 0;\r\n  \/\/ Don't forget the virtual destructor, otherwise `delete`ing a pointer of `IDrawable` may result in memory leak!\r\n  virtual ~IDrawable() {}  \r\n};\r\n\r\n\/\/ Implementation\r\nclass Rectangle : public IDrawable {\r\n public:\r\n  void Draw(std::ostream&amp; out) const override;\r\n  void SetWidth(double width);\r\n  void SetHeight(double height);\r\n  void SetTransparency(double);\r\n  double Area() const override;\r\n};\r\nclass Circle : public IDrawable {\r\n public:\r\n  void Draw(std::ostream&amp; out) const override;\r\n  void SetRadius(double radius);\r\n  void SetTransparency(double transparency);\r\n  double Area() const override;\r\n};\r\nclass Point : public IDrawable {\r\n public:\r\n  void Draw(std::ostream&amp; out) const override;\r\n  constexpr double Area() const override { return 0; }\r\n};<\/code><\/pre>\n<p>If we use <code>std::string<\/code> to represent the command line, the parameter type of <code>MakeDrawableFromCommand<\/code> could be <code>const std::string&amp;<\/code>, where there should not be much debate. But what should the return type be? <code>IDrawable*<\/code>? <code>std::unique_ptr&lt;IDrawable&gt;<\/code>? Or <code>std::shared_ptr&lt;IDrawable&gt;<\/code>? Specifically,<\/p>\n<ul>\n<li>If we use <code>IDrawable*<\/code>, the semantics of the return type is ambiguous because it is a raw pointer type and does not indicate the lifetime of the object. For instance, it could be allocated via <code>operator new<\/code>, from a memory pool or even a global object. Clients always need to learn the hidden contract from the author (or even need to learn the implementation details if the author and documentation are not available for consulting) and properly disposing of the object when the related business has finished via <code>operator delete<\/code> or some other way corresponding to how it was allocated.<\/li>\n<li>If we use <code>std::unique_ptr&lt;IDrawable&gt;<\/code>, it means every single object is allocated individually from the heap, even if the value is potentially immutable or reusable (&#8220;flyweight&#8221;), which is potentially bad for performance.<\/li>\n<li>If we use <code>std::shared_ptr&lt;IDrawable&gt;<\/code>, the performance could become better for flyweight objects due to the relatively low cost of copying, but the ownership of the object becomes ambiguous (a.k.a. &#8220;ownership hell&#8221;), and the thread-safety guarantee of copy-construction and destruction of <code>std::shared_ptr<\/code> may also add to runtime overhead. On the other hand, if we prefer <code>std::shared_ptr<\/code> across the whole system, every polymorphic type is encouraged to inherit <code>std::enable_shared_from_this<\/code>, which may significantly affect the design and maintenance of a large system.<\/li>\n<\/ul>\n<p>For <code>proxy<\/code>, with the definition from the &#8220;Overview&#8221; section, we can simply define the return type as <code>pro::proxy&lt;DrawableFacade&gt;<\/code> without further concern. In the implementation, <code>pro::proxy&lt;DrawableFacade&gt;<\/code> could be instantiated from all kinds of pointers with potentially different lifetime management strategy. For example, <code>Rectangle<\/code>s may be created every time when requested from a memory pool, while the value of <code>Point<\/code>s could be cached throughout the lifetime of the program:<\/p>\n<pre><code class=\"language-cpp\">pro::proxy&lt;DrawableFacade&gt; MakeDrawableFromCommand(const std::string&amp; s) {\r\n  std::vector&lt;std::string&gt; parsed = ParseCommand(s);\r\n  if (!parsed.empty()) {\r\n    if (parsed[0u] == \"Rectangle\") {\r\n      if (parsed.size() == 3u) {\r\n        static std::pmr::unsynchronized_pool_resource rectangle_memory_pool;\r\n        std::pmr::polymorphic_allocator&lt;&gt; alloc{&amp;rectangle_memory_pool};\r\n        auto deleter = [alloc](Rectangle* ptr) mutable\r\n            { alloc.delete_object&lt;Rectangle&gt;(ptr); };\r\n        Rectangle* instance = alloc.new_object&lt;Rectangle&gt;();\r\n        std::unique_ptr&lt;Rectangle, decltype(deleter)&gt; p{instance, deleter};  \/\/ Allocated from a memory pool\r\n        p-&gt;SetWidth(std::stod(parsed[1u]));\r\n        p-&gt;SetHeight(std::stod(parsed[2u]));\r\n        return p;  \/\/ Implicit conversion happens\r\n      }\r\n    } else if (parsed[0u] == \"Circle\") {\r\n      if (parsed.size() == 2u) {\r\n        Circle circle;\r\n        circle.SetRadius(std::stod(parsed[1u]));\r\n        return pro::make_proxy&lt;DrawableFacade&gt;(circle);  \/\/ SBO may apply\r\n      }\r\n    } else if (parsed[0u] == \"Point\") {\r\n      if (parsed.size() == 1u) {\r\n        static Point instance;  \/\/ Global singleton\r\n        return &amp;instance;\r\n      }\r\n    }\r\n  }\r\n  throw std::runtime_error{\"Invalid command\"};\r\n}<\/code><\/pre>\n<p>The full implementation of the example above could be found in our <a href=\"https:\/\/github.com\/microsoft\/proxy\/blob\/1.0.1\/tests\/proxy_integration_tests.cpp#L87-L116\">integration tests<\/a>. In this example, there are 3 <code>return<\/code> statements in different branches and the return types are also different. Lifetime management with inheritance-based polymorphism is error-prone and inflexible, while <code>proxy<\/code> allows easy customization of any lifetime management strategy, including but not limited to raw pointers and various smart pointers with potentially pooled memory management.<\/p>\n<p>Specifically, Small Buffer Optimization (SBO, a.k.a., SOO, Small Object Optimization) is a common technique to avoid unnecessary memory allocation (see the second <code>return<\/code> statement). However, for inheritance-based polymorphism, there are few facilities in the standard that support SBO; for other standard polymorphic wrappers, implementations may support SBO, but there is no standard way to configure it so far. For example, if the size of <code>std::any<\/code> is <code>n<\/code>, it is theoretically impossible to store the concrete value whose size is larger than <code>n<\/code> without external storage.<\/p>\n<p><strong>The top secret making <code>proxy<\/code> both easy-to-use and fast is that it allows lifetime management <em>per object<\/em><\/strong>, which had not been addressed in traditional OOP theory (inheritance-based polymorphism) ever before.<\/p>\n<p>If you have tried other polymorphic programming libraries in C++ before, you may or may not find this highlight of lifetime management unique to <code>proxy<\/code>. Some of these libraries claim to support various lifetime management model, but do not allow <em>per-object<\/em> customization like <code>proxy<\/code> does.<\/p>\n<p>Take <a href=\"https:\/\/github.com\/ldionne\/dyno\"><code>dyno<\/code><\/a> as an example. <code>dyno<\/code> is another non-intrusive polymorphic programming library in C++. Given an &#8220;interface&#8221; type <code>I<\/code>, <code>dyno<\/code> does not allow <code>dyno::poly&lt;I&gt;<\/code> to have a different lifetime management model. By default, <code>dyno::poly&lt;I&gt;<\/code> always allocates from the heap by the time this blog was written (see <a href=\"https:\/\/github.com\/ldionne\/dyno\/blob\/b05a51eeaa4ddd7deb9e3100984bf20cfbf94eab\/include\/dyno\/poly.hpp#L64\">typename Storage = dyno::remote_storage<\/a>). For example, if we want to take advantage of SBO, it is needed to override the <code>Storage<\/code> type, i.e., <code>dyno::poly&lt;I, dyno::sbo_storage&lt;...&gt;&gt;<\/code>, which is a different type from <code>dyno::poly&lt;I&gt;<\/code>. Therefore, <code>dyno::poly&lt;I&gt;<\/code> could not be used to implement features like <code>MakeDrawableFromCommand<\/code> above, where the optimal lifetime management model of each branch may differ. Whereas <code>proxy<\/code> does not have a second template parameter. Given a facade type <code>F<\/code>, <code>pro::proxy&lt;F&gt;<\/code> is compatible with <em>any<\/em> lifetime management model within the constraints of the facade.<\/p>\n<h3>Highlight 3: High-quality code generation<\/h3>\n<p>Not only does <code>proxy<\/code> allow efficient lifetime management per object, but also it could generate high quality code for every indirect call. Specifically,<\/p>\n<ol>\n<li>Invocations from <code>proxy<\/code> could be properly inlined, except for the virtual dispatch on the client side, similar to the inheritance-based mechanism.<\/li>\n<li>Because <code>proxy<\/code> is based on pointer semantics, the &#8220;dereference&#8221; operation may happen inside the virtual dispatch, which always generates different instructions from the inheritance-based mechanism.<\/li>\n<li>As tested, with &#8220;clang 13.0.0 (x86-64)&#8221; and &#8221; clang 13.0.0 (RISC-V RV64)&#8221;, <code>proxy<\/code> generates one more instruction than the inheritance-based mechanism, while the situation is reversed with &#8220;gcc 11.2 (ARM64)&#8221;. This may infer that <code>proxy<\/code> could have similar runtime performance in invocation with the inheritance-based mechanism at least on the 3 processor architectures (x86-64, ARM64, RISC-V RV64).<\/li>\n<\/ol>\n<p>More details of code generation analysis could be found in <a href=\"https:\/\/wg21.link\/p0957\">P0957<\/a>.<\/p>\n<h3>Highlight 4: Composition of abstractions<\/h3>\n<p>To support reuse of declaration of expression sets, like inheritance of virtual base classes, the <code>facade<\/code> allows combination of different dispatches with <code>std::tuple<\/code>, while duplication is allowed. For example,<\/p>\n<pre><code class=\"language-cpp\">struct D1;\r\nstruct D2;\r\nstruct D3;\r\nstruct FA : pro::facade&lt;D1, D2, D3&gt; {};\r\nstruct FB : pro::facade&lt;D1, std::tuple&lt;D3, D2&gt;&gt; {};\r\nstruct FC : pro::facade&lt;std::tuple&lt;D1, D2, D3&gt;, D1, std::tuple&lt;D2, D3&gt;&gt; {};<\/code><\/pre>\n<p>In the sample code above, given <code>D1<\/code>, <code>D2<\/code> and <code>D3<\/code> are well-formed dispatch types, <code>FA<\/code>, <code>FB<\/code> and <code>FC<\/code> are equivalent. This allows &#8220;diamond inheritance&#8221; of abstraction without<\/p>\n<ul>\n<li>syntax ambiguity<\/li>\n<li>coding techniques like &#8220;virtual inheritance&#8221;<\/li>\n<li>extra binary size<\/li>\n<li>runtime overhead<\/li>\n<\/ul>\n<h3>Highlight 5: Syntax for CPOs and modules<\/h3>\n<p>Along with the standardization of Customization Point Objects (CPO) and improved syntax for Non-Type Template Parameters (NTTP), there are two recommended ways to define a &#8220;dispatch&#8221; type:<\/p>\n<p>The first way is to manually overload <code>operator()<\/code> as demonstrated before. This is useful when a dispatch is intended to be defined in a header file shared with multiple translation units, e.g., in <a href=\"https:\/\/github.com\/microsoft\/proxy\/blob\/1.0.1\/tests\/proxy_invocation_tests.cpp#L25-L33\">tests\/proxy_invocation_tests.cpp<\/a>:<\/p>\n<pre><code class=\"language-cpp\">template &lt;class T&gt;\r\nstruct ForEach : pro::dispatch&lt;void(pro::proxy&lt;CallableFacade&lt;void(T&amp;)&gt;&gt;)&gt; {\r\n template &lt;class U&gt;\r\n void operator()(U&amp; self, pro::proxy&lt;CallableFacade&lt;void(T&amp;)&gt;&gt;&amp;&amp; func) {\r\n  for (auto&amp; value : self) {\r\n   func.invoke(value);\r\n  }\r\n }\r\n};<\/code><\/pre>\n<p>The second way is to specify a <code>constexpr<\/code> callable object as the second template parameter. It provides easier syntax if a corresponding CPO is defined before, or the &#8220;dispatch&#8221; is intended to be defined in a module with lambda expressions, e.g. in <a href=\"https:\/\/github.com\/microsoft\/proxy\/blob\/1.0.1\/tests\/proxy_invocation_tests.cpp#L23\">tests\/proxy_invocation_tests.cpp<\/a>:<\/p>\n<pre><code class=\"language-cpp\">struct GetSize : pro::dispatch&lt;std::size_t(), std::ranges::size&gt; {};<\/code><\/pre>\n<h3>Highlight 6: Static reflection<\/h3>\n<p>Reflection is an essential requirement in type erasure, and <code>proxy<\/code> welcomes general-purpose static (compile-time) reflection other than <code>std::type_info<\/code>.<\/p>\n<p>In other languages like C# or Java, users are allowed to acquire detailed metadata of a type-erased type at runtime with simple APIs, but this is not true for <code>std::function<\/code>, <code>std::any<\/code> or inheritance-based polymorphism in C++. Although these reflection facilities add certain runtime overhead to these languages, they do help users write simple code in certain scenarios. In C++, as the reflection TS keeps evolving, there will be more static reflection facilities in the standard with more specific type information deduced at compile-time than <code>std::type_info<\/code>. It becomes possible for general-purpose reflection to become zero-overhead in C++ polymorphism.<\/p>\n<p>As a result, we decided to make <code>proxy<\/code> support general-purpose static reflection. It&#8217;s off by default, and theoretically won&#8217;t impact runtime performance other than the target binary size if turned on. Here is an example to reflect the given types to <code>MyReflectionInfo<\/code>:<\/p>\n<pre><code class=\"language-cpp\">class MyReflectionInfo {\r\n public:\r\n  template &lt;class P&gt;\r\n  constexpr explicit MyReflectionInfo(std::in_place_type_t&lt;P&gt;) : type_(typeid(P)) {}\r\n  const char* GetName() const noexcept { return type_.name(); }\r\n\r\n private:\r\n  const std::type_info&amp; type_;\r\n};\r\n\r\nstruct MyFacade : pro::facade&lt;\/* Omitted *\/&gt; {\r\n  using reflection_type = MyReflectionInfo;\r\n};<\/code><\/pre>\n<p>Users may call <code>MyReflectionInfo::GetName()<\/code> to get the implementation-defined name of a type at runtime:<\/p>\n<pre><code class=\"language-cpp\">pro::proxy&lt;MyFacade&gt; p;\r\nputs(p.reflect().GetName());  \/\/ Prints typeid(THE_UNDERLYING_POINTER_TYPE).name()<\/code><\/pre>\n<h3>Highlight 7: Performance tuning<\/h3>\n<p>To allow implementation balance between extensibility and performance, a set of constraints to a pointer is introduced, including maximum size, maximum alignment, minimum copyability, minimum relocatability and minimum destructibility. The term &#8220;relocatability&#8221; was introduced in <a href=\"https:\/\/wg21.link\/p1144\">P1144<\/a>, &#8220;equivalent to a move and a destroy&#8221;. This blog uses the term &#8220;relocatability&#8221; but does not depend on the technical specifications of <a href=\"https:\/\/wg21.link\/p1144\">P1144<\/a>.<\/p>\n<p>While the size and alignment could be described with <code>std::size_t<\/code>, the constraint level of copyability, relocatability and destructibility are described with enum <code>pro::constraint_level<\/code>, which includes <code>none<\/code>, <code>nontrivial<\/code>, <code>nothrow<\/code> and <code>trivial<\/code>, matching the standard wording. The defaults are listed below:<\/p>\n<table>\n<thead>\n<tr>\n<th><strong>Constraints<\/strong><\/th>\n<th><strong>Defaults<\/strong><\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Maximum size<\/td>\n<td>The size of two pointers<\/td>\n<\/tr>\n<tr>\n<td>Maximum alignment<\/td>\n<td>The alignment of a pointer<\/td>\n<\/tr>\n<tr>\n<td>Minimum copyability<\/td>\n<td>None<\/td>\n<\/tr>\n<tr>\n<td>Minimum relocatability<\/td>\n<td>Nothrow<\/td>\n<\/tr>\n<tr>\n<td>Minimum destructibility<\/td>\n<td>Nothrow<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>We can assume the default maximum size and maximum alignment greater than or equal to the implementation of raw pointers, <code>std::unique_ptr<\/code> with default deleters, <code>std::unique_ptr<\/code> with any one-pointer-size of deleters and <code>std::shared_ptr<\/code> of any type.<\/p>\n<p>Note that the default minimum copyability is &#8220;None&#8221;, which means <code>proxy<\/code> could be instantiated from a non-copyable type like <code>std::unique_ptr<\/code>. However, if we never want to instantiate a <code>proxy<\/code> with non-copyable types (including <code>std::unique_ptr<\/code>) and want the <code>proxy<\/code> to be copyable, it is allowed to customize it in a facade definition:<\/p>\n<pre><code class=\"language-cpp\">\/\/ Abstraction\r\nstruct MyFacade : pro::facade&lt;\/* Omitted *\/&gt; {\r\n  static constexpr auto minimum_copyability = pro::constraint_level::nontrivial;\r\n};\r\n\r\n\/\/ Client\r\npro::proxy&lt;MyFacade&gt; p0 = \/* Omitted *\/;\r\nauto p1 = p0;  \/\/ Calls the constructor of the underlying pointer type<\/code><\/pre>\n<p>In some cases where we clearly know we always instantiate a <code>proxy<\/code> with a raw pointer, and want to optimize the performance to the limit, it is allowed to add even more constraints in a facade definition, at the cost of reducing the scope of feasible pointer types:<\/p>\n<pre><code class=\"language-cpp\">\/\/ Abstraction\r\nstruct MyFacade : pro::facade&lt;\/* Omitted *\/&gt; {\r\n  static constexpr auto minimum_copyability = pro::constraint_level::trivial;\r\n  static constexpr auto minimum_relocatability = pro::constraint_level::trivial;\r\n  static constexpr auto minimum_destructibility = pro::constraint_level::trivial;\r\n  static constexpr auto maximum_size = sizeof(void*);\r\n  static constexpr auto maximum_alignment = alignof(void*);\r\n};\r\n\r\n\/\/ Client\r\nstatic_assert(std::is_trivially_copy_constructible_v&lt;pro::proxy&lt;MyFacade&gt;&gt;);\r\nstatic_assert(std::is_trivially_destructible_v&lt;pro::proxy&lt;MyFacade&gt;&gt;);<\/code><\/pre>\n<p><strong>IMPORTANT NOTICE<\/strong>: clang will fail to compile if the <strong>minimum_destructibility<\/strong> is set to <strong>constraint_level::trivial<\/strong> in a facade definition. The root cause of this failure is that the implementation requires the language feature defined in <a href=\"http:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/2019\/p0848r3.html\">P0848R3: Conditionally Trivial Special Member Functions<\/a>, but it has not been implemented in clang, according to its <a href=\"https:\/\/clang.llvm.org\/cxx_status.html\">documentation<\/a>, at the time this blog was written.<\/p>\n<h3>Highlight 8: Diagnostics<\/h3>\n<p>The design of <code>proxy<\/code> is SFINAE-friendly, thanks to the Concepts feature since C++20. If it is used incorrectly, compile error messages could be generated accurately at the spot. For example, if we call the constructor of <code>proxy<\/code> with a pointer, whose type does not meet the facade definition:<\/p>\n<pre><code class=\"language-cpp\">pro::proxy&lt;MyFacade&gt; p;\r\np.invoke&lt;nullptr_t&gt;();  \/\/ nullptr_t is not a valid dispatch type<\/code><\/pre>\n<p>Here is the error message gcc 11.2 will report:<\/p>\n<pre><code class=\"language-text\">&lt;source&gt;:550:22: error: no matching function for call to 'pro::proxy&lt;MyFacade&gt;::invoke&lt;nullptr_t&gt;()'\r\n  550 |   p.invoke&lt;nullptr_t&gt;();\r\n      |   ~~~~~~~~~~~~~~~~~~~^~\r\n&lt;source&gt;:445:18: note: candidate: 'template&lt;class D, class ... Args&gt; decltype(auto) pro::proxy&lt;F&gt;::invoke(Args&amp;&amp; ...) requires (pro::details::dependent_traits&lt;pro::details::facade_traits&lt;F&gt;, D&gt;::dependent_t&lt;pro::details::facade_traits&lt;F&gt;, D&gt;::applicable) &amp;&amp; (pro::details::BasicTraits::has_dispatch&lt;D&gt;) &amp;&amp; (is_convertible_v&lt;std::tuple&lt;_Args2 ...&gt;, typename D::argument_types&gt;) [with D = D; Args = {Args ...}; F = MyFacade]'\r\n  445 |   decltype(auto) invoke(Args&amp;&amp;... args)\r\n      |                  ^~~~~~\r\n&lt;source&gt;:445:18: note:   template argument deduction\/substitution failed:\r\n&lt;source&gt;:445:18: note: constraints not satisfied<\/code><\/pre>\n<h2>Conclusion<\/h2>\n<p>We hope this has helped clarify how to take advantage of the library &#8220;proxy&#8221; to write polymorphic code easier. If you have any questions, comments, or issues with the library, you can comment below, file issues in <a href=\"https:\/\/github.com\/microsoft\/proxy\">our GitHub repo<\/a>, or reach us via email at <a href=\"mailto:visualcpp@microsoft.com\">visualcpp@microsoft.com<\/a> or via Twitter at <a href=\"https:\/\/twitter.com\/visualc\">@VisualC<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>proxy is a single-header cross-platform library that facilitates runtime polymorphism.<\/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":[270,1,266,256,230,272],"tags":[],"class_list":["post-30853","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-announcement","category-cplusplus","category-cmake","category-experimental","category-new-feature","category-vcpkg"],"acf":[],"blog_post_summary":"<p>proxy is a single-header cross-platform library that facilitates runtime polymorphism.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/30853","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=30853"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/30853\/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=30853"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/categories?post=30853"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/tags?post=30853"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}