Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 57 additions & 1 deletion include/wil/stl.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,11 +252,67 @@ inline auto str_raw_ptr(basic_zstring_view<TChar> str)

inline namespace literals
{
#if __WI_LIBCPP_STD_VER >= 20
/**
A statically-allocated, BSTR-shaped literal: a length-prefixed wide string whose data pointer is a valid
BSTR (usable with SysStringLen, SysStringByteLen, wcslen). No heap allocation; size is the literal's exact
length. Lifetime is tied to the literal object itself.

Example:
void Use(BSTR);
Use(L"foo"_bst);
*/
template <std::size_t N>
struct wchar_literal_storage
{
wchar_t value[N];
constexpr wchar_literal_storage(const wchar_t (&str)[N]) WI_NOEXCEPT
{
for (std::size_t i = 0; i < N; ++i)
{
value[i] = str[i];
}
}
};

template <std::size_t N>
wchar_literal_storage(const wchar_t (&)[N]) -> wchar_literal_storage<N>;

// N includes the trailing L'\0'; the BSTR byte-length prefix excludes it.
template <std::size_t N>
struct bstr_literal_t
{
unsigned long m_byte_length;
wchar_t m_data[N];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider: something like static_assert(offsetof(m_data) == 4)


constexpr bstr_literal_t(const wchar_t (&text)[N]) WI_NOEXCEPT :
m_byte_length(static_cast<unsigned long>((N - 1) * sizeof(wchar_t))), m_data{}
{
for (std::size_t i = 0; i < N; ++i)
{
m_data[i] = text[i];
}
}

WI_NODISCARD constexpr operator BSTR() const WI_NOEXCEPT
{
return const_cast<wchar_t*>(&m_data[0]);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need to guard against N=0? In that case this is getting the address of a zero-size buffer.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Zero sized arrays are not valid in C++

}
};

template <wchar_literal_storage Lit>
WI_NODISCARD constexpr auto operator""_bst() WI_NOEXCEPT

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My personal expectation is that this would be _bstr, but I don't use BSTRs enough to know how common of an "abbreviation" this is.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing worth mentioning is that "bst" could stand for "binary search tree" (not that that would make much sense for a user defined literal)

{
return bstr_literal_t<sizeof(Lit.value) / sizeof(wchar_t)>{Lit.value};

@dunhor dunhor Jul 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add something like:

static constexpr const std::size_t size = N;

or a (constexpr) size() method to wchar_literal_storage so you don't need the manual length calculation

}

#endif // __WI_LIBCPP_STD_VER >= 20

constexpr zstring_view operator""_zv(const char* str, std::size_t len) noexcept
{
return {str, len};
}

constexpr zwstring_view operator""_zv(const wchar_t* str, std::size_t len) noexcept
{
return {str, len};
Expand Down
19 changes: 17 additions & 2 deletions tests/StlTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ TEST_CASE("StlTests::TestZStringView", "[stl][zstring_view]")

TEST_CASE("StlTests::TestZWStringView literal", "[stl][zwstring_view]")
{

SECTION("Literal creates correct zwstring_view")
{
auto str = L"Hello, world!"_zv;
Expand All @@ -137,7 +137,22 @@ TEST_CASE("StlTests::TestZWStringView literal", "[stl][zwstring_view]")
REQUIRE(str[12] == L'!');
}
}


TEST_CASE("StlTests::TestBSTR literal", "[stl][bstr]")
{
#if __WI_LIBCPP_STD_VER >= 20
SECTION("Literal creates a valid BSTR")
{
const auto literal = L"foo"_bst;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should also include a constexpr validation

const BSTR value = literal;
REQUIRE(value != nullptr);
REQUIRE(SysStringLen(value) == 3);
REQUIRE(SysStringLen(L"zot"_bst) == 3);
REQUIRE(std::wstring_view(value) == L"foo");
}
#endif
}

TEST_CASE("StlTests::TestZStringView literal", "[stl][zstring_view]")
{

Expand Down
Loading