使用 boost 或标准库的 wchar 参数

wchar parameters using boost or the Standard Library

如何使此代码使用 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");

你应该看看 C++ 标准 <filesystem> library, specifically its path class, which has a 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() and 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'[=12=]');
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...

只要您必须处理包含 C 风格 return 引用参数的 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++ 端具有赋值运算符的东西都可以使用。