有没有一种方法可以编写BSTR字面量?

6

调用期望BSTR的函数时,希望能够像这样编写:

iFoo->function( bs"HELLO" );

然而,我所知道的唯一解决方法是使用一个调用SysAllocString等函数的包装器,例如:

iFoo->function( WideString(L"HELLO").c_bstr() );

这样有点丑陋。是否实际上有一种选项可以创建 BSTR 文字?

动机:更易读的代码,通过避免分配和释放来提高运行时性能。

澄清:我只谈到了调用者(即我们)拥有BSTR的情况,例如:调用一个接受BSTR [in] 参数的函数。当然,向试图释放字符串的函数提供指向 BSTR 文字的指针是愚蠢的。


1
不仅丑陋,等到有人在上面调用 SysFreeString() 就更糟了 :) - Frédéric Hamidi
1
BSTR的定义是动态管理的。这个概念当然可以被扔进一个字面上,但它不会成为一个BSTR。如果在最终被释放的地方使用它,那将是灾难性的。OLE库有许多地方(变量函数、编组器代码等)在幕后管理VARIANT成员之类的东西。在这样的地方放置类似的东西将是灾难性的。你总是可以使用任何预制的BSTR智能指针类,如bstr_tCComBSTR - WhozCraig
2
回答自己的问题...我猜测一些内存检查工具可能会尝试检查提供给期望BSTR的函数的任何指针是否实际上与OLE分配表中存在的内容相对应。 - M.M
1
@MattMcNabb 这是一个好问题。由于所有的 BSTR 都应该遵守规则,因此它的构建不取决于你,而是取决于微软。他们会记录它们,这很好,但他们也可以自由地更改它们。试图字面化它很容易,甚至比遵守规则更繁琐。例如:您提出的“非常丑陋”的解决方案不符合规范(它没有两个终止空值)。为什么要那样做呢?而且你可以完全放弃 COM 为你执行的 BSTR 缓存。 - WhozCraig
2
@WhozCraig 刚试了一下,这里是结果 ... - M.M
显示剩余13条评论
2个回答

4
为了跟进@MSalters的回答,一个自定义用户定义的字面量可能会看起来像这样:
CComBSTR operator "" _bstr (const char* str, std::size_t len)
{
    return CComBSTR(len, str);
}

那么你可以这样做(因为CComBSTR定义了一个BSTR转换运算符):

iFoo->function( "HELLO"_bstr );

你甚至可以为多个输入字符串字面类型重载运算符:

CComBSTR operator "" _bstr (const wchar_t* str, std::size_t len)
{
    return CComBSTR(len, str);
}

CComBSTR operator "" _bstr (const char16_t* str, std::size_t len)
{
    return CComBSTR(len, (wchar_t*)str);
}

iFoo->function( L"HELLO"_bstr ); // calls wchar_t* version with UTF-16 encoded data

iFoo->function( u"HELLO"_bstr ); // calls char16_t* version with UTF-16 encoded data

iFoo->function( u8"HELLO"_bstr ); // calls char* version with UTF-8 encoded data...

注意最后一种情况。因为操作符无法知道是否传递的是ANSI或UTF-8数据,而CComBSTR在传递char*数据时假定为ANSI,所以您应该使用不同的字面量后缀来区分它们,以便正确转换UTF-8,例如:

CComBSTR operator "" _utf8bstr (const char* str, std::size_t len)
{
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> conv;
    std::wstring wstr = conv.from_bytes(std::string(str, len));
    return CComBSTR(wstr.length(), wstr.c_str());
}

iFoo->function( u8"HELLO"_utf8bstr );

这实际上看起来是使用用户定义字面量的好例子。 - MSalters
1
必须小心,不要写成BSTR b = "hello"_bstr; - M.M
是的,您需要使用 CComBSTR b = "hello"_bstr; 或更好的 auto b = "hello"_bstr; - Remy Lebeau

4

用户自定义字面量是一个不错的选择:

"HELLO"_bstr 调用了 template<char...> BSTR operator "" _bstr ( const char*, std::size_t),该函数可以调用 SysAllocString()

在VS14中新增。

[编辑]

根据评论,最好返回一个 _bstr_t 或其他类,该类接管了 SysAllocString() 的结果并隐式转换为 BSTR。该临时对象将在完整表达式结束后销毁,因此在 iFoo->function( "HELLO"_bstr ); 返回之后才会被销毁。


1
这解决了“丑陋的语法”问题,但如果像 iFoo->function( "hello"_bstr ); 这样使用会导致内存泄漏。 - M.M
@MattMcNabb:当iFoo->function返回时,析构函数不是被调用了吗? - TonyK
1
@TonyK:BSTR只是一个指针,如果操作符直接返回BSTR,就没有析构函数可供调用。您需要使操作符返回类的实例,该类可以定义一个BSTR转换操作符。 - Remy Lebeau
1
@MSalters:一个被标记为[out]的参数必须通过地址传递,而不是通过值传递。因此,您将无法将BSTR传递给[out]参数,因为它期望的是BSTR*。因此,返回BSTR(直接或间接)的自定义operator""_bstr只能与[in]参数一起使用。 - Remy Lebeau
@MSalters:是的,没错。我已经添加了一个示例来证明它。 - Remy Lebeau
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接