#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 или стандартную библиотеку