параметры wchar с использованием boost или стандартной библиотеки

#c #boost

Вопрос:

Как я могу заставить этот код использовать библиотеку строк 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");
 

Комментарии:

1. Ни один класс STL не обеспечивает этого, но вы можете реализовать его самостоятельно. Возвращает временный объект, из GetBuffer которого выполняется присвоение в указанную строку STL при уничтожении из внутреннего буфера, и предоставляет оператор приведения wchar_t , который указывает на этот буфер

2. На самом деле это не имеет никакого отношения к тому, STL что относится только к частям контейнеров и алгоритмов Стандартной библиотеки (хотя std::string и взаимодействует с ними).

Ответ №1:

Вам следует взглянуть на стандартную <filesystem> библиотеку C , в частности на ее 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'');
do {
    DWORD dwSize = GetModuleFileNameW(NULL, wFileName.data(), wFileName.size()); // or amp;wFileName[0] before C  17
    if (dwSize < wFileName.size()) {
        wFileName.resize(dwSize);
        break;
    }
    wFileName.resize(wFileName.size() * 2);
}
while (true);
// use wFileName as needed...
 

Комментарии:

1. Вы проигнорировали корректность во второй половине. «Работает в текущих версиях компилятора», но является неопределенным поведением и прямым нарушением спецификации API std::wstring::c_str() , в которой явно указано, что вы никогда не должны изменять внутреннюю строку.

2. @Ext3h Я не игнорировал это, это была просто опечатка, я имел в виду data() вместо этого. Я все исправил

3. первые два метода выглядят интересно, но последний с циклом я не вижу ничего хорошего в вызове системной функции в цикле

4. Я не могу скомпилировать файловую систему std::, если я не использую std::экспериментальную::файловую систему::путь, у меня есть VS2019 и последняя библиотека boost

5. @Ext3h data() в C 17 была введена неконстантная версия, специально предназначенная для изменения данных внутреннего массива строки. Что гарантирует C 11, является непрерывным, что делает его подходящим в качестве выходного буфера (что уже было безопасно делать до C 11 в большинстве реализаций, просто без стандартной гарантии)

Ответ №2:

Этот маленький помощник работает всякий раз, когда вам приходится иметь дело с API, которые содержат параметры возврата по ссылке в стиле C, но вы хотите использовать исключительно типы, следующие за 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_paramamp;) = delete;
    ref_param(ref_paramamp;amp;) = delete;
    
    ref_param(Samp; ref) : _ref(ref), _storage{} {}
    ~ref_param()
    {
        _ref = _storage;
    }
    
    operator T*()
    {
        return _storage;
    }
    
private:
    Samp; _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_paramamp;) = delete;
    ref_param(ref_paramamp;amp;) = delete;
    
    ref_param(Samp; ref) : _ref(ref), _storage{} {}
    ~ref_param()
    {
        _ref = _storage;
    }
    
    operator T*()
    {
        return amp;_storage;
    }
    
private:
    Samp; _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 , просто работает.

Комментарии:

1. Спасибо! Можете ли вы добавить еще один отрывок о том, как это использовать?

2. Взгляните ниже, пример использования включен.

3. Я вижу, что вы используете char, это также работает с wchar ?

4. Если вы используете wchar_t и std::wstring , да, это так. Или просто ref_param<decltype(bar), decltype(bar)::value_type, 32>(bar) , если вы хотите, чтобы он был универсальным для всех типов строк.

5. Я не знаю, почему такая функция не включена в boost или стандартную библиотеку