Использование ярких контрастов — излюбленный прием литераторов. В нашем случае, к сожалению, не до такой утонченности, и все же… От экзотичного, мощного и по-своему уникального языка Rebol мы перейдем к ознакомлению с разработкой не менее полезной… и несоизмеримо менее популярной. Если сравнительно "молодой" Rebol уже сформировал внушительное количество тематических Internet-ресурсов, ему посвящают статьи ведущие издания для разработчиков и к нему серьезно "присматриваются" Apple и IBM, то предмет нашего сегодняшнего обсуждения, хорошо известный в научном сообществе, популярным назвать трудно. Предваряя необходимые отступления, стоит предупредить читателя сразу — в этой статье не будет "потешных" примеров и даже попытки "облегчить участь" начинающим "с нуля". В данном случае такой подход просто непозволителен, и далее будет понятно, почему.
Если прибегнуть к помощи какого-нибудь хорошего сетевого ресурса и попытаться
даже не проанализировать (анализ — слишком серьезная задача), а просто оценить
все разнообразие интерпретируемых языков программирования, в той или иной степени
соответствующих неформальному сленговому определению "скриптовых",
можно сделать ряд очевидных "открытий". Например, взглянув на страницу
портала
dmoz, посвященную интерпретируемым языкам, можно вполне уверенно говорить
о том, что в мире скриптинга правят бал весьма специфические разработки, максимально
удаленные от "традиционных", ориентированных на быстрое исполнение
компилируемых языков. И этот факт, имеющий столь очевидное объяснение, что мы
его даже не упомянем, тем более подчеркивает специфичность героя сегодняшнего
обсуждения. Все дело здесь заключается в весьма неприятном парадоксе, которому
стоит уделить минуту внимания.
Традиционно и небезосновательно принято считать, что основные инструментально-языковые
средства современного массового программирования (естественно, это знаменитые
языки C и C++) относятся к классу потенциально опасных и обладающих массой "врожденных"
недостатков. Между тем большинство претендующих на "совершенство"
средств аналогичного назначения, опять же, создаются именно с помощью такого
опасного инструментария. В частности, среди обширного класса интерпретаторов
можно найти один-два образчика, нарушающих это правило, но основная масса ему
полностью соответствует, сколь бы ни был велик разрыв между уровнем двух инструментов
— новосозданного и использованного в процессе создания.
![]() |
Минимальный инструментальный набор, необходимый для эффективного использования CINT, вне зависимости от платформы более чем скромен — текстовый редактор и консоль |
Самое время читателю спросить: "А где же парадокс?". Он действительно
неочевиден и заключается в следующем: с одной стороны, создатели "сверхуровневых"
скриптовых языков реализацией высокоуровневых функций заботятся о благе пользователей
ПО, с другой — остается без ответа вопрос "А кто позаботится о благе самих
создателей?". То есть для прикладного и системного программиста каждый
"еще один скриптовый язык" — еще одна головная боль, с которой нужно
мириться для того, чтобы создать настраиваемую, гибкую и "подгоняемую"
под требования конкретного пользователя программу, но ведь и сам программист
является пользователем… В подобной ситуации действительно необходим хороший
скриптовый язык, одновременно ориентированный на облегчение решения типовых
задач, стоящих перед программистом (например, прототипирования и быстрой отладки),
и максимально приближенный к самым распространенным языковым технологическим
средствам. Последнее требование немаловажно — использование даже очень хорошего
"еще одного языка" создает в процессе разработки дополнительную "технологическую
ступень", что всегда связано и с дополнительными трудностями.
Теперь мы готовы сформулировать минимальный перечень требований к скриптовому
языку "для программистов": он должен быть максимально приближенным
к используемым в традиционном процессе разработки массово-популярным языковым
средствам и интерпретируемым (что существенно облегчает решение задач прототипирования
и отладки). Наконец, он должен быть доступным, мобильным и стабильно развивающимся.
Это, скажем так, программа-минимум. А уж если такой инструмент будет обладать
врожденными способностями к "встраиванию" в приложения и расширяемости,
можно говорить и о выполнении программы-максимум, которую ставят перед собой
далеко не все создатели языковых средств.
Собственно, на этом отступления завершаются, и наступает время непосредственного
знакомства с по-своему уникальным скриптовым языком, который, по крайней мере,
не добавляет головной боли…
Аспект второй — прототипирование
Looks pretty inconsistent,but this is C++.
Документация CINT
Японские программисты давно доказали свое умение самостоятельно ставить
и решать весьма нетривиальные задачи. С одним их детищем, к слову, тоже скриптовым
языком, мы ранее знакомились (речь идет о Ruby). Но если Ruby (автор Йокихиро
Мацумото) является классическим образчиком "еще одного языка" (отличающегося
от своих предшественников как синтаксически, так и в части семантики привычных
синтаксических конструкций), то сегодня мы будем говорить о куда более заурядной
на первый взгляд программе с незамысловатым названием CINT (разработчик Масахару
Гото — Masaharu Goto). Аббревиатура CINT означает "C INTerpretator",
т. е. "интерпретатор C". И, опять же, предваряя возможное недовольство
читателя ("подумаешь, интерпретатор C…"), сразу стоит пояснить и
смысл ранее использованного эпитета "уникальный", и оправданность
его использования. Так что не спешите возмущаться…
Действительно, существует не одна реализация интерпретаторов языка C, и в этом
смысле CINT не единственный и не уникальный (хотя это на самом деле не совсем
так). Но… в оценке любой реализации, в отличие от оценки "чистой идеи",
есть один существенный нюанс — Как Это Реализовано. И здесь для начала стоит
привести только один неоднократно проверенный факт — CINT, написанный на языках
программирования С (и частично С++), способен интерпретировать… самого себя.
То есть, имея исполняемую программу CINT для выбранной вами платформы, вы можете
интерпретировать ею исходные тексты CINT, из которых с помощью того или иного
компилятора была получена сама программа CINT. И созданный таким образом "CINT
в CINT" будет совершенно работоспособным. Это уникальное свойство открывает
два главных качества CINT — очень высокие степень близости к двум основным
языкам программирования (С и С++) и качество разработки. Кроме того, CINT легально
бесплатно доступен с открытыми исходными текстами, работает "на всем, что
движется" (в перечне поддерживаемых платформ присутствуют фактически все
актуальные Unix-совместимые ОС, Windows, DOS, BeOS и Mac OS/Mac OS X и даже
такая по нынешним временам экзотика, как VAX VMS), позволяет прототипировать
С/C++ программы "mainstream-платформ" — с Win32 API и Posix API —
и, наконец, развивается более 12 лет…
Именно такие особенности CINT и сформировали упомянутый ранее более обзорный,
чем ознакомительный, характер статьи — популярность и сложность языков С и
С++ совершенно исключают необходимость в каких-либо "потешных" примерах.
Вместо этого мы сконцентрируемся на рассмотрении потенциальных возможностей
CINT и девиаций интерпретируемых этой программой языков от стандартных ANSI
C/C++.
Естественно, знакомство с CINT надо начинать с установки интерпретатора на свой
компьютер. Получить исходные тексты программы и ее бинарные версии для некоторых
платформ можно по адресу root.cern.ch/root/Cint.html
или с одного из многочисленных "зеркал" проекта ROOT (некогда разработанный
для компании Agilent, CINT "перекочевал" в состав масштабной объектно-ориентированной
системы поддержки научного анализа данных ROOT, при этом не утратив значимости
как самостоятельная разработка). Процедура установки системы для пользователей
ОС Unix совершенно традиционна и потому не заслуживает внимания, пользователи
ОС Windows могут собрать CINT с помощью имеющихся в поставке bat-файлов и компилятора
С/С++ (примитивная процедура описана в файле README.txt) или поступить проще
— установить распространяемую бинарную версию, с одним маленьким "но":
исполняемая версия CINT для ОС Windows помещается "поверх" исходных
текстов, т. е. загружать надо оба дистрибутива! Последний этап процедуры инсталляции
— установка переменной окружения CINTSYSDIR, содержащей путь каталога, в котором
развернута CINT (опять же маленькое замечание для ОС Windows — учитывая кросс-платформенный
характер CINT, целесообразно избегать в пути к инсталляции имен каталогов, содержащих
пробелы или не-ANSI символы; оптимально разместить CINT просто в каталоге с:cint).
В частности, автором статьи CINT была неоднократно успешно и без проблем инсталлирована
на платформы FreeBSD 4.4—4.7 и Windows 2000 с помощью компиляторов gcc 2.95—2.95.4
и Borland C++ 5.5.
Для удобства использования CINT в не Unix-совместимых системах понадобится хорошая
реализация текстовой консоли. В частности, в ОС Windows можно для этих целей
применить удачную легально бесплатную разработку Econsole.
Собственно, на этом перечень основных технологических нюансов считается исчерпанным
(а с "неосновными" всегда можно ознакомиться в документации и архивах
переписки пользователей CINT по адресу root.cern.
ch/root/cinttalk/AboutCint.html).
Теперь поговорим о "языковых нюансах". Несмотря на то что сам автор
CINT в документации неоднократно отмечает, что цель достижения 100%-ной совместимости
со стандартами ANSI/ISO C/C++ им не ставилась, все же интерпретатор "понимает"
"most of K&R and ANSI C/C++ language constructs" (большинство
конструкций языков С/С++ в версиях Кернигана—Ричи, а также соответствующих
стандартам ANSI/ISO). Не стоит смущаться неопределенностью термина "большинство
конструкций" — в части интерпретации ANSI/ISO С CINT "понимает"
почти все, по оценкам авторов документации и многочисленных пользователей системы
ROOT — примерно 90—95% от языка С, предписанного стандартами. Оставшиеся 5—10%
несовместимости на самом деле являются не столько недостатком, сколько исключительно
интересным фактором при учете нашего "нестандартного" подхода к рассмотрению
CINT как самостоятельной конструкции. Использование CINT для прототипирования
именно с учетом этих несовместимостей на деле оказывается даже… полезным для
будущего компилируемого кода. Хотя бы потому, что несовместимости CINT лучше
всего охарактеризовать простым правилом их избежания: "не пользуйтесь в
CINT-прототипах слишком заумными конструкциями С, избегайте применения в качестве
ключевых элементов вашей программы тех языковых конструкций, которые стандартом
или Керниганом—Ричи отмечены как угрожающие мобильности, придерживайтесь хорошего
стиля программирования". Уточняя это правило, стоит выделить самые важные
девиации CINT от ANSI/ISO C. Все они, можно сказать, малосущественны — от синтаксических
до семантических. Синтаксические, например, требуют обязательного разделения
хотя бы одним пробелом символов "*" и имен типа и переменной при объявлении
указателей, т. е. объявления вида "int****iPtr;" считаются
недопустимыми. Это требование CINT, по идее, нарушается в реальных программах
С/C++ разве что участниками "С
obfuscated code contest" — создателями умышленно нечитаемого кода.
Также к синтаксическим относятся требования обязательно использовать метку "default:"
в case-конструкциях только последней в перечне возможных ветвлений.
К счастью, хороший стиль программирования на С/С++ требует именно такой формы
записи оператора ветвления, и "упрятывание" метки default: в глубинах
конструкции switch никогда никем не приветствовалось.
К смешанным синтаксически-семантическим ограничениям CINT относится отсутствие
оператора ",", реализующего последовательность вычислимых операторов
С со значением, возвращаемым последним из них. Опять же к счастью, оператор
последовательности в С/С++ — конструкция во многом крайне неприятная, о которой
принято говорить так: "Если есть хоть одна возможность к ней не прибегать,
то эту возможность надо использовать, а если возможности нет, то ее надо создать".
Семантические "отклонения от нормы" CINT тоже не сильно шокируют.
Одна из главных девиаций связана с областями видимости локальных переменных.
В ANSI/ISO С/С++ последние имеют область видимости "ограничивающий блок",
что позволяет объявлять локальные переменные в началах блоков, например, операторов,
изменяющих поток выполнения программы (if, for, while, do while), а после завершения
блока повторно использовать эти же имена. В CINT область видимости локальной
переменной — "ограничивающая функция", что означает фактически следующее:
вы можете объявлять переменные в соответствии с правилами ANSI/ISO C/C++, но
их повторное объявление в пределах одной функции невозможно. Считать такое ограничение
слишком суровым трудно, и при прототипировании программ лучше всего придерживаться
традиционного правила структурного программирования: "Все переменные должны
быть объявлены в начале функции". По крайней мере, следование ему вносит
дополнительный порядок в исходные тексты, дисциплинирует и прекрасно "уживается"
с требованиями последних версий стандартов. То же самое можно сказать и об ограничении
на использование оператора переименования типа typedef — его в CINT можно применять
только на уровне модуля, т. е. вне тела любой функции. Впрочем, если "перекопать"
реальные тексты C/C++ программ, переименования типов, локализованные в функциях,
окажутся редкостью. Здравый смысл подсказывает, что в C подобный "фокус"
не нужен просто потому, что именно модули, а не функции этого языка, используются
для локализации операций над одним типом данных, а уж в С++ typedef вообще является
рудиментарным наследием C. Более тонкие нюансы несовместимости CINT с ANSI/ISO
С в большинстве случаев на нашем ознакомительном уровне можно вообще не принимать
во внимание (если придерживаться вышеуказанного правила, эти нюансы никак не
скажутся даже в самом активном процессе использования CINT). Что же касается
вопросов совместимости CINT-кода с C++, следует отметить очень хорошую поддержку
интерпретатором механизма шаблонов (естественно, здесь есть свои ограничения,
но обеспечиваемый уровень совместимости можно считать более чем достаточным
для решения задач прототипирования даже весьма сложных и нетривиальных программ),
почти полное соответствие правил перегрузки функций требованиям стандарта, реализацию
механизмов обработки исключительных ситуаций (exception handling) и пространств
имен (namespace). Девиации CINT-подмножества стандартного языка С++ также обладают
двойственным характером: с одной стороны, они ограничивают некоторые возможности
программиста, с другой — вынуждают отказываться от ряда "грязных трюков
C++". Это утверждение касается, например, "нелюбви" CINT к указателям
на функции, которые применяются в качестве аргументов для вызовов других функций,
или к отсутствию реализации указателей на данные — члены класса.
"Технологическая оснастка" интерпретатора CINT вполне самодостаточна
— он располагает встроенными командным отладчиком (напоминающим упрощенный
до разумного уровня сложности gdb для Unix-совместимых систем) и краткой подсистемой
помощи. Главные же особенности "оснастки" заключаются в пригодности
CINT к встраиванию в приложения (например, в виде динамически разделяемой библиотеки)
и к расширяемости самого интерпретатора сторонним исполняемым кодом, разработанным
в соответствии с требованиями API CINT. Именно благодаря последнему свойству,
CINT, создатель которого умышленно ограничил базовый набор встроенных библиотечных
функций С соответствующим стандартам ANSI перечнем, поддерживает вызовы POSIX
и Win32 API. К слову, "подстегивание" дополнительных интерфейсных
библиотек, обеспечивающих расширенное использование возможностей целевой платформы,
где работает CINT, — задача относительно несложная для подготовленного программиста
C/C++. Последнее, о чем хотелось бы упомянуть, — пригодность CINT к интерпретации
далеко немаленьких программ. Это, как и большинство свойств CINT, подтверждено
многолетней эксплуатацией в составе пакета ROOT — программа справляется со
"скриптами" из десятков и даже сотен тысяч строк кода.
Итак, настало время подводить итоги. Но это уже задача, требующая от читателя
самостоятельного решения. CINT — действительно очень красивая и полезная разработка,
для которой легко найти много применений — от образовательной сферы до узкопрофессиональных
приложений. Начинающим изучать языки C и C++ CINT поможет сравнительно быстро
почувствовать их "на вкус", не загромождая бесспорно присущее этим
инструментам изящество массой громоздких деталей, свойственных мощным системам
программирования. Профессиональные программисты найдут применение CINT в качестве
инструмента для разработки в стиле "что будет, если?" — ведь до сих
пор никто не отменил потребности в сложной нетривиальной алгоритмике, для которой
полный технологический цикл "программирование — трансляция — отладка
— изменение программы — …" оказывается не то чтобы неподходящим, но
утомительным. И, наконец, CINT — хороший претендент на роль встраиваемой компоненты,
но разработчикам надо обязательно учитывать специфику ее лицензии.
Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: