вторник, 30 сентября 2008 г.

Революции нужны?

Я сторонник эволюционного подхода. То есть я верю что любая программа может планомерно мутировать куда нужно. При условии, что совместимость со всем прошлым не стоит как задача. То есть я не отрицаю частичную совместимость. Например KDE 4.0, 4.1, 4.2 должны быть совместимы между собой. Но нету никакой необходимости поддеживать запуск приложений 10-ти летней давности. Это нужно только гнусным проприетарщикам. :)

В то же время совместимость - понятие разноплановое. Совместимость бывает бинарная, или сборочная, или скриптовая.

Бесспорно то, что программы должны развиваться. То есть, новый функционал неизбежно появляется, если программа конечно не умерла :). С появлением нового функционала старый функционал должен отмирать, и исключаться.

Это в идеале. В реальности, особенно в мире свободного ПО, активная разработка зачастую скатываеся к наращиванию функционала. А поскольку все это делается еще в свободное время - на чистку времени совершенно не остается, и программа начинает прогибаться под грузом функциональности. А поскольку программа свободна - в какой-то момент это кому-то надоедает и появляется форк.

Но иногда появляются не просто форки а качественные переосмысления идей первого продукта. То есть новый продукт выполняя сходные функции работает совершенно иначе. Он конечно может использовать некоторые наработки предшественников, но тем не менее ни о какой совместимости в данном случае не может быть и речи. Революция?

К чему я это все. Последнее время мой любимый gentoo явно находится в застое. Вероятно это происходит потому, что количество проектов велико, и ни на какие творческие поиски просто не остается времени, рутинное добавление новых пакетов - все что осталось. В то время как новое ПО требует нового отношения. И, как видно не всегда старых средств хватает.

Сейчас в gentoo явная проблема например с KDE. Часть команды KDE по политическим разноглаcиям покинула проект, и стабильные пользователи gentoo еще не скоро увидят KDE4 (Кстати не понимаю, почему она не в слоте).

Такая ситуация, видимо, давно уже кому-то надоела и возник проект Exerbo, где переделана сама структура портеджей. А кроме того новые сценарии инициализаци системы и некоторые другие вкусности. Но одна беда - система еще не достаточно наполнена пакетами. Но это быстро проходит. буквально два месяца назад там еще не было даже иксов, но сейчас там уже есть kde4. Пожалуй как нибудь на досуге я посмотрю на не поближе (в плане поставить), Потому что это самый вероятный кандидат на замену моей любимой gentoo.

Наверное, мысль поста как-то ускользнула. Просто в любом проекте в какой-томомент назревает необходимость выбросить все и начать сначала. Или хотя бы наметить пути дальнейшего наступления.
Собственно революция - это быстрая эволюция, зачастую можно все то же проделать и в более спокойном режиме, просто надо остановить наращивание, осознать направление движения, и смелее отрезать все лишнее.

PS: А что творится в недрах Windows - я даже представить боюсь, хотя они тоже иногда забивают на совместимость, но это пока исключение. Совместимость - их хлеб. А Windows еще не умер от груза совместимости? есть ли жизнь после XP? о чем это я...

пятница, 26 сентября 2008 г.

deprecated: gcc vs vs vs vs...

vs Visual Studio... Опять наверное прописные истины, но хочу рассказать про аттрибут deprecated.

Аттрубут deprecated - это не только ценный инструмент для командной разработки (чтобы довести до других членов команды необходимость изменения API), но так же он незаменим при рефакторинге.

Я очень часто применяю его, если мне хочется что-то удалить, но предварительно нелишним будет узнать где это что-то используется. Тогда я это что-то объявляю как deprecated, и после компиляции точно знаю файлы и строки где это используется. Вовсе не обязательно удалять сразу, есть время подумать, подчистить и когда варнинги исчезнут - удалить окончательно.

Все, про рефакторинг больше ничего не скажу.

Хочется сравнить возможности атрибутов на gcc и visual с++. Поскольку атрибуты не входят в стандарт, то в каждом компиляторе это делается как попало.

Начнем с gcc. Для объявления чего-то устаревшим в нем используется конструкция __attribute__((deprtecated)). Её можно применять для переменных и типов:
int a __attribute__((deprtecated));
struct a_struct {} __attribute__((deprtecated));
struct { int a_field __attribute__((deprtecated)); };
void func(int a_arg __attribute__((deprtecated))) {}

Практическая польза от последнего - спорна, но может пригодиться для очень больших функций. Код же разный бывает, в конце концов.

Для функций:
void func() __attribute__((deprtecated)); // в прототипе
void __attribute__((deprtecated)) func() {} // в декларации

Причем если поставить атрибут не на место, то gcc будет ругаться грязно и непонятно насчет точек с запятыми, еще какой-то фигни.

