使用 boost 或标准库的 wchar 参数
Posted
技术标签:
【中文标题】使用 boost 或标准库的 wchar 参数【英文标题】:wchar parameters using boost or the Standard Library 【发布时间】:2021-08-27 23:37:13 【问题描述】:我怎样才能让这段代码使用 boost C++ 字符串库或标准库,以避免wchar_t
大小定义,并拥有更容易处理的动态字符串?此代码使用 MFC 的 CString
,但我更喜欢使用标准库或 boost。
TCHAR drive[_MAX_DRIVE];
TCHAR dir[_MAX_DIR];
TCHAR fname[_MAX_FNAME];
TCHAR ext[_MAX_EXT];
CString cstr;
GetModuleFileName(NULL,cstr.GetBuffer(MAX_PATH),MAX_PATH);
cstr.ReleaseBuffer();
_wsplitpath_s(cstr,drive,dir,fname,ext);
cstr=drive;
cstr+=dir;
cstr+=_T("\\myfile.dat");
【问题讨论】:
没有 STL 类提供该功能,但您可以自己简单地实现它。从GetBuffer
返回一个临时对象,该对象在从内部缓冲区销毁时对引用的 STL 字符串执行赋值,并向指向该缓冲区的 wchar_t
提供强制转换运算符
这与STL
没有任何关系,它仅指标准库的容器和算法部分(尽管std::string
确实与它们互操作)。
【参考方案1】:
你应该看看 C++ 标准 <filesystem>
库,特别是它的 path
类,它有一个 replace_filename()
方法,例如:
#include <filesystem>
#include <windows.h>
WCHAR szFileName[MAX_PATH] = ;
GetModuleFileNameW(NULL, szFileName, MAX_PATH);
std:wstring str = std::filesystem::path(szFileName).replace_filename(L"myfile.dat").wstring();
或者,由于您仍然使用 Win32 API,您可以简单地使用 PathRemoveFileSpec()
和 PathAppend()
(或它们更安全的 Cch
对应物),例如:
#include <windows.h>
WCHAR szFileName[MAX_PATH] = ;
GetModuleFileNameW(NULL, szFileName, MAX_PATH);
PathRemoveFileSpecW(szFileName);
PathAppendW(szFileName, L"myfile.dat");
std:wstring str = szFileName;
如果你真的想避免MAX_PATH
的限制,你将不得不在循环中调用GetModuleFileName()
,在每次迭代中增加缓冲区的大小,直到它最终成功:
std::wstring wFileName(MAX_PATH, L'\0');
do
DWORD dwSize = GetModuleFileNameW(NULL, wFileName.data(), wFileName.size()); // or &wFileName[0] before C++17
if (dwSize < wFileName.size())
wFileName.resize(dwSize);
break;
wFileName.resize(wFileName.size() * 2);
while (true);
// use wFileName as needed...
【讨论】:
你在下半场忽略了 const 正确性。 “在当前编译器版本中工作”,但行为未定义,直接违反了std::wstring::c_str()
的 API 规范,该规范明确规定您不得修改内部字符串。
@Ext3h 我没有忽略它,这只是一个错字,我的意思是data()
。我已经修好了
前两种方法看起来很有趣,但最后一种带有循环我认为循环调用系统函数没有任何好处
我无法编译std::filesystem,除非我使用std::experimental::filesystem::path,我有VS2019和最新的boost库
@Ext3h 在 C++17 中引入了 data()
的非常量版本,专门用于修改字符串的内部数组数据。哪个 C++11 保证是连续的,使其适合作为输出缓冲区(在 大多数 实现中,在 C++11 之前已经可以安全地执行此操作,只是没有标准保证)【参考方案2】:
当您必须处理包含 C 样式的按引用返回参数的 API,但您想使用 RAI 之后的专有类型时,这个小助手就可以工作。
#include <cstddef>
#include <string>
#include <iostream>
/**
* Wrap an instance of class S for C-style return-by-reference
* with base-type T and maximum length N.
*
* Use is required when S only supports modification by methods,
* and direct modification of internal buffer is forbidden.
*/
template<class S, class T, size_t N = 1>
class ref_param
public:
ref_param() = delete;
ref_param(const ref_param&) = delete;
ref_param(ref_param&&) = delete;
ref_param(S& ref) : _ref(ref), _storage
~ref_param()
_ref = _storage;
operator T*()
return _storage;
private:
S& _ref;
T _storage[N];
;
// Specialization for single value
template<class S, class T>
class ref_param<S, T, 1>
public:
ref_param() = delete;
ref_param(const ref_param&) = delete;
ref_param(ref_param&&) = delete;
ref_param(S& ref) : _ref(ref), _storage
~ref_param()
_ref = _storage;
operator T*()
return &_storage;
private:
S& _ref;
T _storage;
;
extern "C"
// example function with C style return-by-reference
void foo(char* ret, size_t size)
for(size_t i = 0; i < size - 1; ++i)
ret[i] = i + 'a';
ret[size - 1] = 0;
int main()
std::string bar;
foo(ref_param<std::string, char, 32>(bar), 32);
std::cout << bar;
return 0;
这种模式也适用于其他类型,例如ComPtr
s,基本上所有在 C++ 端有赋值运算符的东西都可以工作。
【讨论】:
谢谢!你能添加另一个关于如何使用它的片段吗? 往下看,包括示例用法。 我看到您使用的是 char,这也适用于 wchar 吗? 如果您使用wchar_t
和std::wstring
,是的,它确实如此。或者只是 ref_param<decltype(bar), decltype(bar)::value_type, 32>(bar)
,如果您希望它适用于所有字符串类型。
我不知道为什么boost或标准库中不包含这样的功能以上是关于使用 boost 或标准库的 wchar 参数的主要内容,如果未能解决你的问题,请参考以下文章