{"id":109922,"date":"2024-06-20T07:00:00","date_gmt":"2024-06-20T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=109922"},"modified":"2024-06-20T09:35:36","modified_gmt":"2024-06-20T16:35:36","slug":"20240620-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240620-00\/?p=109922","title":{"rendered":"How to convert between different types of counted-string string types"},"content":{"rendered":"<p>Last time, we learned about <a title=\"On the sadness of treating counted strings as null-terminated strings\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240619-00\/?p=109915\"> the sadness of treating counted strings as null-terminated strings<\/a>. How can we escape this cycle of depression?<\/p>\n<p>Almost all of the counted-string types have a way of creating an instance with a pointer and a count. The trick is to use it.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Class<\/th>\n<th>Creator<\/th>\n<\/tr>\n<tr>\n<td><code>std::wstring<\/code><\/td>\n<td><code>std::wstring(p, n)<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>HSTRING<\/code><\/td>\n<td><code>WindowsCreateString(p, n, &amp;s)<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>Platform::String<\/code><\/td>\n<td><code>ref new String(p, n)<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>winrt::hstring<\/code><\/td>\n<td><code>winrt::hstring(p, n)<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>BSTR<\/code><\/td>\n<td><code>SysAllocStringLen(p, n)<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>_bstr_t<\/code><\/td>\n<td><code>_bstr_t(SysAllocStringLen(p, n), FALSE)<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>CComBSTR<\/code><\/td>\n<td><code>CComBSTR(n, p)<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>CStringW<\/code><\/td>\n<td><code>CStringW(p, n)<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In most cases, the creator is fairly obvious. The one weirdo is <code>_bstr_t<\/code>, which has no built-in constructor for counted strings. Instead, you have to use the underlying <code>Sys\u00adAlloc\u00adString\u00adLen<\/code> function to create the counted string, and then pass it to the constructor in attach mode (<code>bCopy = FALSE<\/code>).<\/p>\n<p>Of course, once you get the counted string in, you need a way to get it back out.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Class<\/th>\n<th>Pointer and length<\/th>\n<\/tr>\n<tr>\n<td><code>std::wstring<\/code><\/td>\n<td><code>s.c_str(), s.size()<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>HSTRING<\/code><\/td>\n<td><code>WindowsGetStringRawBuffer(s, &amp;n), n<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>Platform::String<\/code><\/td>\n<td><code>s-&gt;Data(), s-&gt;Length()<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>winrt::hstring<\/code><\/td>\n<td><code>s.c_str(), s.size()<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>BSTR<\/code><\/td>\n<td><code>s, SysStringLen(s)<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>_bstr_t<\/code><\/td>\n<td><code>static_cast&lt;wchar_t*&gt;(s), s.length()<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>CComBSTR<\/code><\/td>\n<td><code>static_cast&lt;BSTR&gt;(s), s.Length()<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>CStringW<\/code><\/td>\n<td><code>static_cast&lt;wchar_t const*&gt;(s), s.GetLength()<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The <code>static_cast<\/code>s in the final three rows are typically not necessary if the compiler knows that it has to produce a <code>wchar_t const*<\/code>.<\/p>\n<p>Unfortunately, combining these two tables gives you a horrible <var>n<\/var> \u00d7 <var>n<\/var> matrix of possibilities. Fortunately, you can reduce this to linear complexity by using <code>std::wstring_view<\/code> as a common currency.<\/p>\n<pre>std::wstring_view to_wstring_view(std::wstring const&amp; s)\r\n{\r\n    return s;\r\n    \/\/ or: return { s.c_str(), s.size() };\r\n}\r\n\r\nstd::wstring_view to_wstring_view(HSTRING s)\r\n{\r\n    UINT32 size;\r\n    auto p = WindowsGetStringRawBuffer(s, &amp;size);\r\n    return { p, size };\r\n}\r\n\r\nstd::wstring_view to_wstring_view(Platform::String^ const&amp; s)\r\n{\r\n    return { s-&gt;Data(), s-&gt;Length() };\r\n}\r\n\r\nstd::wstring_view to_wstring_view(winrt::hstring const&amp; s)\r\n{\r\n    return { s.c_str(), s.size() };\r\n}\r\n\r\nstd::wstring_view to_wstring_view(wchar_t*) = delete;\r\n\r\nstd::wstring_view bstr_to_wstring_view(BSTR s)\r\n{\r\n    return { s, SysStringLen(s) };\r\n}\r\n\r\nstd::wstring_view to_wstring_view(_bstr_t const&amp; s)\r\n{\r\n    return { s, s.length() };\r\n}\r\n\r\nstd::wstring_view to_wstring_view(CComBSTR const&amp; s)\r\n{\r\n    return { s, s.Length() };\r\n}\r\n\r\nstd::wstring_view to_wstring_view(CStringW const&amp; s)\r\n{\r\n    return { s, s.GetLength() };\r\n}\r\n<\/pre>\n<p>For <code>std::<wbr \/>wstring<\/code>, we can take advantage of the pre-existing <code>wstring_view<\/code> conversion operator.<\/p>\n<p>Note that the <code>BSTR<\/code> version has to be named <code>bstr_<wbr \/>to_<wbr \/>wstring_<wbr \/>view<\/code> because <code>BSTR<\/code> is a type alias for <code>wchar_t*<\/code>, so just calling it <code>to_<wbr \/>wstring_<wbr \/>view<\/code> would make it too easy to pass <code>wchar_t*<\/code>s that aren&#8217;t really <code>BSTR<\/code>s. We delete the <code>to_wstring_view(wchar_t*)<\/code> overload so that the compiler won&#8217;t choose to convert the pointer to a <code>std::wstring<\/code> and then use the <code>std::wstring<\/code> overload, which would result in a dangling pointer.\u00b9<\/p>\n<p>The other half is creating the counted string from a <code>wstring_<wbr \/>view<\/code>.<\/p>\n<pre>template&lt;typename T&gt;\r\nT size_check(std::wstring_view v)\r\n{\r\n    if (v.size() &gt; (std::numeric_limits&lt;T&gt;::max)()) {\r\n        throw std::bad_alloc();\r\n    }\r\n    return static_cast&lt;T&gt;(v.size());\r\n}\r\n\r\ntemplate&lt;typename T&gt;\r\nT from_wstring_view(std::wstring_view v) = delete;\r\n\r\ntemplate&lt;&gt;\r\nstd::wstring from_wstring_view&lt;std::wstring&gt;(std::wstring_view v)\r\n{\r\n    return std::wstring(v.data(), v.size());\r\n}\r\n\r\ntemplate&lt;&gt;\r\nHSTRING from_wstring_view(std::wstring_view v)\r\n{\r\n    HSTRING s;\r\n    THROW_IF_FAILED(\r\n        WindowsCreateString(v.data(),\r\n                            size_check&lt;UINT32&gt;(v)));\r\n    return s;\r\n}\r\n\r\ntemplate&lt;&gt;\r\nPlatform::String^ from_wstring_view&lt;Platform::String^&gt;(std::wstring_view v)\r\n{\r\n    return ref new Platform::String(\r\n        v.data(),\r\n        size_check&lt;UINT32&gt;(v));\r\n}\r\n\r\ntemplate&lt;&gt;\r\nwinrt::hstring from_wstring_view&lt;winrt::hstring&gt;(std::wstring_view v)\r\n{\r\n    return winrt::hstring(v.c_str(), size_check&lt;uint32_t&gt;(v));\r\n}\r\n\r\ntemplate&lt;&gt;\r\nBSTR from_wstring_view&lt;BSTR&gt;(std::wstring_view v)\r\n{\r\n    return ::SysAllocStringLen(v.data(), size_check&lt;UINT&gt;(v));\r\n}\r\n\r\ntemplate&lt;&gt;\r\n_bstr_t from_wstring_view&lt;_bstr_t&gt;(std::wstring_view v)\r\n{\r\n    return _bstr_t(\r\n        ::SysAllocStringLen(v.data(), size_check&lt;UINT&gt;(v)),\r\n        FALSE);\r\n}\r\n\r\ntemplate&lt;&gt;\r\nCComBSTR from_wstring_view&lt;CComBSTR&gt;(std::wstring_view v)\r\n{\r\n    return CComBSTR(size_check&lt;int&gt;(v), v.data());\r\n}\r\n\r\ntemplate&lt;&gt;\r\nCStringW from_wstring_view&lt;CStringW&gt;(std::wstring_view v)\r\n{\r\n    return CStringW(v.data(), size_check&lt;int&gt;(v));\r\n}\r\n<\/pre>\n<p>You can now convert from one type to another through the pair of functions <code>to_<wbr \/>wstring_<wbr \/>view<\/code> and <code>from_<wbr \/>wstring_<wbr \/>view<\/code>.<\/p>\n<pre>\/\/ _bstr_t to winrt::hstring\r\nauto s = from_wstring_view&lt;winrt::hstring&gt;(to_hstring_view(b));\r\n<\/pre>\n<p>You could wrap this in an adapter class, with a bunch of custom conversion operators:<\/p>\n<pre>struct counted_string_converter\r\n{\r\n    std::wstring_view v;\r\n\r\n    template&lt;typename T&gt;\r\n    counted_string_converter(T const&amp; source) :\r\n        v(to_wstring_view(source)) {}\r\n\r\n    counted_string_converter(std::wstring_view source) :\r\n        v(source) {}\r\n\r\n    static counted_string_converter from_bstr(BSTR source)\r\n    {\r\n        return counted_string_converter(\r\n            bstr_to_wstring_view(source));\r\n    }\r\n\r\n    template&lt;typename T&gt;\r\n    operator T() const {\r\n        return from_wstring_view&lt;T&gt;(v);\r\n    }\r\n};\r\n\r\nwinrt::hstring GetName();\r\n\r\nvoid example()\r\n{\r\n    std::wstring s = counted_string_converter(GetName());\r\n}\r\n<\/pre>\n<p>The danger with the <code>counted_<wbr \/>string_<wbr \/>converter<\/code> is that you might try to store it in an <code>auto<\/code>, which could produce dangling pointers.<\/p>\n<pre>winrt::hstring GetName();\r\n\r\nvoid example()\r\n{\r\n    \/\/ Oops: Reference to destroyed temporary\r\n    auto s = counted_string_converter(GetName());\r\n}\r\n<\/pre>\n<p>All of this is really awkward, and the simplest approach might be to create custom case-by-case conversions. After all, it&#8217;s unlikely that any one program is going to need all <var>n<\/var> \u00d7 <var>n<\/var> conversions.<\/p>\n<p><b>Bonus chatter<\/b>: The <code>to_wstring_view<\/code> is handy if you want to insert these counted strings into a C++ stream.<\/p>\n<pre>BSTR s = \u27e6something\u27e7\r\n\r\n\/\/ This stops at the embedded null.\r\nstd::wcout &lt;&lt; s;\r\n\r\n\/\/ This prints the whole string.\r\nstd::wcout &lt;&lt; bstr_to_wstring_view(s);\r\n<\/pre>\n<p>\u00b9 If you really want to construct a <code>wstring_view<\/code> from a pointer to a null-terminated C-style string, then just use the <code>wstring_view<\/code> constructor.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Looking for constructors that take a character count.<\/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-109922","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Looking for constructors that take a character count.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109922","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=109922"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109922\/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=109922"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=109922"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=109922"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}