В visual c++ дело обстоит так: указание аттрибута осуществляется с помощью __declspec(deprecated) и используется практически единообразно.


__declspec(deprecated) int a;
struct __declspec(deprecated) a_struct {};
// почему после struct то?
struct { __declspec(deprecated) int a_field; };
void func(__declspec(deprecated) int a_arg) {}
// игнорируется
__declspec(deprecated) void func();

В принципе схоже, свои заморочки, но их все перекрывает возможность указать сообщение.
__declspec(deprecated("use foo instead")) void func();
В gcc такая возможность только обсуждаются.

Правда как всегда в Microsoft - без ложки дегтя не обходится. Указал сперва сообщение по русски, но в Output увидел только квадратики... только два квадратика разного размера. Хотя в сообщении было явно больше двух букв...

среда, 24 сентября 2008 г.

Разбор командной строки в boost

Долго ломал голову как сделать обработку командной строки в boost по хитрому. А по хитрому - это так: сперва указываются глобальные опции, потом некоторая команда, потом локальные опции, свойственные конкретной команде. Но program_option из boost, упрямо засасывает все опции сразу, и чтобы придумать способ - пришлось покопаться в исходниках.

И как всегда, после того как разобрался, решение оказывается простым и понятным, но где оно было раньше - не понятно. Всмысле это могло бы быть написано в докумментации.

Начнем с устройства program_options::command_line_parser. Устроен он следующим образом:

Имеется некоторый набор парсеров стиля (style_parser), которые по некоторым критериям выбирают из строки опции. набор стайл парсеров формируется на основе стиля командной строки, который может варьироваться в некоторых пределах - поддержка длинных имен, значения у опций, опции формата DOS и тд.

Каждый парсер возвращает список опций (vector<program_options::option>), обработанных им на данном этапе. Это может быть пустой список, одна или много опций, хотя парсить много опций за раз нет необходимости, там цикл и остатки командной строки обязательно снова пройдут через каждый парсер. Обработка заканчивается после того, как командная строка опустевает.

После прохождения командной строки через набор парсеров формируется список опций. неопциональные элементы в котором представлены некоей нетипизированной опцией.

Это было введение. :)

Теперь предположим, что командная строка предположительно будет разбираться за два прохода. Первый проход выделяет глобальные опции (идущие перед командой) и команду соответственно. А второй проход получит список аргументов, объединяющий в себе локальные опции и все остальное.

Но как и в любом деле здесь главное вовремя остановиться, то есть хотелось бы чтобы boost не анализировал строку до конца в поисках опций. И тут нам на помощь приходит extra_style_parser. Дополниельный парсер стиля - это отдельная функция. Её задачей будет при обнаружении первого неопционального слова, предположительно команды, оставшуюся часть строки без разбора загнать в результат. Cлайды:

vector<program_options::option> stop_on_command (vector<string> &args)
{
vector<program_options::option> result;
const string &tok = args[0];
if (tok[0] != '-') {
BOOST_FOREACH (string &arg, args) {
program_options::option opt;
opt.value.push_back(arg);
result.push_back(opt);
}
args.clear();
}
return result;
}

Используем это при разборе строки:

program_options::command_line_parser parser (argc, argv);
parser.extra_style_parser (stop_on_command);

...

program_options::store(parser.run(), vm);

Естественно чтобы это дело сработало необходимо создать позиционный параметр неограниченного количества, куда будут сложены все локальные опции/аргументы.

Вот собственно и все. Может пригодиться кому.

понедельник, 15 сентября 2008 г.

Компиляцию варнингом не испорть...

Но не все варнинги одинаково полезны. Включение варнингов предполагает, что при компиляции ты вообще не будешь их видеть. То есть, на предупреждения надо реагировать и исправлять их. Но если предупреждения вываливаются из внешнего кода (из инклюдов), то вывод засоряется, свои предупреждения теряются и и приходится мучаться. Лишние предупреждения надо убирать, но как?

Я очень люблю предупреждения компилятора. :) В своих проектах я всегда выставляю -Wall -Wextra -Weffc++. Последний тоже весьма полезен, поскольку позволяет писать на C++ в соответствии с рекоммендациями Скота Майерса, что позволяет избежать лишних побочных эффектов.

Но вот некоторые, не будем показывать пальцем, хотя это boost, не используют эту опцию. В результате чего вы выходе имею горы предумреждений и никакой возможности их устранить. Можно конечно патчить boost, или даже слать патчи разработчикам, но мне некогда этим заниматься. Но и отключать любимые варнинги тоже не хочется. Должен быть путь управлять этим на уровне кода.

Вот в MSVC испокон веков существует #pragma warning, которая легко и непринужденно позволяет делать все это. Но в gcc все обстоит не столь радужно.

