February 16, 2020

Что такое __wchar_t и почему я получаю ошибки, связанные с этим?

Компилятор Microsoft Visual C++ имеет параметр с названием /Zc:wchar_t, который позволяет управлять значением wchar_t.

Согласно стандартам в C++, wchar_t является отдельным типом, что компилятор Visual C++ использует по умолчанию. Тем не менее, вы можете использовать /Zc:wchar_t, что позволит определять wchar_t так, как вам нужно. В Windows это исторически обозначается, как

typedef unsigned short wchar_t;

, потому что Windows предшествует версиям C и C++, в которых wchar_t вводится в качестве собственного типа.

Проблема возникает, если вы пишите библиотеку, которая будет пользоваться как старым кодом, так и новым. Какой тип данных вы используете для строковых параметров?

Что ж, если ваша библиотека привязана к C, то вам повезло. Встроенный wchar_t представляет собой 16-разрядное целое число без знака в Visual C++, что совместимо с двоичными данными с unsigned short, поэтому вы можете объявить свою функцию как принимающую wchar_t в заголовочном файле и каждый клиент сможет интерпретировать её через собственный wchar_t-color: Код /Zc:wchar_t будет видеть собственный wchar_t. Код /Zc:wchar_t- увидит unsigned short. А в C вы сможете экспортировать функцию, которая примет значение wchar_t.

Это работает для недекорированных функций, но как насчет C++, который использует декорирование для кодирование типов параметров? Что же использовать?

Попробуйте написать две версии вашей функции, одна будет использовать unsigned short, а другая - __wchar_t.

Другими словами, /Zc:wchar_t приводит к тому, что компилятор внутри делает эквивалент:

typedef __wchar_t wchar_t;

Оно делает символ wchar_t псевдонимом для внутреннего типа __wchar_t.

Допустим, у вас есть функция DoSomething, которая использует широкую строку и вы хотите принимать клиентов, скомпилированных с любым параметром для /Zc:wchar_t.

// Something.h

bool DoSomething(const __wchar_t* s);
bool DoSomething(const unsigned short* s);

Оно объявляет две версии функции. Первой будет соответствовать код, скомпилированный из /Zc:wchar_t. Второй будет скомпилирован из /Zc:wchar_t-.

Это будет выглядеть так:

// Something.cpp
#include <Something.h>

bool DoSomethingWorker(const wchar_t* s)
{
 ... implementation ...
}

bool DoSomething(const __wchar_t* s)
{
    return DoSomethingWorker(reinterpret_cast<const wchar_t*>(s));
}

bool DoSomething(const unsigned short* s)
{
    return DoSomethingWorker(reinterpret_cast<const wchar_t*>(s));
}

Как отмечалось ранее, вызов программы, компилирующей /Zc:wchar_t будет соответствовать первой версии DoSomething, а /Zc:wchar_t- - второй. Но оба они объединяются, образуя wchar_t, так что она соответствует настройке /Zc:wchar_t, которая используется библиотекой.

Итак __wchar_t - это имя внутреннего типа данных для широких строк. Если вы компилируете с /Zc: wchar_t, то этот тип данных wchar_t. Забавное имя существует для того, чтобы код, скомпилированный с /Zc: wchar_t-, мог также получить к нему доступ, и чтобы код / Zc: wchar_t-agnostic, мог ссылаться на внутренний нативный тип.

Перевод: https://devblogs.microsoft.com/oldnewthing/20161201-00/?p=94836