Yoksel: Блог/2008?/07?/26?/ПриключенияDynamicCastВ1С ...
SourceForge.net Logo

Home Page | Изменения / НовыеКомментарии / Справка / Помочь проекту | Вход:  Пароль:  

Блог

Приключения dynamic_cast в 1С


Как известно, при использовании dynamic_cast к объектам, порожденным 1С, вылетает исключение std::__non_rtti_object. Это нарушает семантику dynamic_cast, определенную стандартом C++, поэтому с подобным поведением надо что-то делать. Рассмотрим, например, следующее НЕПРАВИЛЬНОЕ решение:


Рассмотрим проблемы приведенного выше ослокода. Как должен работать dynamic_cast? Данная конструкция может работать как с указателями, так и со ссылками. Если dynamic_cast применяется к указателям, то, в случае неудачи, оператор должен вернуть NULL. Если dynamic_cast применяется к ссылкам, то, в случае неудачи dynamic_cast должен выкинуть исключение std::bad_cast. Итак, первая проблема рассматриваемого ослокода – некорректная работа со ссылками. Вернее вообще неработа со ссылками. Примерный путь решения этой проблемы может быть таким:



Т.е. реализованы две версии кастов: для указателей и для ссылок. Версия для указателей возвращает NULL в случае неудачи, а версия для ссылок в случае неудачи выкидывает std::bad_cast.


Однако оба этих кода непригодны для применения. Ни кривой код, приведенный в начале, ни более прямой, приведенный позднее. Дело здесь в исключении std::__non_rtti_object. Как правило, dynamic_cast применяется к объектам классов-наследников классов 1С. Например, CBLContext. Подобные классы, как правило, имеют виртуальные функции, поэтому dynamic_cast для подобных классов должен работать совершенно нормально. Так, спрашивается, а с какого хрена dynamic_cast выкидывает исключение, когда он должен вернуть NULL? А с такого хрена, что 1С собрана без поддержки RTTI. Когда dynamic_cast применяется к объекту, порожденному модулем без поддержки RTTI, то как раз и выкидывается std::__non_rtti_object. А как выкидывается std::__non_rtti_object? Это легко установить в отладчике: RTL пытается получить доступ к RTTI, RTTI отсутствует, возникает win32-исключение “Access violation”, этот AV ловится в RTL и ретранслируется в C++-исключение std::__non_rtti_object. Таким образом обе приведенные выше мегаконструкции основываются на использовании в своей логике “Access violation”... Да, вряд ли подобный подход можно считать нормальным при построении качественных решений...


Таким образом, выходит, что dynamic_cast можно использовать только ВНУТРИ своего модуля, где ГАРАНТИРОВАННО все будет в порядке с RTTI. А что же делать с 1С? Что если требуется информация о типах на этапе выполнения? В принципе, можно забить на проблемы вышеприведенных решений и их использовать. Подумаешь, AV... Задачу они решают, а при AV данные портиться вроде бы не должны. Однако, приведенные решения смогут работать только в том случае, когда мы ожидаем получить объект, порожденный нашим модулем. А что делать, если нам требуется получать объекты, порожденные именно в 1С? Например, требуется убедиться, что пользователь передает нам именно «СписокЗначений», а не какую-нибудь левую хрень? Ведь приведенные здесь решения совершенно не соответствуют ожидаемому поведению dynamic_cast: если объект нужного типа, вернуть правильный указатель, если объект неправильного типа – вернуть NULL или выкинуть исключение. А приведенные решения будут сигнализировать об ошибке даже в случае, когда объект нужного нам типа, но имел несчастье быть рожденным в 1С...


Грамотному читателю уже ясен путь решения. В самом деле, почему в 1С отключено использование RTTI? А не потому ли, что 1С построена практически полностью на MFC? В этой библиотеке реализована самодельная система RTTI, которая позволяет спокойно обойтись без RTTI C++. Соответственно, при широком использовании MFC необходимости в RTTI C++ почти нет. Может быть, даже без «почти». Итак, какую замену предоставляет MFC RTTI для dynamic_cast? Это макрос DYNAMIC_DOWNCAST и функция AfxDynamicDownCast. Вот как выглядит использование DYNAMIC_DOWNCAST:

Как можно видеть, аналогия с dynamic_cast полнейшая. Правда, есть лишнее приведение типа – т.к. используется нешаблонная функция AfxDynamicDownCast, которая возвращает ненулевой указатель на CObject в случае успешного каста.


Надо заметить, что MFC RTTI довольно часто используется во внешних компонентах, построенных по технике Rainbow. Правда, обычно происходит банальный вызов GetRuntimeClass и проверка имени класса. AfxDynamicDownCast делает то же самое, но идет немного дальше. Эта функция проходит по иерархии наследования классов и позволяет выполнить каст даже в случае, когда тип переданного объекта не в точности равен «заказанному», а представляет собой производный класс от нужного. Т.е. AfxDynamicDownCast – практически полный аналог dynamic_cast. И, главное – безопасный.


Ссылок на эту страницу нет


 
Файлов нет. [Показать файлы/форму]
Комментариев нет. [Показать комментарии/форму]