{"id":108187,"date":"2023-05-15T07:00:00","date_gmt":"2023-05-15T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108187"},"modified":"2023-05-16T10:57:29","modified_gmt":"2023-05-16T17:57:29","slug":"20230515-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230515-00\/?p=108187","title":{"rendered":"What are the duck-typing requirements of C++\/WinRT <CODE>com_ptr<\/CODE>?"},"content":{"rendered":"<p>We conclude our survey of duck-typing requirements of various C++ COM smart pointer libraries by looking at C++\/WinRT&#8217;s <code>com_ptr<\/code>, running it through our standard tests.<\/p>\n<pre>\/\/ Dummy implementations of AddRef and Release for\r\n\/\/ testing purposes only. In real code, they would\r\n\/\/ manage the object reference count.\r\nstruct Test\r\n{\r\n    void AddRef() {}\r\n    void Release() {}\r\n    Test* AddressOf() { return this; }\r\n};\r\n\r\nstruct Other\r\n{\r\n    void AddRef() {}\r\n    void Release() {}\r\n};\r\n\r\n\/\/ Pull in the smart pointer library\r\n\/\/ (this changes based on library)\r\n<span style=\"border: solid 1px currentcolor;\">#include &lt;winrt\/base.h&gt;<\/span>\r\n\u00a0\r\n<span style=\"border: solid 1px currentcolor; border-bottom: none;\">using TestPtr = winrt::com_ptr&lt;Test&gt;;\u00a0\u00a0<\/span>\r\n<span style=\"border: solid 1px currentcolor; border-top: none;\">using OtherPtr = winrt::com_ptr&lt;Other&gt;;<\/span>\r\n\r\nvoid test()\r\n{\r\n    Test test;\r\n\r\n    \/\/ Default construction\r\n    TestPtr ptr;\r\n\r\n    \/\/ Construction from raw pointer\r\n    TestPtr ptr2(&amp;test); \/\/ (does not compile)\r\n\r\n    \/\/ Copy construction\r\n    TestPtr ptr3(ptr2);\r\n\r\n    \/\/ Attaching and detaching\r\n    auto p = ptr3.<span style=\"border: solid 1px currentcolor;\">detach<\/span>();\r\n    ptr.<span style=\"border: solid 1px currentcolor;\">attach<\/span>(p);\r\n\r\n    \/\/ Assignment from same-type raw pointer\r\n    ptr3.<span style=\"border: solid 1px currentcolor;\">copy_from(&amp;test);<\/span>\r\n\r\n    \/\/ Assignment from same-type smart pointer\r\n    ptr3 = ptr;\r\n\r\n    \/\/ Accessing the wrapped object\r\n    \/\/ (this changes based on library)\r\n    if (ptr.<span style=\"border: solid 1px currentcolor;\">get()<\/span> != &amp;test) {\r\n        std::terminate(); \/\/ oops\r\n    }\r\n    if (ptr-&gt;AddressOf() != &amp;test) {\r\n        std::terminate(); \/\/ oops\r\n    }\r\n\r\n    \/\/ Returning to empty state\r\n    ptr3 = nullptr;\r\n\r\n    \/\/ Receiving a new pointer\r\n    \/\/ (this changes based on library)\r\n    Test** out = <span style=\"border: solid 1px currentcolor;\">ptr3.put()<\/span>;\r\n\r\n    \/\/ Bonus: Comparison.\r\n    if (ptr == ptr2) {}\r\n    if (ptr != ptr2) {}\r\n    if (ptr &lt; ptr2) {}\r\n\r\n    \/\/ Litmus test: Accidentally bypassing the wrapper\r\n    ptr-&gt;AddRef();\r\n    ptr-&gt;Release();\r\n\r\n    \/\/ Litmus test: Construction from other-type raw pointer\r\n    Other other;\r\n    TestPtr ptr4(&amp;other);\r\n\r\n    \/\/ Litmus test: Construction from other-type smart pointer\r\n    OtherPtr optr;\r\n    TestPtr ptr5(optr);\r\n\r\n    \/\/ Litmus test: Assignment from other-type raw pointer\r\n    ptr.<span style=\"border: solid 1px currentcolor;\">copy_from(&amp;other);<\/span>\r\n\r\n    \/\/ Litmus test: Assignment from other-type smart pointer\r\n    ptr = optr;\r\n\r\n    \/\/ Destruction\r\n}\r\n<\/pre>\n<p>C++\/WinRT doesn&#8217;t require that the <code>Release<\/code> method return a reference count, unlike ATL, WRL, and wil. So that&#8217;s a relief.<\/p>\n<p>As with wil, we have to make a small tweak to the boilerplate by switching to lowercase names for <code>detach<\/code> and <code>attach<\/code>, because that&#8217;s how C++\/WinRT spells them.<\/p>\n<p>Another thing we have to fix is removing construction from raw pointers. C++\/WinRT doesn&#8217;t support the operation of &#8220;construct with shared ownership of a raw pointer&#8221;. It does support &#8220;take ownership of a raw pointer&#8221; by passing the marker <code>winrt::<wbr \/>take_<wbr \/>ownership_<wbr \/>of_<wbr \/>abi<\/code> as a second parameter. However, this is not generally used because it also discards type safety.<\/p>\n<p>Instead of assigning a raw pointer, C++\/WinRT uses the <code>copy_from<\/code> method. This makes it clearer that the smart pointer is sharing ownership with the original, rather than taking ownership from it. (The <code>attach<\/code> method takes ownership.)<\/p>\n<p>The only way to receive a pointer in C++\/WinRT is to use the <code>put<\/code> method. This releases the old pointer and nulls it out, then returns the address of the pointer so a new value can be placed there. There is no ability to access the inner pointer for in\/out use.<\/p>\n<p>C++\/WinRT doesn&#8217;t &#8220;color&#8221; the return value of the <code>-&gt;<\/code> operator, so you don&#8217;t get protection from signatures, but you also don&#8217;t get protection from accidentally doing a <code>ptr-&gt;Release()<\/code> when you meant to do a <code>ptr = nullptr<\/code>, but the two expressions are so different-looking that you&#8217;re less likely to confuse them.<\/p>\n<p>The other-type litmus tests all pass. They all result in various types of compile-time errors.<\/p>\n<p>Finally, so here&#8217;s the scorecard for <code>winrt::com_ptr<\/code>.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th colspan=\"2\"><code>winrt::com_ptr<\/code> scorecard<\/th>\n<\/tr>\n<tr>\n<td>Default construction<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td>Construct from raw pointer<\/td>\n<td>Not supported<\/td>\n<\/tr>\n<tr>\n<td>Copy construction<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td>Destruction<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td>Attach and detach<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td>Assign to same-type raw pointer<\/td>\n<td>Pass (<code>copy_from<\/code>)<\/td>\n<\/tr>\n<tr>\n<td>Assign to same-type smart pointer<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td>Fetch the wrapped pointer<\/td>\n<td><code>get()<\/code><\/td>\n<\/tr>\n<tr>\n<td>Access the wrapped object<\/td>\n<td><code>-&gt;<\/code><\/td>\n<\/tr>\n<tr>\n<td>Receive pointer via <code>&amp;<\/code><\/td>\n<td>N\/A<\/td>\n<\/tr>\n<tr>\n<td>Release and receive pointer<\/td>\n<td><code>put()<\/code><\/td>\n<\/tr>\n<tr>\n<td>Preserve and receive pointer<\/td>\n<td>N\/A<\/td>\n<\/tr>\n<tr>\n<td>Return to empty state<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td>Comparison<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td>Accidental bypass<\/td>\n<td>Fail<\/td>\n<\/tr>\n<tr>\n<td>Construct from other-type raw pointer<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td>Construct from other-type smart pointer<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td>Assign from other-type raw pointer<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td>Assign from other-type smart pointer<\/td>\n<td>Pass<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Next time, we&#8217;ll capture all these results into a large comparison table and discuss what we find.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The experiments conclude.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-108187","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>The experiments conclude.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108187","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=108187"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108187\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=108187"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108187"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108187"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}