суббота, 20 ноября 2010 г.
понедельник, 30 августа 2010 г.
Программные ловушки
Вы знали что программа "Hello World!", которая обычно бывает первой написанной человеком на С программой, выявляет самые опасные моменты языка? Программа содержит следующее выражение: printf ("Hello World!\n"); Рассмотрим объявление функции printf: int printf (const char * restrict format, ...); (restrict - новое ключевое слово C.) Данная функция принимает переменное число аргументов. Количество и тип переменных указываются в строке форматирования. Когда производится проверка соответствия между форматом и списком аргументов? Не во время компиляции, т.к. компилятор понятия не имеет о строке форматирования (хотя некоторые компиляторы способны выдать предупреждение если строка статически известна). Тогда во время компиляции? Если программист сделал ошибку при вызове printf с меньшим числом аргументов, то он получит замечательный код ошибки или исключение. Вот что говорится об этом в стандарте языка С: если не достаточно аргументов для форматирования, то поведение не определено. Неопределенное поведение - это худшее, что может случится с программой. Если вам повезло, программа выкинет ошибку и остановится. Если вам не совсем повезло, то программа продолжит работу в неопределенном состоянии, и в худшем случае, он выполнит поврежденный код, который возмет полный контроль над компьютером. Другая опасность в функции printf заключается в использовании указателя. Программист сам должен проследить за тем, чтобы указатель адресовал правильное место в памяти. В примере "Hello World!" указатель адресует статическую заканчивающуюся завершающим нулем строку, так что проблем не возникает. Но вот эта программа так же успешно скомпилируется: char * format = 0; printf (format); Угадайте, что случится. Внутри printf указатель разыменовывается и все рушится. Снова, цитируя стандарт С: если указателю было присвоено неверное значение, то поведение унарного оператора * не определено. Каждый раз при выделении памяти возвращается верный адрес (пока у программы не закончится память). Вы можете подумать, что разыменование такого указателя может быть безопасным. Это верно лишь до того, пока ваша программа не будет эту память освобождать при окончании времени жизни объектов. После этого у вас окажется указатель с неверным адресом и пиши пропало. И снова стандарт языка не обнадеживает. Из тех значений указателя, которые не следует разыменовывать оператором * он указывает на null указатель, неправильно выравненный с началом объекта адрес, и адрес объекта, после того как последний был удален. Ясно, что C был создан не для слабонервных. Он является низкоуровневым и программисту нужно знать, что и зачем он делает, иначе будут проблемы. Но C++ же не такой? С момента создания C++ старался избавиться наследие C. В него было добавлено множество конструкций, которые закрывали небезопасные возможности языка С. Например, программа "Hello World!" может быть трансформирована в более безопасный вариант. std:cout << "Hello World!" <<> Здесь уже не требуется считать количество передаваемых аргументов, и std::cout достаточно умен, чтобы понять типы передаваемых ей аргументов (хотя множество программистов продолжают использовать printf в C++, из-за более простого написания). В отличие он С, выделение памяти в C++ типизировано и производится при выполнении конструктора объекта (если вы конечно не используете malloc и free). Это хорошая сторона. Однако объекты по прежнему должны быть явно удалены. И после удаления все еще остаются указатели, использование которых, как вы уже можете догадаться, приведет к неопределенному поведению. Когда как в C указатели были очень важны, то в C++ они являются основным механизмом Стандартной библиотеки. Алгоритмы STL используют итераторы, объекты, которые являются указателями сами или имитируют их поведение (и все опасные моменты в том числе). Как и в случае указателей, неправильное использование итераторов приведет к неопределенному поведению (см. пример с swap_range). В то время как в C/C++ имеются проблемы с безопасностью, в языках наподобие Java и C# пошли другим путем. Они либо вообще запретили использование указателей, либо выделили их в специальные блоки с меткой "небезопасный". Управление памятью, которое порождает риск обращения по неверным указателям, скрыт от программиста и производится автоматическая сборка мусора. Кроме этого в них есть еще множество упрощения и средств, повышающих безопасность. К несчатью, все они либо снижают мощность языка или его производительность.
Подмножество безопасного D (SafeD Subset)
В языке D, основная работа выполняется в безопасной области языка, которую мы называем SafeD. Безопасность и простота использования языка D в этом случае сравнима с Java. На самом деле программы на Java можно автоматически транслировать в безопасную часть D. SafeD легок в освоении и избавляет программиста от неопределенного поведения. И он очень эффективен в плане производительности. В SafeD вы не используете указатели, непроверенное приведение типа и union'ы. Управление памятью ложится на сборщик мусора. Объекты передаются с помощью идентификаторов. Выполняется проверка выхода за границы массивов и строк (можно отключить эту возможность с помощью ключа компиляции, но тогда вы уже не в SafeD). Вы можете писать код, который будет выкидывать исключения времени выполнения (т.е. ошибка выхода за границы массива, неинициализированный объект), но вы не сможете перезаписать память, которую выделили или уже освободили. Посмотрим на программу "Hello World!" на языке D. На первый взгляд, различий с C не много: writeln ("Hello Safe World!"); Функция writeln является эквивалентом printf в С (более точно, это представитель семейства функций для вывода, которое включает write и ее версии для форматированного вывода - writef и writefln). Так же как и printf, writeln принимает переменное число аргументов любого типа. Однако на этом сходство заканчивается. Пока вы будете передавать ей SafeD-аргументы, вы можете быть уверены, что не возникнет никакого неопределенного поведения. Здесь writeln вызван одним аргументом типа string. В отличие от C, D строки не являются указателями. Это массив char, и массивы находятся в безопасной части D. Вам может быть интересно, как осуществлена безопасность функции writeln. Один из возможных способов, это сделать функцию обрабатываемую компилятором, чтобы генерировался правильный код. Красота D в том, что он дает современные инструменты для подобных случаев. Из таких инструментов, которые использовались при написании этой функции нужно отметить: Генерирование кода во время компиляции с помощью шаблонов, и Безопасный механизм работы с переменным числом аргументов с помощью кортежей.
SafeD Библиотеки
Основное отличие между Java и D в том, что в D есть достаточно возможностей, чтобы создать бибиотеки для использования в SafeD. Большое количество возможностей D совместимы с SafeD. Например, в библиотеке может быть реализован типизированный список (generic list). Этот список может быть создан для любого типа, в том числе указателя. Список указателей не может быть безопасным по определению. Причем список целых чисел или объектов может и должен быть безопасным, поэтому в SafeD можно использовать списки таким образом, а использование вне безопасного Ди может быть небезопасным. Более того, может оказаться в плане эффективности лучше реализовать список через указатели. И если такая внутренняя реализация хорошо скрыта от пользователя, такая реализация может быть названа совместимой с безопасным Ди.
Опыт одного пользователя
Даже до того, как у меня в голове возникла идея о SafeD2, в своих проектах я пытался ограничивать себя безопасным в использовании подмножеством Ди. Я был удивлен, насколько многого можно при этом сделать и насколько возрастает продуктивность. Я также показал Ди знакомому по работе, программисту С++, и он смог освоить его за очень короткое время. Так по моему опыту можно было сказать, что если написанная на SafeD программа компилируется без ошибок, она будет и работать без ошибок и делать то, чего я от нее хочу. Это определенно не то, с чем я сталкивался при использовании C++. Что еще больше меня удивляет, как я умудрился все это сделать, не имея в наличии подходящего инструментария и получая непонятные сообщения от компилятора. Ди все еще не хватает инфраструктуры, но я могу представить, насколько легко будет на нем программировать, когда появится критическая масса инструментария для разработчика. В отличие от C++, D можно легко парсить и у него открытый фронт енд, что упрощает работу людям, пишущим инструментарий.
Заметки
Сейчас нет какого-либо органа, кто-бы занимался подобной сертификацией, каждый кто создает библиотеки устанавливает свой уровень доверия с клиентами. В частности, вам следует ожидать, что разработчик компилятора обеспечивает совместимость стандартной библиотеки с SafeD. Название SafeD было предложено David B. Held.
Благодарности
Огромное спасибо членам команды разработчиков языка D за ценный отклик и исправления в данной статье.
Оригинал: http://d-programming-language.org/safed.html
Это черновой перевод статьи. Буду еще дорабатывать. Если есть замечания и комментарии, прошу в комменты.
суббота, 29 мая 2010 г.
Начало работы
Установка
Clojure можно найти на Google Code. Репозитарий исходников находится на GitHub. Там же находится порт на CLR. Совместная разработка ведется на Assembla.
Замечания и обсуждение идет на Clojure Google Group. Пожалуйста присоединяйтесь!
Документацию и советы от сообщества пользователей можно почитать на Wiki. Вы можете свободно принимать участие в написании.
Clojure распространяется как zip файл с clojure.jar, readme, текстом лицензии EPL и исходниками в папке src. Язык использует библиотеку байткода ASM 3.0 и она включена в дистрибутив. Для работы требуется Java 1.5 или выше.
Быстрое начало
В папке, в которую вы разархивировали clojure.zip, наберите команду:
java -cp clojure.jar clojure.main
После чего появится read-eval-print (REPL). Большая часть Clojure написана на ней самой (в файле core.clj, который находится в папке src), который автоматически загружается, когда стартует Clojure из .jar архива.
Когда core.clj будет загружен, то Clojure будет в вашем полном распоряжении.
Попробуйте напечатать:
user=> (+ 1 2 3)
6
user=> (. javax.swing.JOptionPane (showMessageDialog nil "Hello World"))
В REPL можно осуществлять только базовое редактирование. Для большего удобства попробуйте запустится через JLine ConsoleRunner:
java -cp jline-0.9.91.jar:clojure.jar jline.ConsoleRunner clojure.main
После чего появится возможность навигации по клавишам левый/правый и листать историю ввода по клавишам вверх/вниз.
Редактирование
Редактировать код на Clojure удобнее всего в каком-нибудь редакторе с поддержкой соответствия скобок. Свежую информацию о таком инструментарии (Netbeans, Eclipse, IntelliJ Idea, Emacs, Vim и другие) можно найти на странице Getting Started в Clojure Assembla. Так же Sean Devlin сделал серию скринкастов, о том как можно настроить редактирование кода на Clojure. Всем спасибо!
Отладка
Я успешно отлаживал код на Clojure с помощью JSwat версии 3.16 на Mac OS X. Вы можете запустить Clojure под JSwat, но возможности редактирования будут ограничены. Лучше присоединить JSwat к работающему Clojure. Вы должны запустить Java с этими флагами, чтобы проводить удаленную отладку:
java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n ...
Когда Clojure запустится, он напечатает что-то типа этого:
Listening for transport dt_socket at address: 63657
Clojure
user=>
Потом в JSwat выберите Attach... и введите localhost в поле Host и адрес сокета в Port. В JSwat откройте файл Clojure, который вы загрузили (включая boot.clj) и можете ставить брейкпойнты в коды на Clojure. Брейкпойнты, стек вызова, локальные переменные и пошаговая отладка (в Сlojure коде, и вызовах с Clojure на Java и обратно) будут работать. Вы также можете подключить запущенный Clojure как inferior Lisp в Emacs. Если вы поменяли .clj, то лучше всего перезагрузить его в, JSwat обнаружить изменения в файле и вы сможете ставить брейкпойнты в коде даже без его перезапуска.
Профайлинг
Профилирование кода на Clojure можно осуществлять с любым профайлером для Java. Я успешно использовал YourKit Java Profiler, лицензии для которого были щедро предоставлены YourKit.
Так YourKit поддерживает open source проекты, любезно обеспечивая их профайлером. YourKit, LLC является создателем инновационных и продвинутых инструментов для профилирования кода в приложениях для Java и .NET. Вы можете найти продукты этой компании на:
YourKit Java Profiler и
YourKit .NET Profiler
Copyright 2008-2010 Rich Hickey
понедельник, 10 мая 2010 г.
Clojure (перевод с clojure.org)
Clojure являетя диалектом Lisp, и использует лисповскую философию единства кода и данных и мощную систему макросов. Clojure является функциональным языком программирования и везде, где это только возможно, старается использовать неизменяемые структуры данных. Когда же вам необходимы изменяемые данные Clojure предлагает software transactional memory и систему Agent, которые позволяют построить понятный, правильный и многопоточный дизайн.
Я надеюсь вы найдете предоставляемые Clojure возможности элегантными, мощными, практичными и вам понравиться их использовать.
Основное обсуждение Clojure идет в Google Group - присоединяйтесь!
Rich Hickey