|
Программирование >> Обобщенные обратные вызовы
Коротко говоря, героические усилия Александреску по втискиванию своего шаблона variant в узкие рамки стандарта привели к очень близкому к полностью переносимому решению. У него только один слабый мо.меит, причем на практике достаточно переносимый, несмотря на его выход за рамки гарантий стандарта. Главная его проблема (даже если опустить вопросы, связанные с выравниванием) в том, что код Variant настолько сложен и продвинут, что будет работать только на очень небольшом количестве компиляторов; по крайней мере, лично мне удалось скомпилировать его код только на одном компиляторе. Ключевая часть подхода Александреску заключается в попытке обобщить идею max al i gn, чтобы сделать ее библиотечным средством, которое соответствует стандарту С++. Это делается, в частности, для того, чтобы иметь возможность решать проблемы относительно безопасного использования нединамического символьного буфера. Александреску приложил героические усилия по использованию метапрограммми-рования с использованием шаблонов для вычисления безопасного выравнивания. Будет ли переносимым его решение? Вот что он пишет по этому поводу: ...приведенная выше реализация не является 100% переносимой для всех типов. Теоретически возможна реализация компилятора, который соответствует стандарту, но будет некорректно работать с размеченньши объединениями. Это связано с тем, что стандарт не гарантирует, что все пользовательские типы будут иметь такое же выравнивание, как один из обычных старых типов (POD-munoe). Однако вряд ли такой компилятор появится на практике, а не только в воспаленном воображении знатоков стандарта. [...] Переносимое вычисление выравнивания - дело трудное, но выполнимое. Но оно не может быть на 100% переносимым. - [Alexandrescu02] У подхода Александреску есть и другие ключевые возможности, в частности, шаблон объединения, который получает в качестве параметра шаблон typelist со списком типов, объекты которых могут в нем содержаться, поддержка инспектирования, обсспсчи-вающая расширяемость, и методика реализации, состоящая в подделке viable , что позволяет повысить эффективность за счет отказа от лишнего уровня косвенности при обращении к содержащемуся в объединении типу. Все эти части существенно более тяжеловесны по сравнению с boost; :апу, но теоретически переносимы. Примечание теоретически существенно, поскольку, например, в той же книге [AlexandrescuOl] часто встречаются комментарии в шаблонах наподобие Гарантированно вызовет внутреннюю ошибку компилятора у [список различных распространенных компиляторов] , и имеется даже закомментированный текст с пометкой Эта конструкция не будет работать ни на одном из компиляторов . В этом и заключается главная слабость variant; большинство реальных компиляторов и близко не подошли к тому уровню, чтобы скомпилировать этот код, который в результате представляет собой всего лишь экспериментальную разработку, пусть и чрезвычайно важную. Я пытался собрать код Александреску с помощью разных компиляторов: Borland 5.5; Comeau 4.3.0.1; EDG 3.0.1; gcc 2.95, 3.1.1, и 3.2; Intel 7.0; Metrowerks 8.2; Microsoft VC++ 6.0, 7.0 (2002), и 7.1 (2003). Ряд компиляторов в этом списке очень строго соответствует стандарту, но, тем не менее, ни один из них не смог успешно скомпилировать предложенный им код. Я попытался причесать исходный текст Александреску так, чтобы его можно было скомпилировать хотя бы одним из компиляторов, но добился успеха только с VC+- 7.1 (2003). Большинство компиляторов просто не способны на такой подвиг , поскольку они обладают недостаточно строгой поддержкой шаблонов, чтобы работать с кодом Александреску. (Интересно отметить, что некоторые из компиляторов оказываются ужасно многословными - так, Intel 7.0 в ответ на компиляцию mai п. срр разразился огчетом об ошибках, объем которого оказался равен почти полумегабайту - 430 Кбайт диагностических сообщений.) я внес три изменения в код, чтобы скомпилировать его без ошибок (хотя не смог избежать ряда предупреждений о сужающих преобразованиях) компилятором Microsoft VC++ 7.1 (2003): добавил отсутствующее слово typename в класс AlignedPOO; добавил отсутствующую конструкцию this->, чтобы в converterToo: :unit<>: :Dovisit() сделать имя зависимым; добавил символы новой строки в конце ряда заголовочных файлов, как того требует стандарт С++ (некоторые компиляторы недостаточно строги в этом отношении и допускают отсутствие таких символов в качестве расширения; VC++ строго относится к этому требованию). Вот как прокомментировал автор [Мап1еу02] компромиссы дизайна Александреску: Этот способ не использует динамическую память, а также избегает проблем, связанных с выравниванием и переключением типов. К сожалению, у меня нет компилятора, который бы мог скомпилировать этот код, так что я не могу сравнить его производительность с вариантами, использующими myunion и any. Подход Александреску требует наличия 9 заголовочных файлов общим объемом около 80 Кбайт, что влечет за собой множество проблем поддержки (К. Мэнли, частное письмо). Все это так. Я не намерен резюмировать здесь три статьи Андрея, но если вам они интересны - то они доступны в Web и указаны в списке литературы. > Рекомендация Если вам надо использовать вариантный тип, то лучше всего воспользоваться для этого классом boost; ;апу (или чем-то в той же степени простым). Когда используемый вами компилятор станет поддерживать шаблоны в достаточном объеме, а в стандарт включат поддержку выравнивания, наступит время рассмот- реть в качестве возможной замены объединений шаблон наподобие variant. Резюме Хотя дизайн и реализация myunion полны недостатков, сама проблема вполне реальна и стоит того, чтобы ею заняться. Я хотел бы поблагодарить К. Мэнли за то, что он нашел время и написал свою статью, привлекшую внимание к проблеме вариантных типов, а также Кевлину Хенни (Kevlin Henney) и Андрею Александреску за их вклад в решение данной проблемы. Это очень сложная проблема, и подходы Мэнли и Александреску оказываются не строго переносимы, хотя variant Александреску приближается к этому идеалу - он переносим настолько, что на практике его не способен скомпилировать ни один из распространенных компиляторов. В настоящий момент наиболее предпочтительным оказывается использование такого класса, как boost::any. Если в конкретных местах измерения указывают, что вам действительно требуется повышенная эффективность или дополнительные возможности, предоставляемые классом наподобие Variant Александреску, и у вас есть время и знания, то вы можете поэкспериментировать в написании собственных версий Variant путем применения только тех идей из [Alexandrescu02], которые вы сможете объяснить компилятору. 5 Я благодарен коллеге Джеффу Пейлу (Jeff Pell) за его замечание об этом требовании [С++03] § 2.1/1, которое гласит: Если непустой исходный файл не завершается символом новой строки или завершается символом новой строки, непосредственно перед которым идет символ обратной косой черты, поведение является неопределенным . Задача 37. Ослабленная монолитность. Часть 1: взгляд на std:: stri ng Сложность: 3 я решил завершить раздел, посвященный изучению конкретных примеров, минисерией, где рассматривается часть стандартной библиотеки, а именно - std:.string. Мы начнем наше рассмотрение с обзора важных правил, с учетом которых разрабатывался стандартный класс string. Вопрос для новичка 1. Что такое монолитный класс и чем он плох? Поясните свой ответ., Вопрос для профессионала 2. Перечислите поименно все функции-члены std: :basic string. Решение Избегайте чрезмерно монолитных конструкций 1. Что такое монолитный класс и чем он плох? Поясните свой ответ. Слово монолитный используется для описания программного обеспечения, которое представляет собой единое, неделимое большое целое наподобие монолита. Слово монолит происходит от слов моно (один) и лит (камень); оно вызывает в памяти образ огромного булыжника, который наглядно демонстрирует массивность и неделимость такого кода. Единая тяжеловесная программа, которая предназначена для всего , - зачастую просто тупик, в который зашли се создатели. В конечном счете часто оказывается, что чем сложнее приложение, тем уже область его применения. В частности, таким монолитом может оказаться класс, в функции-члены которого затолкали всю мыслимую функциональность, несмотря на то, что всю ее можно с тем же успехом реализовать в виде функций, не являющихся ни членами, ни друзьями этого класса. У такого подхода как минимум два недостатка. (Главный). Потенциально независимая функциональность оказывается локализованной в одном классе. Интересующая нас операция могла бы использоваться и с другими типами, но из-за жесткой закодированности в конкретном классе это оказывается невозможно (в то время как если бы эта операция была реализована как шаблон функции, не являющийся членом класса, она могла бы использоваться существенно более широко). (Второстепенный). Этот подход может препятствовать расширению класса с использованием новой функциональности. Минутку! - может возразить кто-то из читателей. - Не имеет значения, реализован ли существующий интерфейс класса при помощи функций-членов или не членов, так как я в любом случае могу расширить его при помощи моих собственных функций-не членов . Технически это так, но если вся функциональность класса достигается посредством функций-членов, т.е. реализована как естественная идиома класса, то расширение при помощи функций-не членов противоречит используемой идиоме и всегда остается решением второго сорта. То, что класс предоставляет свою функциональность в виде функций-членов класса, является семантическим указанием для его пользователей, которые привыкнут к такому стилю его использования, но который невозможно будет использовать при его расширениях.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |