1

Тема: WinAPI - як працювати з LPTSTR (однобайтові і багатобайтові рядки).

Зараз працюю із великим проектом, який розробляло багато різних людей із різними стилями. Колись стрічки були однобайтними std::string, потім місцями перейшли на std::wstring
<MODE ="SARCASM">

typedef string std::wstring; //happy debugging!

</MODE>
І, схоже, у кожного другого був свій підхід до WinAPI. В параметри LPTSTR (що розшифровується як Long Pointer to Text String) пхають:

wchar_t *str = new wchar[size];
SomeWinAPIFunction(str, size);
...
delete str;

або

wstring str;
str.reserve(size);
SomeWinAPIFunction(str.c_str(), size);

і навіть

std::vector<char> buffer(sizof(wchar_t)*size);
SomeWinAPIFunction(buffer.data(),size);

Ну так от, це все неправильні підходи. Розберемося. "Long Pointer" - це в сучасній процесорній архітектурі просто вказівники (Long було актуально в часи 16-бітних процесорів, щоб відрізняти 16- і 32-бітні вказівники). Text String - це 0-термінований неперервний масив TCHAR, а TCHAR залежно від налаштувань проекту - це CHAR або WCHAR, які, своєю чергою, є псевдонімами char та wchar_t.
std::wstring та std::vector не підходять. Це по суті рядок та масив довільної довжини; так, всередині вони зберігають потрібну кількість байтів, але пара рядків між проголошенням і викликом WinAPI - і ці байти можуть опинитися у зовсім іншому місці, а функція WinAPI спробує писати туди, де вони були. std::vector<TCHAR> виглядає краще, але все одно трохи не те. new TCHAR[size] ніби підходить, але треба не забути його видалити. Якщо у вас 10 таких буферів, це може створити проблему. Масиви довільної довжини (Variable-length arrays, VLA) могли б врятувати ситуацію, але поки що Visual Studio каже "ні".

Моє поточне рішення:

auto str = std::make_unique<TCHAR[]>(size);
SomeWinAPIFunction(str.get(),size);

Переваги:
1. std::unique_ptr гарантує звільнення пам'яті (delete) зі своїм знищенням.
2. Використання std::make_unique дозволяє уникнути передачі неправильного посилання в конструктор unique_ptr на кшталт

std::unique_ptr<TCHAR []> p1(new TCHAR[20]);
std::unique_ptr<TCHAR []> p2(p1.get()); //а тепер ми двічі викликаємо delete при знищенні p2 і p1

3. Ключове слово auto дозволяє уникнути повторень на кшталт

std::unique_ptr<My_long_type_name> ptr= std::unique_ptr<My_long_type_name>(new std::unique_ptr<My_long_type_name>);

4. Дані, що повертаються, саме того типу, що ми запитували. Уникаються дурні помилки

std::wstring data(buffer.begin(),buffer.end());//а тепер гляньте ще раз на визначення buffer вище

Сподіваюся, комусь це буде корисно (а може, хтось ще й поділиться своїм кращим досвідом).

2

Re: WinAPI - як працювати з LPTSTR (однобайтові і багатобайтові рядки).

Колись була пропозиція додати до стандарту клас dynarray, щоб уникнути unique_ptr на масив, але її так і не втілили наче через те. що не знали як краще реалізувати у випадку з малими масивами, думали їх на стек по замовчанню класти.

Я колись написав собі такий клас dynarray.h, і дуже радію з цього.

Подякували: koala, leofun01, Arete, М18х004