Написание приложений Win32 с помощью одних классов C++ (часть 2)

ОГЛАВЛЕНИЕ

Данная статья продолжает первую часть. Здесь к NLib добавляются другие возможности.

•    Скачать исходники - 52.8 Кб
•    Скачать исполняемый файл - 47.1 Кб

Подход

Соблюдается модульный подход: недопущение превращения библиотеки в каркас и использование компонентной модели (не обязательно COM или .NET: применяются собственные возможности языка), позволяющей использовать тот компонент при необходимости, без необходимости включать его или использовать при ненадобности.

Как в предыдущей статье, не делается ничего нового, что не было бы сделано раньше: есть много статей о том, как сделать то же самое с помощью MFC или WTL. Тут лишь испытывается другая техника программирования. Не изобретается ли колесо заново? Да, так и есть!

Но если MFC хорош для одного, а WTL – для другого, то нижеописанное сгодится для третьего!

Отправная точка

Это проект W2 из предыдущей статьи: малозаметное окно "Hello world", но уже с картами сообщений и доставкой команд. Был создан пустой проект W3 Win32, в него помещена копия источников W2, и был сделан ряд настроек (добавление "NLib" к решению, активация RTTI, установка "использовать предварительно скомпилированные заголовки" и "создать предварительно скомпилированный заголовок" для stdafx.cpp, установка зависимости W3 от NLib).

Некоторое изменение NLIB

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

Распаковщики сообщений

Макрос карты распаковщика сообщений был изменен, чтобы хранить функции, имеющие bool& bHandled в качестве последнего параметра. Макрос устанавливает это значение в «Истину» перед вызовом переданной функции, но данная схема позволяет установить его обратно в «Ложь». Благодаря установке в «Ложь» связанное действие не прерывается вызовом обработчика и может продолжиться с другим EWnd, впоследствии прикрепленным к тому же самому HWND. Во избежание путаницы этот макрос был переименован в ONMSG_xxx, а не в GETMSG_xxx.

Это очень удобно для отслеживания сообщения без вмешательства в его отправку.

Переделка оберток

При расширении оберток для создания дополнительных возможностей было обнаружено несколько изъянов, которые при отсутствии переделки налагают ряд ограничений на использование. И так как издержки переделки малы, оказалось удобным доработать имеющийся код:

В частности:
•    Были введены новые типы: CRH и COH: обычно они работают как CH и RH, но возвращаются функциями "const". Наоборот, старые типы возвращаются непостоянными функциями.
•    Были введены новые функции в классы traits_wrp_xxx: CAccess и CDereference: они возвращают COH и CRH из IH<CODE>.
•    В WrpBase оператор operator TT::OH() больше не является const и вызывает Access. Вместо него был введен оператор operator TT::COH()const, вызывающий CAccess.
•    В Wrp операторы operator->() и operator*() были переписаны в постоянной и непостоянной версиях и вызывают соответствующие функции типажей.

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

Строки

Многие Windows API определены через TCHAR. Хотя сравнительно легко сделать класс строк, хранящий TCHAR, путем определения std::basic_string<TCHAR>, реализация std::strings, представленная П.Дж. Плогером (по сути, специализация векторов) не очень эффективна: строки считаются "значениями" и всегда копируются.

Напротив, CString из MFC и ATL использует разделяемые векторы, клонирующиеся только при изменении значения строки. Для реализации похожей схемы было испытано переопределение "WrpBase и traits", позволяющее хранить разделяемые данные между обертками и осуществлять клонирование при попытке непостоянного доступа.

Хотя это работало, были издержки из-за внутренних вызовов между Wrp, WrpBase и traits. Поэтому была выбрана специальная реализация, более эффективная для данной конкретной цели (притом менее гибкая).

Был введен новый тип обертки (NWrp::SShare<T, nullval>) для автоматической генерации, автоматического клонирования и разделения буферов T.

Затем NWin::SString порождается с помощью NWrp::SShare<TCHAR, _T('\0')>, добавляя Left, Mid, Right, конструкторы и операторы.

Заметьте, что SString сам не является полноценным объектно-ориентированным классом (ни один из определенных тут классов не является таковым): это всего лишь диспетчер памяти. Строками манипулируют с помощью Windows API или строковой функции, взятой из tchar.h.

SString неявно преобразует в и из LPCTSTR. Для изменения буфера используется знак “-“ вместо GetBuffer(intwantedsize), при этом передавая требуемый размер или -1, чтобы фактический размер не изменился. GetBuffer производит копирование буфера, если фактический буфер разделяемый, затем соответственно изменяет его размер и возвращает его адрес.