Вероятно, в gcc-2.96 появилась #pragma GCC system_header. Которая должна отключить генерацию последующего кода, при условии, что используется она в теле файла инклюда. Но мне почему-то не удалось заставить ее работать. Может быть ей требуется исключительно #include <>, В то время как локальные инклюды #include "" не прокатывают? не знаю.

Вычитал, что есть #pragma GCC diagnostic, правда появилась эта прагма только в gcc-4.2. Она позволяет выбирать реакцию на предупреждения от игнорирования до ошибки.

#pragma GCC diagnostic ignored "-Weffc++"
#include <boost/...>
#pragma GCC diagnostic warning "-Weffc++"

Это дело работает, но основной gcc в моей Gentoo на данный момент 4.1.2, Хотелось бы заставить компилиться без ошибок и на нем. Как же юзается этот system_header???

воскресенье, 7 сентября 2008 г.

О макросах бедных замолвите слово...

Макросы конечно опасны. Так же, как опасны и многие другие, весьма повседневные вещи. И всем понятно, что нет никакой необходимости применять макросы препроцессора в C++. Но здесь речь пойдет не о C++, хотя во второй части, посвященной рефакторингу, я немного коснусь и его. Совсем чуть-чуть. Мог бы даже и не говорить об этом во вступлении.

Но если говорить о C, то в некоторых случаях без макросов просто никуда...

Начнем с тех моментов, когда нет никакой необходимости применять макросы. Это происходит тогда, когда типы параметров и результата заранее известны. То есть ситуация предельно конкретная. Нет необходомости использовать макрос. Вполне можно описать функцию. Даже в инклюде, если она используется из множества файлов, описав ее как static inline. За счет контроля типов этот способ гораздо предпочтительнее в плане надежности.

Проблема возникает тогда, когда типы заранее неизвестны. Обычно это указывает на весьма широкое использование какой либо мелкой функции, типа max. В C++ для этого дела обычно применяют шаблонные функции. Но си не имеет шаблонов. В си, для таких случаев, альтернативы макросам просто нету.

Но при программировании макросов есть много подводных камней. Они многократно описаны, переписывать это я не буду. Скажу лишь что лучше всего избегать любых побочных эффектов в макросах. Макросы должны вести себя как соответствующие функции. Это упростит понимание и поддержку.

За исключением одного момента. Есть в препроцессоре две переменных - __FILE__ и __LINE__. Использование их в инлайн функции сводит смысл их использования практически к нулю. Поэтому я позволяю себе использовать макроопределение, которое помимо параметров использует эти две переменных, но привязывает их к месту вызова макроса. Хотя с другой стороны это трудно назвать побочным эффектом, поскольку состояние выполнения никак не меняется. Но если попытаться заменить макрос на функцию, то соответствующая информация просто не будет доступна.

Вот совсем недавно ковырялся тут со своим проектам. У меня там широко используются утверждения (asserton). Я конечно стараюсь чтобы выражения в утверждениях были понятны, но в отрыве от контекста они обычно ни о чем не говорят. Поэтому я дополнил утверждения текстовой строкой, которая выводится в случае нарушения. После этого я заметил один момент.

Выражение assert(x < y) содержит в себе условие допустимости выполнения. И это вполне адекватно воспринимается. Но если попытаться дополнить его текстовой строкой, которая должна отображаться в случае нарушения условия - assert(x < y, "слишком большой x"), То все становится с ног на голову. Почему же x большой, если он меньше y? Долго думал во что мне переименовать assert, чтобы можно было использовать обратную логику утверждений, но потом бросил это дело, ибо assert - это просто утверждение.

Но логику выражений я твердо решил перевернуть (мои утверждения носят название STUB_ASSERT, но это почти ничего не меняет). А поскольку недвано начитался Фаулера, то делать это начал по всем правилам. :)

Сперва я создал другую функцию, на самом деле это просто макрос, которая ориентировалась на обратную логику утверждений. То есть выражение описывает условие остановки программы и сообщение, выводимое при остановке программы. Так логично.

Старый макрос я хотел бы объявить устаревшим, я всегда так делаю с функциями, чтобы обнаружить места где он используется, но есть проблема. В препроцессоре почти невозможно объявить макроопределеение устаревшим. Поэтому я превратил его в функцию, при этом я потерял возможность видеть файл/строку сбоя, но поскольку я всеравно собирался его удалять - мне это всеравно.

После чего я, долго и нудно, менял все функции на новый вариант, паралельно переворачивая логику утверждений. Поскольку функция использовалась широко - это была долгая работа, но поскольку старая функция существовала независимо от новой - я мог производить эту модификацию поэтапно. После того, как старый вариант исчез из обращения - я просто взял с помощью sed переименовал все новые функции на старые: sed -i -e 's/REAL_ASSERT/STUB_ASSERT/g'. И получил старое имя макроса и новую логику его использования. Все встало на свои места и стало логичным. Теперь главное не начать использовать новую логику по старому. :)

