Тема: 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 вище
Сподіваюся, комусь це буде корисно (а може, хтось ще й поділиться своїм кращим досвідом).