Reputation: 2125
In a code, that inserts the Windows style OLE strings (BSTR
's) into the good ol' STL container - the unordered_map<BSTR>
, I declared the unordered_set
, like this:
unordered_set<BSTR, std::hash<std::wstring>, std::equal_to<std::wstring>> usetBstr;
expecting BSTR
strings to act like wstring
s.
Everything seemed to work fine, until one day a BSTR
with a zero wchar
in the middle of the string was emplaced into the unordered_set
.
Of course!, when the compiler converts the BSTR
to a std::wstring
, it stops at the first null terminator it finds in the string, because wstring
s are not really BSTR
strings (which keep their length in memory word immediately preceding the BSTR data and wstring
s keep their length somewhere else).
Question: What Hash
and EqualTo
functions from the STL library would be appropriate for the unordered_map<BSTR>
in this case?
P.S. Of course I could roll out my own functions, but I prefer to wrap something already in the standard.
Upvotes: 0
Views: 212
Reputation: 180585
The problem with using std::hash<std::wstring>
and std::equal_to<std::wstring>
is that when the compiler converts the BSTR
to a std::wstring
, it stops at the first null terminator it finds in the string. This means "this\0\0"
and "this\0"
become the same string when it hashes and checks for equality.
What you need to provide an adapter that that passes a properly construct std::wstring
/std::wstring_view
to the hash and equality functions. You can do that by creating your own functors like
struct BSTR_hash
{
std::size_t operator()(BSTR const& s) const noexcept
{
return std::hash<std::wstring_view>{}({s, SysStringLen(s)});
}
};
struct BSTR_equal_to
{
bool operator()(BSTR const& lhs, BSTR const& rhs) const noexcept
{
return std::wstring_view(lhs, SysStringLen(lhs)) == std::wstring_view(rhs, SysStringLen(rhs));
}
};
unordered_set<BSTR, BSTR_hash, BSTR_equal_to> usetBstr;
Upvotes: 2