Анимация
JavaScript
|
Главная Библионтека взаимоотношения с авторами компилятора и стандартной библиотеки. Не лезьте на их территорию, и вы останетесь невредимы. С++ достаточно открытый язык, который позволяет вам писать код любой степени сложности и гибкости, используя любые имена вне пространства имен std. Но все же, когда речь идет об именах, то у С++ в этом отношении есть запретная зона, опутанная колючей проволокой и обставленная таблицами с надписями "вход воспрещен, „предъявите „пропуск". Нарушителей - в зависимости от степени их невезения .......могут ждать крупные неприятности. > Рекомендация Никогда не используйте имена определенного вида, а именно - начина-юшиеся с подчеркивания и прописной буквы или содержащие двойное подчеркивание. Такие имена зарезервированы для использования компиляторами и реализациями стандартной библиотеки. Использование boost::any 4. Покажите лучший способ получения обобщенного вариантного типа, и расскажите, с какими сложностями вам пришлось столкнуться. В рассматриваемой статье сказано; Вы можете захотеть реализовать язык сценариев с единым типом переменных, которые могут быть целыми числами, строками ими списками. - [Мап1еу02] Все правильно и не вызывает никаких разногласий. Однако дальше сказано следующее: Объединение идеально подходит для реализации такого составного типа. - [Мап1еу02] Вместо этого статья служит демонстрацией того, почему объединения вообще не подходят для такой цели. И о если не объединения, то что же тогда следует использовать? Одним очень хорошим кандидатом для реализации такого вариантного типа является средство any из [Boost], вместе с many и anycast. Интересно, что полная реализация обобщенного any (работающего с любым количеством и любыми комбинациями типов, и даже с рядом платформозависимых #i fdef) имеет примерно такой же размер исходного текста, как и реализация myunion для рассмотренного частного случая трех типов i nt, list<int> и string, но при этом any представляет собой средство общего назначения, не зависящее от типов, расширяемое, безопасное с точки зрения типов, политкорректное и не содержащее холестерина. Естественно, и тут не обошлось без компромисса, который в данном случае представляет собой динамическое выделение памяти. Средство boost: :апу не пытается повысить эффективность за счет отказа от динамического распределения памяти, что было одним из исходных положений рассматриваемой статьи. Заметим также, что накладные расходы на динамическое распределение памяти в boost: :апу оказываются большими, чем в случае использования динамического выделения памяти для буфера в рассматриваемом нами фрагменте исходного текста. .Это связано с тем, что в случае MYUNION динамическое выделение памяти выполнялось бы однократно в течение жизни объекта, в то время как boost::any выполняет динамическое выделение памяти всякий раз при изменении типа своего содержимого. Вот как должна выглядеть демонстрационная часть исходного текста из статьи при использовании boost: :апу вместо myunion (оригинальный код статьи приведен в качестве комментария). Более детальное рассмотрение этого вопроса имеется в [HyslopO!]. 238 Изучение конкретных примеров any u; Вместо: myunion u; Вместо самодельной структуры, которая должна разрабатываться отдельно для каждого конкретного случая, здесь использован класс any. Заметим, что это обычный класс, а не шаблон. Обращение к объединению как к int U = 12345; вместо: u.getintC) = 12345; Это присваивание any имеет более естественный синтаксис. cout « "int=" « any cast<int>(u) « endl; или просто int(u) Вместо: cout « "int=" « u.getintC) « endl; Преобразование типа any мне представляется предпочтительным в силу его большей обобшенности (включая то, что не используется синтаксис доступа к члену класса) и большего соответствия естественному стилю С++; можно также воспользоваться более кратким int (и), без any cast, если тип вам известен. Кроме того, функции MYUNION get type менее надежны, сложны в написании, сопровождении и т.д. Обращение к объединению как к std::list u = list<int>(); list<int>& 1 = *any cast<list<int> >(&u); вместо: LIST& list = u.getlistC); 1.push back(5); Аналогично: list.push backC5); 1.push backC10); Аналогично: list.pushback(lO); 1.push backC15); Аналогично: 1ist.push backC15); Я думаю, что any cast можно усовершенствовать, с тем чтобы ссылку можно было получить более простым способом. (Кстати; мне не нравится использование 1i st в качестве имени переменной, когда в области видимости оказывается шаблон с тем же именем; это даст слишком много возможностей для неоднозначности.) Итак, мы достигли большей удобочитасмости и типизирусмости. Остальные отличия не так уж велики. list<int>::iterator it = 1.beginC); Вместо: list::iterator it = list.beginC); whileC it != 1.endC) ) { cout « "1i st item=" « *Cit) « endl; it++; } while Почти так же, как и в оригинале. Продолжим сравнение. Обращение к объединению как к std::string u = stringCHello world!") ; Вместо: STRING& str = u.getstringC); str = "Hello world!"; Опять версия с any немного проще исходной, но всего лишь немного. cout « "string=" « any cast<string>Cu) или просто "stringCu)" « " « endl; Вместо: cout«"string= "«str.c„strC)««endl; Размеченные объединения Александреску Нельзя ли достичь обеих поставленных целей ~ безопасности и отказа от динамического распределения памяти - при полном соответствии стандарту? Это похоже на проблемы, которые так любит Андрей Александреску (Andrei Alexandrescu), особенно если при этом можно написать шаблоны посложнее. Как видно из [Alexandrescu02J, где он описал свои подход к объединениям (variant), это возможно, причем с использованием шабло-нов (думали ли вы, rтo объединения могут быть шаблонами?), и это сделано. Коротко говоря, героические усилия Александреску по втискиванию своего шаблона 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 Кбайт диагностических сообщений.) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 [ 74 ] 75 76 77 78 79 80 81 82 83 84 85 |