В самом центре Гомеля находится, не побоюсь этого слова, один из красивейших парков в Белоруссии. Парк выходит на обрывистый берег реки Сож. Набережную которой, после спада весеннего паводка, оккупируют рыбаки. Не берусь это утверждать, но у меня складывается впечатление, что с каждым годом их становится все больше и больше. Во время последней прогулки по парку уделил рыбакам немного своего времени. Небольшой фоторепортаж об этом ниже.
Размышления и впечатления, которые не хочется держать в себе. О программировании в частности. Ну и о творчестве, и о жизни вообще.
суббота, 22 июня 2013 г.
пятница, 21 июня 2013 г.
[work] В Google решили, что головоломки на собеседованиях -- это пустая трата времени
Прочитать об этом можно в интервью вице президента Ласло Блока "In Head-Hunting, Big Data May Not Be Such a Big Deal". Цитирую:
On the hiring side, we found that brainteasers are a complete waste of time. How many golf balls can you fit into an airplane? How many gas stations in Manhattan? A complete waste of time. They don’t predict anything. They serve primarily to make the interviewer feel smart.
Т.е. "мы выяснили, что при найме головоломки -- это пустая трата времени. Сколько мячиков для гольфа можно запихнуть в самолет? Сколько заправок в Манхэттене? Пустая трата времени. Они ни о чем не говорят. Они служат лишь для того, чтобы интервьюеры чувствовали себя умными"!
Блин, я помню, с каким экстазом на профильных форумах сторонники головоломок на собеседованиях трубили о том, что это используют в самом Google! Хотя здравый смысл (по крайней мере мой) говорил именно о том, о чем сейчас в интервью сказал господин Блок.
Еще один момент в вышеобозначенном интервью, который согрел мне душу:
Behavioral interviewing also works — where you’re not giving someone a hypothetical, but you’re starting with a question like, “Give me an example of a time when you solved an analytically difficult problem.” The interesting thing about the behavioral interview is that when you ask somebody to speak to their own experience, and you drill into that, you get two kinds of information. One is you get to see how they actually interacted in a real-world situation, and the valuable “meta” information you get about the candidate is a sense of what they consider to be difficult.
Т.е. если спросить у человека что-нибудь вроде "Приведите пример случая, когда вы решили аналитически сложную проблему", то вы получите информацию о двух вещах: о том, как человек вел себя в реальной ситуации и, плюс к этому, еще и "мета"-информацию о кандидате, например о том, что кандидат считает сложным.
Не откажу себе в удовольствии сказать, что одним из самых любимых моих вопросов на интервью была просьба рассказать о проекте или работе, которой кандидат может гордится. Чертовски приятно осознавать, что я года с 2003 применяю то, что в Google почти что официально признали эффективным сейчас :)
Ну и еще один маленький фрагмент, в защиту так любимых мной маленьких команд (выделение жирным мое):
We’re also observing people working together in different groups and have found that the average team size of any group at Google is about six people. So we’re trying to figure out which teams perform well and which don’t. Is it because of the type of people? Is it because of the number of people? Is it because of how they work together? Is there something in the dynamic? We don’t know what we’re going to discover.
Средний размер команд в Google оказался шесть человек. Но вот почему оно так, в Google пока не знают. Будут искать :)
[life.photo] Фотосессия для ангелочка
Вчера, гуляя по парку Гомеля, повезло подсмотреть, как родители с помощью фотографа устраивают фотосессию для своего маленького ребенка. Чуть-чуть поснимал и я. Неловко себя, правда, при этом чувствовал и постеснялся поставить оптимальный для этих целей 80-200, использовал стоявший на аппарате в тот момент старенький 35-70/3.3-4.5.
35-70/3.3-4.5 62mm, ISO 220, f=5.6, 1/125
четверг, 20 июня 2013 г.
[life.idiotic] Чтение мыслей на расстоянии
Только что в какой-то оккультной телепередаче ведущая, обращаясь к матери подозреваемой в наличии телепатических способностей девочки, выдала что-то вроде:
-- Это правда, что ваша дочь умеет читать мысли на расстоянии? И даже чужие?
На последовавшую за этим реплику моей жены "А чьи еще мысли можно читать на расстоянии" я, давясь смехом, смог сказать, что можно еще и свои, и это гораздо сложнее!
среда, 19 июня 2013 г.
[prog.thoughts] Философически о DSL-ях
Думаю, что нужно логичным образом закрыть внезапно всплывшую тему о DSL-ях (раз и два) своими соображениями о том, имеет ли смысл связываться с DSL и, если имеет, то когда и зачем.
Если говорить коротко, то есть два типа DSL: внутренний/встроенный (internal или emedded DSL) и внешний (external DSL). Внутренний DSL -- это не что иное, как использование возможностей синтаксиса какого-то общеупотребительного языка программирования для создания иллюзии использования другого, нового языка. Я такие вещи делал на Ruby и C++, наслышан, что похожим образом можно создавать встроенные DSL-и на Python-е. Был бы у меня опыт в Lisp-е, наверное, я бы развил эту тему больше, т.к. Lisp просто заточен под то, чтобы на нем под конкретную задачу делали свое подмножество Lisp-а ;) Из языков, с которыми мне приходилось работать, наименее подходит для DSL-естроения Java. И в этом, полагаю, ее сила :) Никакие Равшаты, Джумшуты и eao197 не способны извратиться на Java так, чтобы... Впрочем, это совсем другая история.
Так вот, по своей реализации внутренний DSL довольно слабо отличается от просто библиотеки (классов, функций, макросов или чего-то еще, что поддерживается host-языком). Поэтому, когда я интересовался темой DSL, бытовало мнение, что встроенный DSL -- это не более чем некоторый способ оформления прикладной библиотеки. С этим мнением я не согласен. И считаю, что отношение к DSL как к самостоятельному явлению, более высокоуровневому, нежели библиотека, является краеугольным камнем в вопросе "Имеет ли смысл делать внутренний DSL?"
Исходя из своего опыта могу сказать, что сначала нужно попытаться решить задачу наиболее простыми средствами. Если можно обойтись библиотекой, значит нужно ограничится библиотекой. Но, если становится видно, что при использовании библиотеки решение какой-то задачи упрощается ненамного, то тогда и только тогда можно смотреть в сторону внутреннего DSL.
Одним из критериев здесь может быть декларативность. Преимущество DSL в том, что он повышает декларативность описания какой-то предметной области или ее части. При помощи DSL разработчик кратко и понятно описывает то, что ему нужно, а внутренняя кухня DSL-я делает за него все остальное. По-моему, именно для этого DSL-и и должны использоваться. Т.е. критерий простой: если декларативность DSL-я в разы больше, чем использование библиотеки, то вариант с DSL-ем получает право на жизнь.
Но в декларативности DSL заключена и его слабость. За счет декларативности может потеряться гибкость. А у меня, как правило, она терялась. Для достижения простоты декларативного описания могут быть применены какие-то компромиссы или ограничения. Например, может быть жестко зафиксирован порядок инструкций в DSL-е. Или же какая-то одна инструкция может иметь всего лишь несколько жестко зафиксированных вариантов, расширить список которых прикладной разработчик не может. Со временем список претензий к DSL-ю может вырасти настолько, что расширение DSL-я окажется невозможным и нужно будет придумывать что-то другое: либо новый DSL, либо вообще отказ от DSL-я. Кроме того, отказ от DSL-я может назреть и раньше, если некому его будет дорабатывать. Но при этом не нужно забывать об уже имеющемся прикладном коде, который написан с использованием DSL-ей. Ведь просто так никто этот код переписывать не будет!
В этом плане, на мой взгляд, работа с библиотеками оказывается дешевле. Как правило, библиотеки могут расширяться. В крайних случаях, можно перейти на использование совсем другой библиотеки. При этом со старым кодом можно поступать по-разному: можно просто оставить его как есть и задействовать в проекте сразу две библиотеки для одних и тех же целей (старую для старого кода и новую для нового). Можно API старой библиотеки реализовать как фасад над новой библиотекой, тогда старый код сможет работать с новой библиотекой даже не зная об этом. Если вместо библиотеки было принято решение об использовании DSL-я, то таких возможностей применения нескольких DSL или маскирования нового DSL под старый DSL может вообще не быть.
Еще при работе с внутренними DSL-ями нужно смотреть на то, насколько простыми оказываются потроха самого DSL и оценивать возможные проблемы при его использовании, в том числе и освоение написанного на DSL-е прикладного кода новыми разработчиками. Например, язык Ruby более приспособлен для DSL-естроения, чем C++. То, что в C++ можно сотворить на макросах, как показывает жизнь, лучше не делать :) На этот счет могут быть разные мнения, но я здесь высказываю свою точку зрения, исходя из своего опыта. Чем делать что-то на макросах C++, я бы лучше воспользовался Ruby или Python-ом, из которых бы генерировал вспомогательный C++ код. Для того же Ruby эта проблема не столь актуальна. Хотя и там без чувства меры можно натворить такого, что на первый взгляд выглядит просто и лаконично, то затем, при отладке и развитии прикладного кода вызывает желание обойтись без DSL. Что до Lisp-а, то, вероятно, одна из причин его нишевого статуса как раз в том, что новые программисты не очень благосклонно относятся к сопровождению чужих DSL-поделий ;)
Разговор о внутренних DSL-ях нужно завершить упоминанием еще одного фактора: описание на внутреннем DSL это все-таки код на общеупотребительном, как правило весьма сложном и мощном, языке программирования. И если вам потребуется работа с этим описанием в каком-то инструменте, то вы можете столкнуться с серьезными проблемами. Например, вам нужен GUI-инструмент для конечных пользователей, в котором нужно каким-то образом отображать то, что задекларировано с помощью DSL. Скажем, есть описание C++проекта в виде .rb-файла для системы сборки Mxx_ru. А нужно отобразить содержимое проекта в какой-то IDE. Потребуется либо собственный парсинг ruby-текстов, либо какой-то другой подход к получению информации из проектных файлов.
Еще одна сторона этой проблемы -- это ограничение того, что пользователь может сделать с помощью DSL-кода. Например, rb-файл с описанием проекта для Mxx_ru -- это обычная Ruby-программа. Соответственно, пользователь может написать ее так, что помимо сборки проекта Mxx_ru будет удалять какие-то файлы, пытаться подбирать чужие пароли, DDOS-ить сайты или вытворять что-то еще. Если вы отдаете свой внутренний DSL на использование сторонним пользователям, вам нужно будет серьезно подумать о том, нужно ли ограничивать их возможности. И если нужно, то сможете ли вы это сделать вообще.
И вот как раз здесь можно перейти к разговору о внешних DSL-ях. Трудоемкость их создания, очевидно, много выше, чем для внутренних DSL. Но есть три фактора, которые заставят вас сделать выбор в пользу внешних DSL:
- необходимость автоматического разбора/обработки или генерации DSL-описаний. Опять же пример с описанием C++ проекта: если оно хранится в виде XML-файла, то реализовать инструменты для автоматической обработки этих файлов будет проще, чем для проектов Mxx_ru или SCons;
- необходимость очень жесткого контроля за тем, что может делать пользователь посредством DSL. Если в DSL-файле должно быть только описание C++ проекта, то в XML-файле это ограничение выдержать гораздо проще, чем в .rb-файле для Mxx_ru или .py-файле для SCons-а;
- необходимость работы с DSL-описаниями специалистов в своей прикладной области, которые могут иметь очень слабое представление о программировании. Например, если ваш DSL описывает последовательность фильтров, которые нужно применить к цифровому изображению или аудиозаписи, то вряд ли пользователь DSL-я окажется опытным Ruby/С++/Scala/Haskell/Lisp разработчиком.
Т.е. не смотря на намного большую трудоемкость создания внешних DSL, вы можете столкнуться с ситуациями, когда у вас нет другого выбора. Нужно просто брать и делать внешний DSL.
И здесь вопрос будет в том, насколько специализированный синтаксис DSL вам нужен. Если вы хотите иметь оптимально заточенный под конкретную прикладную область DSL, то вам придется взять на себя решение проблем лексического и синтаксического анализа. В принципе, все не так печально, поскольку есть куча инструментов разного уровня продвинутости, раскрученности и стоимости, как для того, так и для другого. Мне в свое время хватало flex-а и bison-а, хотя сейчас бы я смотрел в сторону Ragel и CoCo/R или ANTLR. Впрочем, тема лексического/синтаксического анализа настолько увлекательна, что есть опасность погрузившись в нее забыть про изначальную цель и заняться только играми с разными типами парсеров :) Поэтому, если вы не специалист в этой специфической области и занялись разработкой синтаксиса для своего DSL, то нужно трезво отдавать себе отчет о том, что в каждом генераторе парсеров есть свои проблемы, но ваша задача в том, чтобы всего лишь сделать свой DSL с учетом ограничений конкретного инструмента. И не комплексовать по этому поводу :)
Для себя я пришел к выводу, что вместо специализированного синтаксиса, заточенного под конкретную задачу, лучше использовать какой-то более универсальный (возможно более многословный) синтаксис. Особенно на теговой структуре. Хороший пример такого -- XML. Но только пример. Из-за своей многословности XML не есть хороший выбор для DSL-ей, описания на которых будут делать a) люди и b) простыми средствами (вроде обычных текстовых редакторов). Лично мне XML категорически не нравится и я остановился на подмножестве синтаксиса языка программирования Curl (подробнее об этом я писал здесь).
Теговая структура DSL в отличии от заточенного под задачу синтаксиса хороша простотой расширения языка со временем. Если DSL окажется успешным и востребованным, то вам (а может уже и не вам, а вашим последователям) обязательно придется его расширять. И делать это с тегами, на мой взгляд, много проще: можно добавлять/удалять дочерние теги или атрибуты, несложно писать инструменты для автоматической трансформации старых описаний в новые. Сложно передать разницу в сложности расширения bison-грамматики для расширения какой-то одной конструкции DSL и в добавлении еще одного дочернего тега в теговом DSL. Она просто колоссальна :)
Но вопрос выбора синтаксиса далеко не праздный и не простой. Сложно представить себе запись регулярных выражений или EBNF-грамматики посредством теговой структуры. Пытаясь выиграть на последующем сопровождении и выбрав теговую структуру легко потерять главное преимущество DSL -- простоту декларативности.
Нужно ли вообще связываться с внешними DSL при всей их трудоемкости? It depends, как говорится. Но не так все страшно. Даже конфигурационные файлы, в принципе, являются DSL-ями. А создавать их, особенно при наличии привычных инструментов, не так уж и сложно.
Кроме того, на любой философский вопрос "А нужно ли...?" всегда можно дать такой же философский ответ "Чтобы вы не сделали, вы потом пожалеете" ;) При наличии опыта, навыков и здравого смысла, DSL-естроение вполне себе оправдано. Важно лишь понимать, что внутренний DSL обойдется вам дороже, чем библиотека. Внешний DSL обойдется еще дороже, чем внутренний. Имеется в виду трудоемкость реализации библиотеки и DSL-ей. И если вы можете достичь результата дешевым способом, то зачем платить больше? :)
В пользу DSL-ей же работает то, что не все можно сделать библиотекой. Взять, например, описание регулярных выражений или описание программных интерфейсов (IDL). В этих случаях альтернативы DSL-ям нет. И если вы столкнулись с чем-то подобным, то и выбора у вас не будет, нужно будет браться и делать DSL. Ну а как его делать -- это уже каждому решать самостоятельно, исходя из своего опыта, бюджета и эстетических пристрастий :)
PS. Напоследок дам маленький вредный совет: если вы впервые взялись за создание внешнего DSL, то не спрашивайте советов на публичных программерских форумах. Там сидит столько "экспертов" в области парсинга (да и вообще всего на свете), что вы обязательно получите кучу противоречащих друг другу, а то и здравому смыслу, советов, примеров и мнений. Просто возьмите и сделайте как сможете. Получив опыт затем сможете понять, о чем именно вам говорят другие люди. В крайнем случае в частном порядке спросите совета у одного-двух людей, которых вы цените как специалистов и уже знаете, насколько точно/сильно их оценки коррелируют с действительностью.
[photo.thoughts] Из недавно нарытого
Отсутствие работы дает наличие некоторого свободного времени, которое обязательно нужно занимать чем-то полезным, чтобы всякие глупости не лезли в голову. Не знаю, насколько для меня полезно увлечение фотографией и будет ли из этого какой-то толк, но высвободившееся время я без сожаления трачу на фотографию -- пытаюсь снимать, читаю в Интернете чужие блоги, статьи, обзоры и рекомендации и т.д. Думаю, это мое увлечение видно по количеству постов с фотографиями в последнее время. В этот же раз я решил немного потрепаться вокруг да около.
В последнее время открыл для себя блоги трех фотографов: Ming Thein, Jeff Cable и Александра Петросяна. И просматривая фотографии, опубликованные в этих блогах, со мной произошел разрыв шаблона. Фотографии Ming Thein и Александра Петросяна воспринимаются мной в том же русле, что и, например, фотографии Дмитрия Шатрова или Майкла Фримана (автора множества хороших книг по фотографии): они все качественные. Возможно, многие из них мне не нравятся в плане идеи или композиции (либо же я просто еще не понимаю их). Но, по крайней мере, у меня нет к их снимкам претензий в техническом плане.
Совершенно другая ситуация с Jeff Cable. Он работал в качестве фотографа на нескольких олимпиадах. И его спортивные фотографии действительно хороши и интересны. Вот вот все остальное... Мне, конечно, и до такого уровня еще расти и расти. Но все равно я не понимаю, как карточки такого качества можно публиковать рядом с действительно хорошими работами из жанра спортивного репортажа.
Вот уж действительно: "Профессионал -- это не тот, кто делает что-то лучше всех; это лишь тот, кто делает это за деньги".
Отдельная больная тема для меня -- это цифровая обработка фотографий. Я пока лишь чуть-чуть знакомлюсь с Lightroom-ом. К обработке снимков в чем-то более серьезном (вроде Photoshop-а или Gimp-а) даже еще и не приступал. Поэтому с большим пиететом отношусь к рассказам о серьезной обработке снимков. Один из них недавно попался мне на глаза на ресурсе dpreview.com: статья Behind the Shot: Dark Matter.
Ну что можно сказать: внушаить. Только вот по мне, полученный результат -- это уже не фотография. Это какой-то вид изобразительного искусства, использующий, как один из инструментов фотографию. Но не фотография.
Я вообще никогда не понимал попыток с помощью фотографии создавать что-то нереалистичное. На мой взгляд, для изображения чего-то фантастического вполне пригодна живопись -- можно навыдумывать себе все, что заблагорассудится, а потом изобразить это. Фотография же мной воспринималась как возможность документально точно зафиксировать мгновение. И искусство фотографии, по-моему, состоит именно в том, чтобы с одной стороны, овладеть техникой фотографии, а с другой стороны -- и это гораздо более сложная задача -- научиться ловить то самое мгновение.
Причем техника играет очень важную роль. Возможности аппарата ограничены. Фотограф вынужден считаться с имеющимся освещением, с ограничениями носителя (чувствительность пленки или матрицы), вынужден выбирать сочетание диафрагмы и выдержки. Изначально понятно, что камера не способна в точности воспроизвести то, что видит глаз фотографа. Поэтому одна из сложных вещей в освоении фотографии -- это умение предугадывать то, что в итоге получится на снимке. И достигать имеющимися техническими средствами того результата, который нужен фотографу.
Мне кажется, что продвинутая постобработка снимка нивелирует эту составляющую фотомастерства. Если при постобработке какая-то часть снимка "вытягивается", то для меня это означает, что в своей работе "фотограф" переступает через возможности своей камеры. Перестает с ними считаться. И это не есть правильно. Это уже не фотография, а что-то другое.
При этом, полученный посредством серьезной постобработки результат вполне будет объектом изобразительного искусства. Вполне может быть и выдающимся произведением. Но мне бы хотелось, чтобы это произведение называлось какие-то другим термином. Есть же такие понятия, как "графика" и "живопись", каждом из которых есть еще и свои разделы, например, акварель, масло, темпера. Вот аналогичным образом хотелось бы иметь отдельно "фотографию" и, как бы это сформулировать, "отфотошопленную фотографию" или "фотореалистичное цифровое изображение".
Хотя, понятное дело, почва здесь зыбкая. Даже без фотошопа фотография может подвергаться серьезной обработке: экспокоррекция, изменение контраста, насыщенности, игры с интенсивностью отдельных цветов, ретушь и т.д. В какой-то степени все это было и раньше, в эпоху пленочной фотографии, когда изменение времени проявки пленки/фотоснимка (или даже смена реактивов) применялось для исправления ошибок фотографа или получения другого впечатления от снимка. Но, тем не менее, какое-то разделение должно быть. Одно дело, когда посредством настроек изменяется вся фотография целиком. Например, подвинули ползунок контрастности в Lightroom-е и изменилась контрастность всего снимка. Другое дело, когда разные манипуляции производятся над разными фрагментами снимка -- здесь вытянули небо, здесь осветлили тени, здесь исправили переэкспонированные лица, здесь дополнительно размыли фон, здесь повысили четкость деталей... Когда над снимком производят такие частные модификации, фотография переходит в другой вид изобразительного искусства. Имеющий больше общего с живописью.
Ну а под занавес еще раз вернусь к упомянутому вначале Александру Петросяну. В его блоге я нашел ссылку на вот этот пост. А там видео, показывающее Александра Петросяна "за работой".
Никогда до этого не видел процесса фотосъемок в стиле стритлайф. Поэтому часовое видео с участие Петросяна просмотрел на одном дыхании и с большим удовольствием.
вторник, 18 июня 2013 г.
[life.photo] Когда б вы знали из какого сора возводят храмы православные... :)
2/50mm, ISO 200, f=16, 1/250
Не, на самом деле возводят из нормальных материалов. Но рядом со строящимся неподалеку от моего дома храмом такие кучи строительного мусора, что невозможно пройти мимо и не сделать провокационную фотку ;)
понедельник, 17 июня 2013 г.
[prog.memories] Ruby DSL для логирования в C++ программах
Вдогонку к заметке о статье про DSL для работы с пакетами протокола UCP/EMI, расскажу об еще одном сделанном на Ruby DSL-е, который используется моей командой несколько последних лет. Причем этот DSL мной не разрабатывался. Я только сформулировал его цели и идею, а воплощением и доведением до ума занимались мои подчиненные: Игорь Мирончик и Борис Сивко, а затем Николай Гродзицкий и Николай Шмаков (надеюсь, что никого не забыл).
Потребность в данном DSL возникла из-за требования нашей техподдержки включать в документацию по программным компонентам раздел с перечислением всех сообщений, которые приложение вводит в лог (кстати, если не ошибаюсь, аналогичные требования есть в ГОСТ-овском ЕСПД). Первоначально это перечисление делалось вручную, посредством контекстного поиска по исходному коду обращений к подсистеме логирования. Но, понятное дело, занятие это муторное, результаты дает посредственные. А самое плохое, что если при выпуске документации по первой версии компонента еще удавалось набраться терпения и перечислить в документе все сообщения со всеми параметрами, то вот при обновлении версии разыскать и исправить все изменившиеся сообщения было уже не реально. Тем самым провоцировалось расхождение между приложением и документацией, а это не есть хорошо.
Поэтому появилась идея: сначала делать на специальном языке описание логируемых сообщений и их параметров. Затем из этого описания генерировать C++ные функции и классы, которые будут использоваться в коде приложения. А так же фрагменты документации, которые можно будет вставить в текст описания программного компонента.
Сгенерировать и то, и другое не проблема. Т.к. на выходе будет всегда обычный текст: либо .hpp-файл, либо кусок LaTeX-овского кода (т.к. документацию по своим компонентам мы делали в LaTeX-е и это в очередной раз оказалось выгоднее, чем использовать Word). Ну а в качестве базы для языка описания сообщений лога был взят Ruby. Объяснялось это тем, что, во-первых, Ruby в моем подразделении был вторым рабочим языком и проблем с сопровождением кода на Ruby быть не должно было. Во-вторых, у нас уже имелся опыт разработки embedded DSL-ей на Ruby, поэтому и с этой стороны никаких подвохов не ожидалось.
В общем, после небольшого мозгового штурма, довольно быстрой первой реализации и нескольких последующих доработок получилось следующее. Сообщения, которые компонент должен отображать в лог, описываются в отдельном solog-файле (это обычный .rb-файл но с другим расширением). При компиляции C++ проекта solog-файл отдается внешнему кодогенератору, который по содержимому solog-файла строит .hpp-файл с большим набором вспомогательных классов. Этот .hpp-файл должен быть подключен в соответствующие .hpp/.cpp-файлы проекта. Сгенерированные классы задействуются в C++коде посредством специальных макросов, которые выглядят для программиста как API подсистемы логирования. При написании документации из solog-файла тем же кодогенератором, но с другими параметрами, генерируется .tex-файл, который либо подключается в текст документации посредством \include, либо его содержимое просто копипастится в соответствующий раздел. В Subversion мы хранили только solog-файлы, но не результаты C++ кодогенерации, т.к. эти результаты автоматически воспроизводились при компиляции проекта.
Ну а теперь, думаю, пора переходить к примерам, чтобы не продолжать рассказ на пальцах. Вот как выглядит часть описания сообщений лога в solog-файле:
namespace :ig_inout_door_2 do anchor :outgoing_import do logic :started, LEVEL_NORMAL do brief 'Запущен импорт диапазонов новых исходящих сообщений' param :last_stop_point, 'client_message_id_t', 'место остановки последнего импорта' end logic :intermediate, LEVEL_NORMAL do brief 'Получен промежуточный результат импорта диапазонов новых ' + 'исходящих сообщений' param :range_count, 'unsigned int', 'количество найденых новых диапазонов' param :stop_point, 'client_message_id_t', 'место остановки импорта' end logic :finished, LEVEL_NORMAL do brief 'Импорт диапазонов новых исходящих сообщений завершен' param :range_count, 'unsigned int', 'количество найденых новых диапазонов' param :stop_point, 'client_message_id_t', 'место остановки импорта' end end # anchor :outgoing_import |
Здесь нужно дать небольшое пояснение. Наша подсистема логирования отличается от того, что построено на основе идей Log4j. У нас сообщения делятся всего на два типа: сообщения об ошибках (error) или о логических действиях (logic). А уже в рамках каждого типа сообщения есть свои уровни приоритетов (lowest, low, normal, medium, high, highest). Так же из нашего опыта использования лог-файлов, мы пришли к выводу, что сообщения в журнале должны помечаться двойным идентификатором. Его первая часть указывает тип операции, а вторая часть -- конкретное действие или стадию внутри операции. Например, save_to_file::opening говорит о том, что выполняется стадия открытия файла в рамках операции сохранения в файл.
В приведенном выше фрагменте посредством метода anchor описывается операция outgoing_import, которая состоит из трех стадий: started, intermediate и finished. Информация о каждой из стадий содержит свои параметры. В данном случае все параметры обязательны (если бы они описывались посредством метода opt_param, то это были бы опциональные параметры). Поэтому вспомогательный C++ код будет сгенерирован таким образом, что если при логировании какого-то действия операции outgoing_import программист забудет какой-либо из параметров, то он не сможет скомпилировать свой код -- возникнет ошибка компиляции. Так же ошибка компиляции возникнет, если программист ошибется с типом параметров.
В прикладном коде разработчик использует логирование следующим образом:
void a_inout_door_t::perform_new_outgoing_message_import() { const client_message_id_t last_stop_point = m_local_db->last_outgoing_message_import_stop_point(); so_log_msg_same_ns( outgoing_import, started ) .last_stop_point( last_stop_point ) .finish( *this ); last_outgoing_message_import_result_auto_ptr_t import_result = m_global_db->perform_new_outgoing_message_import( last_stop_point ); so_log_msg_same_ns( outgoing_import, intermediate ) .range_count( import_result->m_ranges.size() ) .stop_point( import_result->m_stop_point ) .finish( *this ); initialize_just_loaded_outgoing_message_range_list( import_result->m_ranges ); m_local_db->save_last_outgoing_message_import_result( *import_result ); m_last_outgoing_message_import_time = ACE_OS::gettimeofday(); so_log_msg_same_ns( outgoing_import, finished ) .range_count( import_result->m_ranges.size() ) .stop_point( import_result->m_stop_point ) .finish( *this ); } |
При работе приложения в лог-файл в итоге помещаются сообщения вида:
LOG [2010.05.04 12:00:16.714] Normal -- ig_inout_door_2::default::a_inout_door [outgoing_import::started] last_stop_point: 100265;
LOG [2010.05.04 12:00:16.714] Normal -- ig_inout_door_2::default::a_inout_door [outgoing_import::intermediate] range_count: 0; stop_point: 100265;
LOG [2010.05.04 12:00:16.714] Normal -- ig_inout_door_2::default::a_inout_door [outgoing_import::finished] range_count: 0; stop_point: 100265;
Здесь первое слово LOG указывает, что это сообщение о логическом действии (соответствует методу logic в solog-файле), Normal -- это уровень важности сообщения. Имя ig_inout_door_2::default::a_inout_door -- это имя SObjectizer-агента, который осуществляет логирование (оно берется подсистемой логирования когда следует вызов .finish(*this) в прикладом коде).
Для документации из вышеприведенного фрагмента строится LaTeX-ный код приблизительно такого вида (в данном случае он копипастом включен прямо в текст документации после комментария):
Компонент \CompIGInoutDoor{} оставляет следующие сообщения в журнале работы: \begin{flushleft}\begin{description} %%% Начало автоматически сгенерированного текста. \item[{[log][outgoing\_import::started]}]{~--- Запущен импорт диапазонов новых исходящих сообщений; Параметры сообщения: \begin{description} \item[last\_stop\_point]{место остановки последнего импорта} \end{description} } \item[{[log][outgoing\_import::intermediate]}]{~--- Получен промежуточный результат импорта диапазонов новых исходящих сообщений; Параметры сообщения: \begin{description} \item[range\_count]{количество найденых новых диапазонов} \item[stop\_point]{место остановки импорта} \end{description} } \item[{[log][outgoing\_import::finished]}]{~--- Импорт диапазонов новых исходящих сообщений завершен; Параметры сообщения: \begin{description} \item[range\_count]{количество найденых новых диапазонов} \item[stop\_point]{место остановки импорта} \end{description} } |
Что после генерации PDF-ки дает вот такой результат:
Вот такой инструмент мы себе в свое время соорудили. Удобно. В последние 3-4 года логирование в наших компонентах осуществлялось исключительно с его помощью. Ну а старый код при сопровождении, переписывании и/или доработках, переводится на этот DSL по мере надобности. Конечно, получившийся результат далек от идеала и есть еще над чем работать. Но это рабочий внутренний инструмент, который развивается когда для этого складываются предпосылки и находится время. В общем, обычная ситуация с обычным велосипедом ;)
В заключение хочу сказать, что создание DSL стало возможно из-за использования таких средств, как C++ (#include и #define), Mxx_ru для сборки проектов, LaTeX для документирования. Все это оказалось важным. Для той же Java, боюсь, соорудить что-нибудь подобное на коленке вряд ли получилось бы. Насколько я знаю, в C# поступили мудрее, там фрагменты одного класса могут находится в разных файлах, поэтому в C# использование результатов автоматической генерации исходных текстов так же возможно. Насчет чего-нибудь более модного в настоящий момент (как-то Erlang или Haskell) не знаю, не копенгаген ;)
[life.photo] Еще немного лесных ягод
Похоже, вчера открыл на себе закон лесных ягод: чем слаще ягоды, тем больше вокруг них голодных комаров :) Земляника была просто обалденно вкусной.
2/50mm, ISO 200, f=8, 1/200
воскресенье, 16 июня 2013 г.
[life.photo] Несколько черно-белых портретов
Год назад менял Nikon D90 на D700 в первую очередь из-за совсем другого уровня качества снимков на высоких ISO. Насколько я могу судить (по обзорам и тестам, которые можно найти в Интернете) D700 до сих пор по этому показателю очень хорош. Из Nikon-ов его однозначно превосходит разве что D800. Но D800 много что превосходит по своим возможностям ;)
Пару дней назад еще раз подверг свою 700-сотку небольшому испытанию. Снимал на ISO6400 с довольно большими выдержками, с рук. Получилось более-менее нормально. Но намного лучше снимки стали выглядеть после перевода в черно-белый вариант. Особенно если затем поиграться с ползунками Red, Orange и Yellow цветов. Оказалось, что так можно сделать карточки более выразительными, чем в цвете.
Отдельную роль играет цифровой шум, которого на ISO6400 предостаточно. Я с ним вообще не стал бороться, просто чуть-чуть приглушил. В результате на большом увеличении фотографии стали напоминать старые, снятые на черно-белые пленки с чувствительностью ГОСТ 250. В общем, как в молодости :)
Еще можно упомянуть ручное наведение на резкость. Диафрагма f=4.5 на маленьких расстояниях до объекта съемки делает глубину резкости на снимке очень небольшой. Доверять в таких условиях автофокусу, который может "зацепиться" за что угодно, лично мне стремно. Поэтому фокусировался вручную, стараясь взять в фокус глаза. Было непросто, фокусировочные экраны современных фотоаппаратов для ручного наведения на резкость приспособлены плохо. В очередной раз задумался о том, чтобы сменить себе фокусировочный экран на экран с клиньями или микропризмами. Но пока боязно :)
Ну, слов было сказано достаточно, пора переходить к слайдам. Вот несколько портретов, которые я сделал на небольшой дружеской пьянке.
2/50mm, ISO 6400, f=4.5, 1/80