{"id":107595,"date":"2022-12-15T07:00:00","date_gmt":"2022-12-15T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=107595"},"modified":"2022-12-15T08:42:42","modified_gmt":"2022-12-15T16:42:42","slug":"20221215-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20221215-00\/?p=107595","title":{"rendered":"Inside C++\/WinRT: <CODE>IReference&lt;T&gt;<\/CODE>"},"content":{"rendered":"<p>Last time, we looked at <a title=\"In C++\/WinRT, how do I create or consume an IReference&lt;T&gt; that wraps a particular value?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20221214-00\/?p=107589\"> how to create an <code>IReference&lt;T&gt;<\/code> in C++\/WinRT<\/a>. How did I know the answer? By reading the source code and reverse-engineering what operations were valid and what they produced.<\/p>\n<p>Let&#8217;s look at the available constructors:<\/p>\n<pre>IReference(IReference const&amp; other) = default;\r\nIReference(IReference&amp;&amp; other) = default;\r\n<\/pre>\n<p>The copy and move constructors are implicitly declared, but I&#8217;m declaring them explicitly so they show up on the list.<\/p>\n<pre>IReference(std::nullptr_t = nullptr) noexcept {}\r\n<\/pre>\n<p>This constructor serves as a default constructor, or you can construct from <code>nullptr<\/code>. Either way creates an empty <code>IReference<\/code>. This constructor exists for all interface types, so it&#8217;s nothing surprising.<\/p>\n<pre>IReference(void* ptr, take_ownership_from_abi_t) noexcept\r\n    : Windows::Foundation::IInspectable(\r\n        ptr, take_ownership_from_abi) {}\r\n<\/pre>\n<p>The <code>take_<wbr \/>ownership_<wbr \/>from_<wbr \/>abi<\/code> tag type constructor creates an <code>IReference<\/code> that takes ownership of an ABI pointer. This constructor also exists for all interface types, so nothing special is happening yet.<\/p>\n<pre>IReference(T const&amp; value)\r\n    : IReference&lt;T&gt;(\r\n        impl::reference_traits&lt;T&gt;::make(value))     \r\n{}\r\n<\/pre>\n<p>This is a conversion constructor which takes the underlying value type, calls the <code>make<\/code> method from some class we haven&#8217;t yet studied, and uses that to initialize the <code>IReference<\/code>. We&#8217;ll look at that class later.<\/p>\n<p>The last constructor is<\/p>\n<pre>IReference(std::optional&lt;T&gt; const&amp; value)\r\n    : IReference(\r\n        value ? IReference(value.value()) : nullptr)\r\n{}\r\n<\/pre>\n<p>This is a conversion constructor that takes a <code>std::optional&lt;T&gt;<\/code> and produces the corresponding <code>std::IReference&lt;T&gt;<\/code>. If the <code>optional<\/code> has a value, then we wrap it inside an <code>IReference<\/code> using the value conversion constructor we saw above. Otherwise, we initialize from <code>nullptr<\/code>, which means &#8220;no value&#8221;.<\/p>\n<p>One thing to observe is that these last two constructors work with <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/language\/class_template_argument_deduction\"> CTAD<\/a>, so you can omit the template specialization:<\/p>\n<pre>\/\/ compile deduces IReference&lt;double&gt; from double\r\ndouble value = 42.0;\r\nauto ref = IReference(value);\r\n\r\n\/\/ compile deduces IReference&lt;double&gt; from std::optional&lt;double&gt;\r\nstd::optional&lt;double&gt; value(42.0);\r\nauto ref = IReference(value);\r\n<\/pre>\n<p>The last member function is the <code>std::<wbr \/>optional<\/code> conversion:<\/p>\n<pre>operator std::optional&lt;T&gt;() const\r\n{\r\n    if (*this)\r\n    {\r\n        return this-&gt;Value();\r\n    }\r\n    else\r\n    {\r\n        return std::nullopt;\r\n    }\r\n}\r\n<\/pre>\n<p>This converts an <code>IReference&lt;T&gt;<\/code> to a <code>std::<wbr \/>optional&lt;T&gt;<\/code> by producing the value if the wrapped pointer is non-null, or producing an empty <code>optional<\/code> if the wrapped pointer is null.<\/p>\n<p>All that&#8217;s left is studying the <code>impl::<wbr \/>reference_traits<\/code>.<\/p>\n<pre>template &lt;typename T&gt;\r\nstruct reference_traits\r\n{\r\n    static auto make(T const&amp; value)\r\n    { return winrt::make&lt;impl::reference&lt;T&gt;&gt;(value); }\r\n    using itf = Windows::Foundation::IReference&lt;T&gt;;\r\n};\r\n<\/pre>\n<p>This is the unspecialized template traits class which <code>make<\/code>s an <code>IReference&lt;T&gt;<\/code> by creating an instance of the private <code>impl::<wbr \/>reference<\/code> class. We&#8217;ll make a note to come back to that later.<\/p>\n<p>What follows is a series of specializations for various fundamental types. For example,<\/p>\n<pre>template &lt;&gt;\r\nstruct reference_traits&lt;uint8_t&gt;\r\n{\r\n    static auto make(uint8_t value)\r\n    { return Windows::Foundation::PropertyValue::CreateUInt8(value); }\r\n    using itf = Windows::Foundation::IReference&lt;uint8_t&gt;;\r\n};\r\n<\/pre>\n<p>To create an <code>IReference&lt;uint8_t&gt;<\/code>, the <code>make()<\/code> method uses the <code>PropertyValue::<wbr \/>CreateUInt8()<\/code> method. Repeat for the other special-purpose factory methods of <code>PropertyValue<\/code>.<\/p>\n<p>Okay, so now we&#8217;re left with that <code>impl::<wbr \/>reference<\/code> class:<\/p>\n<pre>template &lt;typename T&gt;\r\nstruct reference :\r\n    implements&lt;reference&lt;T&gt;,\r\n        Windows::Foundation::IReference&lt;T&gt;,\r\n        Windows::Foundation::IPropertyValue&gt;\r\n{\r\n    reference(T const&amp; value) : m_value(value)\r\n    { }\r\n\r\n    T Value() const { return m_value; }\r\n\r\n    Windows::Foundation::PropertyType Type() const noexcept\r\n    {\r\n        return Windows::Foundation::PropertyType::OtherType;\r\n    }\r\n\r\n    static constexpr bool IsNumericScalar() noexcept\r\n    {\r\n        return std::is_arithmetic_v&lt;T&gt; || std::is_enum_v&lt;T&gt;;\r\n    }\r\n\r\n    uint8_t GetUInt8() const\r\n    {\r\n        return to_scalar&lt;uint8_t&gt;();\r\n    }\r\n\r\n    \u301a repeat for the other Get(IntegralType) methods \u301b\r\n\r\n    float GetSingle() { throw hresult_not_implemented(); }\r\n\r\n    \u301a repeat for the other Get(NonIntegralType) methods \u301b\r\n\r\nprivate:\r\n    template &lt;typename To&gt;\r\n    To to_scalar() const\r\n    {\r\n        if constexpr (IsNumericScalar()) {\r\n            return static_cast&lt;To&gt;(m_value);\r\n        } else {\r\n            throw hresult_not_implemented();\r\n        }\r\n    }\r\n\r\n    T m_value;\r\n};\r\n<\/pre>\n<p>The <code>impl::<wbr \/>reference&lt;T&gt;<\/code> type is used for custom enumerations and custom structures. In both cases, the <code>IReference&lt;T&gt;::<wbr \/>Value()<\/code> produces the wrapped value.<\/p>\n<p>The remaining methods provide the implementation of <code>IPropertyValue<\/code>, which we noted last time is one of the hidden requirements of <code>IReference&lt;T&gt;<\/code>.<\/p>\n<p>If used for enumerations, <code>Is\u00adNumeric\u00adScalar()<\/code> reports <code>true<\/code>, and all of the <code>Get(IntegralType)<\/code> methods return the underlying integral value, cast to the requested integral type. On the other hand, for structures, <code>Is\u00adNumeric\u00adScalar()<\/code> reports <code>false<\/code>, and all of the <code>Get(IntegralType)<\/code> methods throw <code>hresult_<wbr \/>not_<wbr \/>implemented()<\/code>.<\/p>\n<p>For the floating point and other non-integral types, the methods always throw <code>hresult_<wbr \/>not_<wbr \/>implemented()<\/code>.<\/p>\n<p>It&#8217;s from reverse-engineering the C++\/WinRT implementation of <code>IReference<\/code> that we inferred the rules for its use. Being able to reverse-engineer the proper use of a library from reading its source code is a key under-appreciated software developer skill. Until you can infer the proper way of working with unfamiliar code, your career growth options will be limited. You&#8217;ll be one of those people who can only do things they are taught, without the ability to learn new things on their own.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Reverse-engineering the usages from the code.<\/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-107595","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Reverse-engineering the usages from the code.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107595","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=107595"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107595\/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=107595"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=107595"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=107595"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}