使用 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;

这种模式也适用于其他类型,例如ComPtrs,基本上所有在 C++ 端有赋值运算符的东西都可以工作。

【讨论】:

谢谢!你能添加另一个关于如何使用它的片段吗? 往下看,包括示例用法。 我看到您使用的是 char,这也适用于 wchar 吗? 如果您使用wchar_tstd::wstring,是的,它确实如此。或者只是 ref_param&lt;decltype(bar), decltype(bar)::value_type, 32&gt;(bar),如果您希望它适用于所有字符串类型。 我不知道为什么boost或标准库中不包含这样的功能

以上是关于使用 boost 或标准库的 wchar 参数的主要内容,如果未能解决你的问题,请参考以下文章

Windows下Boost库的安装与使用

C 标准函式库的历史沿革

Boost标准库安装

Boost标准库安装

VS2015编译boost1.62

qsort 的第四个参数的“不兼容的指针类型”编译器警告