{"id":108192,"date":"2023-05-16T07:00:00","date_gmt":"2023-05-16T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108192"},"modified":"2023-05-15T19:58:12","modified_gmt":"2023-05-16T02:58:12","slug":"20230516-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230516-00\/?p=108192","title":{"rendered":"Summary of the duck-typing requirements of C++ COM wrappers"},"content":{"rendered":"<p>Here&#8217;s a summary of the tables we spent the past few days building. Points of interest are highlighted.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>\u00a0<\/th>\n<th><code>_com_ptr_t<\/code><\/th>\n<th>MFC <code>IPTR<\/code><\/th>\n<th>ATL <code>CComPtr<\/code><\/th>\n<th>WRL <code>ComPtr<\/code><\/th>\n<th><code>wil::com_ptr<\/code><\/th>\n<th>C++\/WinRT<br \/>\n<code>winrt::com_ptr<\/code><\/th>\n<\/tr>\n<tr>\n<td>Default construction<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td>Construct from raw pointer<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td bgcolor=\"#aaa\">N\/A<\/td>\n<\/tr>\n<tr>\n<td>Copy construction<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td>Destruction<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td>Attach and detach<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td>Assign to same-type raw pointer<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/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<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td>Fetch the wrapped pointer<\/td>\n<td><code>GetInterfacePtr()<\/code><\/td>\n<td><code>GetInterfacePtr()<\/code><\/td>\n<td><code>p<\/code>, or<br \/>\nimplicit conversion<\/td>\n<td><code>Get()<\/code><\/td>\n<td><code>get()<\/code><\/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<td><code>-&gt;<\/code><\/td>\n<td><code>-&gt;<\/code><\/p>\n<div style=\"background-color: #aaa;\">(suboptimal)<\/div>\n<\/td>\n<td><code>-&gt;<\/code><\/td>\n<td><code>-&gt;<\/code><\/td>\n<td><code>-&gt;<\/code><\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">Receive pointer via <code>&amp;<\/code><\/td>\n<td>release old<\/td>\n<td>release old<\/td>\n<td bgcolor=\"#aaa\">must be empty<\/td>\n<td>release old<\/td>\n<td>release old<\/td>\n<td bgcolor=\"#aaa\">N\/A<\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">Release and receive pointer<\/td>\n<td><code>&amp;<\/code><\/td>\n<td><code>&amp;<\/code><\/td>\n<td><code>&amp;<\/code><\/td>\n<td><code>ReleaseAndGetAddressOf()<\/code><\/td>\n<td><code>put()<\/code><\/td>\n<td><code>put()<\/code><\/td>\n<\/tr>\n<tr>\n<td valign=\"top\">Preserve and receive pointer<\/td>\n<td bgcolor=\"#aaa\">N\/A<\/td>\n<td bgcolor=\"#aaa\">N\/A<\/td>\n<td><code>&amp;p<\/code><\/td>\n<td><code>GetAddressOf()<\/code><\/td>\n<td><code>addressof()<\/code><\/td>\n<td bgcolor=\"#aaa\">N\/A<\/td>\n<\/tr>\n<tr>\n<td>Return to empty state<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td>Comparison<\/td>\n<td bgcolor=\"#aaa\">Fail<\/td>\n<td bgcolor=\"#aaa\">N\/A<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td>Accidental bypass<\/td>\n<td>Fail<\/td>\n<td>Fail<\/td>\n<td bgcolor=\"#aaa\">Pass<\/td>\n<td>Fail<\/td>\n<td>Fail<\/td>\n<td>Fail<\/td>\n<\/tr>\n<tr>\n<td>Construct from other-type raw pointer<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td>Construct from other-type smart pointer<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td>Assign from other-type raw pointer<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td>Assign from other-type smart pointer<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<td>Pass<\/td>\n<\/tr>\n<tr>\n<td><code>T<\/code> can be <code>final<\/code><\/td>\n<td>Yes<\/td>\n<td>Yes<\/td>\n<td bgcolor=\"#aaa\">No<\/td>\n<td>Yes<\/td>\n<td>Yes<\/td>\n<td>Yes<\/td>\n<\/tr>\n<tr>\n<td><code>T::Release<\/code> must return <code>ULONG<\/code><\/td>\n<td>No<\/td>\n<td>No<\/td>\n<td bgcolor=\"#aaa\">Yes<\/td>\n<td bgcolor=\"#aaa\">Yes<\/td>\n<td bgcolor=\"#aaa\">Yes<\/td>\n<td>No<\/td>\n<\/tr>\n<tr>\n<td><code>T::Release<\/code> must be <code>__stdcall<\/code><\/td>\n<td>No<\/td>\n<td>No<\/td>\n<td bgcolor=\"#aaa\">Yes<\/td>\n<td>No<\/td>\n<td>No<\/td>\n<td>No<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>For the most part, all of the smart pointer wrappers can wrap pointers to non-COM objects, provided they have <code>AddRef<\/code> and <code>Release<\/code> methods which increment and decrement the object reference count.<\/p>\n<p>ATL is the only one that protects against accidental bypass by using the &#8220;coloring&#8221; technique. However, the cost is that the wrapped object may not be <code>final<\/code>. Furthermore, its implementation of &#8220;coloring&#8221; is suboptimal if the underlying object does not derive from <code>IUnknown<\/code>. (If it does, then the &#8220;coloring&#8221; has no overhead.)<\/p>\n<p>The semantics of the <code>&amp;<\/code> address-of operator vary greatly, a topic I had discussed <!-- backref: On the overloading of the address-of operator <CODE>&amp;<\/CODE> in smart pointer classes --> some time ago. ATL is the only one whose <code>&amp;<\/code> operator does not release the pointer before producing the address. C++\/WinRT does not overload the <code>&amp;<\/code> operator at all.<\/p>\n<p><code>_com_ptr_t<\/code>, <code>IPTR<\/code>, and C++\/WinRT do not provide a way to access the wrapped pointer&#8217;s address without freeing it.<\/p>\n<p>Comparison operators break only in <code>_com_ptr_t<\/code>. (Comparison operators never worked with <code>IPTR<\/code>, so there was nothing to break.)<\/p>\n<p>C++\/WinRT does not support constructing directly from a raw pointer. This is annoying because the case we are most likely to use it for a non-IUnknown-derived class is specifically to construct directly from <code>this<\/code>:<\/p>\n<pre>\/\/ Sadly doesn't work\r\nRegisterCallback([strongThis = winrt::com_ptr(this)] { ... });\r\n<\/pre>\n<p>ATL, WRL, and wil require that the <code>Release<\/code> method return the new reference count as a <code>ULONG<\/code>. ATL even requires that the <code>Release<\/code> method be <code>__stdcall<\/code>.<\/p>\n<p>The MFC <code>IPTR<\/code> macro is not really used any more, but I listed it in the table for completeness. If you try to use it in a modern compiler, you will get warnings about its use of exception specifiers, which were deprecated in C++17.<\/p>\n<p>Glancing through the table, it seems that the least weird wrapper classes for wrapping types that aren&#8217;t COM interfaces are <code>WRL::<wbr \/>ComPtr<\/code> and <code>wil::<wbr \/>com_ptr<\/code>. You do have to make sure that your <code>Release()<\/code> method returns the new reference count, but this is likely something you already have readily available, so returning it is not that big of an obstacle.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wrapping up and comparing.<\/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-108192","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Wrapping up and comparing.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108192","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=108192"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108192\/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=108192"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108192"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108192"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}