{"id":109155,"date":"2023-12-15T07:00:00","date_gmt":"2023-12-15T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=109155"},"modified":"2023-12-15T09:41:00","modified_gmt":"2023-12-15T17:41:00","slug":"20231215-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20231215-00\/?p=109155","title":{"rendered":"How do I specify an optional string parameter to a Windows Runtime method?"},"content":{"rendered":"<p>Strings occupy a weird space in the Windows Runtime type system. They are formally classified as value types, even though they have reference behavior.<\/p>\n<p>The problem is that different languages treat strings differently.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Language<\/th>\n<th>String type<\/th>\n<th>Value or reference<\/th>\n<th>Mutable or immutable<\/th>\n<\/tr>\n<tr>\n<td>C++<\/td>\n<td><code>std::wstring<\/code><\/td>\n<td>Value<\/td>\n<td>Mutable<\/td>\n<\/tr>\n<tr>\n<td>C#<\/td>\n<td><code>String<\/code><\/td>\n<td>Reference<\/td>\n<td>Immutable<\/td>\n<\/tr>\n<tr>\n<td>JavaScript<\/td>\n<td><code>String<\/code><\/td>\n<td>Reference<\/td>\n<td>Immutable<\/td>\n<\/tr>\n<tr>\n<td>Python<\/td>\n<td><code>str<\/code><\/td>\n<td>Reference<\/td>\n<td>Immutable<\/td>\n<\/tr>\n<tr>\n<td>Rust<\/td>\n<td><code>String<\/code><\/td>\n<td>Value<\/td>\n<td>Mutable<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>To avoid creating problems over how to create an <code>IReference&lt;String&gt;<\/code> in languages where strings are already references, the Windows Runtime simply disallows <code>IReference&lt;String&gt;<\/code> outright.<\/p>\n<p>Okay, so what are your options here?<\/p>\n<p>If an empty string is an acceptable sentinel value to mean &#8220;No string&#8221;, then you can just take a <code>String<\/code>. Internally, <a title=\"Raymond's complete guide to HSTRING semantics\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20160615-00\/?p=93675\"> an empty <code>HSTRING<\/code> is represented by a null pointer<\/a>, so the two cases are indistinguishable at the ABI layer.<\/p>\n<pre>runtimeclass Widget\r\n{\r\n    Widget(String name);\r\n\r\n    \/\/ Property to get\/set the Name after construction\r\n    String Name;\r\n}\r\n<\/pre>\n<p>In this case, an empty name is probably what you were going to do anyway if somebody wanted to create a Widget without a name.<\/p>\n<p>In the above case, the optionality of the <code>name<\/code> parameter could be expressed by an overload that simply doesn&#8217;t have a <code>name<\/code> parameter at all.<\/p>\n<pre>runtimeclass Widget\r\n{\r\n    Widget();            \/\/ create with no name\r\n    Widget(String name); \/\/ create with explicit name\r\n\r\n    \/\/ Property to get\/set the Name after construction\r\n    String Name;\r\n}\r\n<\/pre>\n<p>Here&#8217;s another case where you might be tempted to have an optional string:<\/p>\n<pre>runtimeclass Connection\r\n{\r\n    void Connect(String token, String server);\r\n}\r\n<\/pre>\n<p>Maybe you want the token to be optional: If not provided, the code will obtain a new token.<\/p>\n<p>And maybe you want the server to be optional: If not provided, the code will use the default server.<\/p>\n<p>Since empty strings are probably not valid tokens or server names, you can use an empty string to mean &#8220;not provided&#8221;.<\/p>\n<p>But what if you want to distinguish between empty strings and no string at all?<\/p>\n<pre>runtimeclass WidgetFilter\r\n{\r\n    WidgetFilter();\r\n\r\n    Windows.Foundation.IReference&lt;Windows.UI.Color&gt; Color;\r\n    Windows.Foundation.IReference&lt;bool&gt; Polarity;\r\n    String Title;\r\n}\r\n\r\nruntimeclass WidgetFinder\r\n{\r\n    static IVectorView&lt;Widget&gt; FindAll(WidgetFilter filter);\r\n}\r\n<\/pre>\n<p>In this case, the <code>FindAll<\/code> method takes a <code>WidgetFilter<\/code> which lets you specify which widgets you are looking for. You can filter by color, by polarity, and by title.<\/p>\n<p>The color and polarity can be represented by an <code>IReference<\/code>, where null means &#8220;Don&#8217;t care&#8221;. But we can&#8217;t use an empty title string to mean &#8220;Don&#8217;t care&#8221;, because that would prevent us from filtering to widgets whose title is the empty string.<\/p>\n<p>This is, admittedly, a weird and unusual case.<\/p>\n<p>Unfortunately, the solution is also weird and unusual.<\/p>\n<p>To work around the inability to use an <code>IReference&lt;String&gt;<\/code>, you can box the string into an <code>Object<\/code>.<\/p>\n<pre>runtimeclass WidgetFilter\r\n{\r\n    WidgetFilter();\r\n\r\n    Windows.Foundation.IReference&lt;Windows.UI.Color&gt; Color;\r\n    Windows.Foundation.IReference&lt;bool&gt; Polarity;\r\n    Object Title; \/\/ null for \"don't care\" or a boxed string\r\n}\r\n\r\n\/\/ C++\/WinRT\r\nWidgetFilter filter;\r\nfilter.Title(nullptr);               \/\/ don't care\r\nfilter.Title(winrt::box_value(L\"\")); \/\/ empty string as title\r\n\r\n\/\/ C++\/CX\r\nWidgetFilter^ filter = ref new WidgetFilter();\r\nfilter-&gt;Title = nullptr;            \/\/ don't care\r\nfilter-&gt;Title = PropertyValue::CreateString(L\"\"); \/\/ (see discussion)\r\n\r\n\/\/ C#\r\nWidgetFilter filter = new WidgetFilter();\r\nfilter.Title = null;                \/\/ don't care\r\nfilter.Title = \"\";                  \/\/ empty string as title\r\n\r\n\/\/ JavaScript\r\nvar filter = new WidgetFilter();\r\nfilter.title = null;                \/\/ don't care\r\nfilter.title = \"\";                  \/\/ empty string as title\r\n<\/pre>\n<p>The C++\/CX case of boxing an empty string is awkward because C++\/CX tries to make strings look like objects. (Related reading: <a title=\"The C++\/CX String^ is not an object, even though it wears a hat\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20211230-00\/?p=106063\"> The C++\/CX <code>String^<\/code> is not an object, even though it wears a hat<\/a>.)<\/p>\n<p>To force C++\/CX to box an empty string to a non-null object, you put the empty string inside a <code>PropertyValue<\/code>, which implements <code>IReference&lt;String&gt;<\/code>, and then use that as the object.<\/p>\n<p>To recover the string, you need to unbox.<\/p>\n<pre>\/\/ C++\/WinRT\r\n\/\/ Version 1\r\nIInspectable title = filter.Title();\r\nif (title) {\r\n    add_title_filter(winrt::unbox_value&lt;hstring&gt;(title));\r\n}\r\n\r\n\/\/ Version 2\r\nIInspectable title = filter.Title();\r\nif (title) {\r\n    add_title_filter(title.as&lt;hstring&gt;());\r\n}\r\n\r\n\/\/ Version 3\r\nstd::optional&lt;hstring&gt; title = filter.Title().try_as&lt;hstring&gt;();\r\nif (title) {\r\n    add_title_filter(title.value());\r\n}\r\n\r\n\/\/ C++\/CX\r\nObject^ title = filter-&gt;Title;\r\nif (title) {\r\n    add_title_filter(safe_cast&lt;String^&gt;(title));\r\n}\r\n\r\n\/\/ C#\r\nobject title = filter.Title;\r\nif (title != null)\r\n{\r\n    add_title_filter((string)title);\r\n}\r\n\r\n\/\/ JavaScript\r\nvar title = filter.title;\r\nif (title != null)\r\n{\r\n    add_title_filter(title);\r\n}\r\n<\/pre>\n<p>The case of boxing a string where you need to distinguish between &#8220;no string&#8221; and &#8220;an empty string&#8221; is a weird and unusual case in the Windows Runtime. The best approach is to try to design your API so you don&#8217;t ever need to do it.<\/p>\n<p>For example, you could change the <code>Title<\/code> property to a pair of methods.<\/p>\n<pre>runtimeclass WidgetFilter\r\n{\r\n    WidgetFilter();\r\n\r\n    Windows.Foundation.IReference&lt;Windows.UI.Color&gt; Color;\r\n    Windows.Foundation.IReference&lt;bool&gt; Polarity;\r\n\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">void SetTitleFilter(String title);<\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">void ClearTitleFilter();          <\/span>\r\n}\r\n<\/pre>\n<p>In this way, a caller can specify &#8220;I am looking for an empty title&#8221; by calling <code>Set\u00adTitle\u00adFilter(\"\")<\/code>, or they can specify &#8220;I don&#8217;t care about the title&#8221; by either never specifying a title filter or by clearing any previous title filter by calling <code>Clear\u00adTitle\u00adFilter()<\/code>.<\/p>\n<p>On the implementation side, you can record the filter in a private <code>std::<wbr \/>optional&lt;<wbr \/>winrt::<wbr \/>hstring&gt;<\/code> to inspect whether a filter has been applied.<\/p>\n<p>Another option is to use a &#8220;nullable string <a href=\"https:\/\/knowyourmeme.com\/memes\/we-have-food-at-home\">at home<\/a>&#8221; by pairing it with a Boolean property.<\/p>\n<pre>runtimeclass WidgetFilter\r\n{\r\n    WidgetFilter();\r\n\r\n    Windows.Foundation.IReference&lt;Windows.UI.Color&gt; Color;\r\n    Windows.Foundation.IReference&lt;bool&gt; Polarity;\r\n\r\n    <span style=\"border: solid 1px currentcolor;\">Boolean UseTitle;<\/span>\r\n    String Title;\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Strings are sort of reference but sort of values.<\/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-109155","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Strings are sort of reference but sort of values.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109155","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=109155"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109155\/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=109155"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=109155"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=109155"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}