А теперь немного про рефакторинг. Большинство методов рефакторинга ориентируются исключительно на объектность. Но если смотреть свысока, то можно заметить что си имеет модули. Каждый модуль имеет свое состояние - свои статические переменные. Которые так же как и в C++ стоит делать приватными - статическими. Правило сокрытия информации распространяется на модули си практически так же как и на классы. То есть все, что не экспортируется лучше делать статическим. Это, просто-напросто, уменьшает количество сущностей, взаимоотношения которых нужно отслеживать программисту. Видя, что функция приватная/статическая мы сразу же, без лишних умственных напряжений, понимаем где она может быть использована. Рефакторинг в си может выполняться путем перемещения функций между модулями или например делением одного модуля на несколько по смыслу функций.

И вот еще один момент. Выделение метода в C++ происходит обычно в тот же класс. В си выделение метода будет происходить в модуль. Новая функция, естественно, имеет локальную область видимости - то есть объявляется как статик. Такое можно применять даже в C++ если например стоит задача минимизировать количество изменений, но для исправления ошибки требуется вынести часть кода из функции, или просто добавить в функцию некоторое новое поведение, не меняя при этом описание класса, которое обычно располагается в другом файле. В дальнейшем, при проведении планового рефакторинга, эту функцию легко можно включить в объект.

PS: может быть это все прописные истины. Но некоторые и их не знают, да и не грех лишний раз повторить любому. :)

пятница, 5 сентября 2008 г.

Последняя статья...

Сегодня последний день первого года существования этого блога :). Надо это дело как-то отметить. Серьезных материалов пока что-то в голову не приходит, буду писать всякую разную ерунду.

И в этой статье вы услышите про плохое поведение ntlmaps, про применимость макросов препроцессора и еще может быть какую нибудь другую ерунду. :) Но коротенькие статьи я не воспринимаю. Должна быть в статье какая нибудь глобальная мысль, размазанная по нескольким страницам текста. А то как у некоторых - "У меня сегодня хорошее настроение, поэтому я пишу эту статью, чтобы поделиться этим хорошим настроением со всеми остальными. Все, пока." - Не мой формат. :)

Итак. Долго не мог понять почему у меня firefox застревает на открытии страниц. Долго ломал голову. Пока сегодня случайно не вывел на панель Kima загрузку всех 4-х моих CPU (об этом чуть позже). И обнаружил что мой proxy-proxy клиент - ntlmaps, загрузил одно из CPU на 100%, в то время как я тщетно пытаюсь дождаться от firefox вменяемого ответа на свой запрос. ntlmaps - мне очень нужен, потому, что proxy сервер у нас конечно от MS, и авторизацию не принимает никак иначе кроме как через NTLM. Собственно для чего и был создан ntlmaps. Он берет на себя авторизацию на сервере, а все подключения пускает уже без авторизации, что позволяет ходить в интернет даже самыми отсталыми инструментами.

Но firefox вроде бы не является отсталым, что он успешно доказал - авторизовавшись на проксе самостоятельно. И вот я здесь. Теперь надо будет разобраться что же такое не нравилось ntlmaps в запросах?, что он так напряженно об этом думал...

Про практическую пользу дефайнов я наверное расскажу потом. А сейчас немного о том счастье, которое мне привалило на работе. Собственно попал под плановый апгрейд и получил в свое девелоперское распоряжение Intel Core 2 Quad с 4 гигами памяти. Я до сих пор никогда особо с EM64T дела не имел, да и с 4 гигами тоже. Я всегда хотел узнать насколько же 64 бита быстрее 32-х, но все это собственно ерунда, я даже исследования соответствующие проводить вряд ли буду. Потому, что 64-х битная система в отличии от 32-х битной видит все 4 гига памяти, в то время как 32-х битная видит только 3,2. 800мегабайт дополнительной памяти достаточно весомый аргумент в пользу 64-х бит.

Своп я отключил сразу... Не знаю как в линуксе, но теоретически ядро должно сильно упрощаться, если не заставлять его парится на тему вытеснения страниц. :) Хотя вот Vista на 4 гигах без свопа часто не справляется со своими функциями. Но моя 32-х битная система на данный момент юзает 900М с учетом всех кешей.

64-х битная система, которая сейчас неспешно устанавливается, вероятно будет юзать побольше. Где то читал, что примерно на 150 мег. Но 3 гигабайта всеравно в резерве.

На этом все. Надеюсь что теперь буду вылезать в блог почаще, завтра в любом случае надо отметить начало нового блогогода. :)