вторник, 1 января 2030 г.

О блоге

Более двадцати лет я занимался разработкой ПО, в основном как программист и тим-лид, а в 2012-2014гг как руководитель департамента разработки и внедрения ПО в компании Интервэйл (подробнее на LinkedIn). Поэтому в моем блоге много заметок о работе, в частности о программировании и компьютерах, а так же об управлении.

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

понедельник, 31 декабря 2029 г.

[life.photo] Характерный портрет: вы и ваш мир моими глазами. Безвозмездно :)

Вы художник? Бармен или музыкант? Или, может быть, коллекционер? Плотник или столяр? Кузнец или слесарь? Владеете маленьким магазинчиком или управляете большим производством? Реставрируете старинные часы или просто починяете примус? Всю жизнь занимаетесь своим любимым делом и хотели бы иметь фото на память?

Предлагаю сделать портрет в обстановке, связанной с вашей работой или увлечением. Абсолютно бесплатно. Очень уж мне нравится фотографировать людей в их естественной среде. Происходить это может так...

вторник, 22 ноября 2016 г.

[prog.c++] Разбор asfikon's "C vs C++". Часть 1.

Итак, поскольку на удивление большое количество желающих захотело послушать разбор тезисов статьи ув.тов.asfikon-а "Почему не лишено смысла писать код на C, а не на C++", то начнем выполнять обещание. Для того, чтобы не было завышенных ожиданий, сразу обозначу рамки, в которых будет вестись повествование. Посты с разбором "C vs C++" будут представлять из себя цитаты из оной статьи с моими комментариями о том, что в данной цитате представляет ценность, а что можно смело отправлять в /dev/null. Такой формат предполагался мной изначально. Приношу свои извинения, если кто-то ждал чего-то сильно другого. Впрочем, в комментариях можно оставлять свои соображения о том, какие темы вы бы хотели увидеть более раскрытыми. Может быть со временем получится дойти и до них.

Disclaimer. Есть у меня подозрение, что по большей части придется повторять скучные, банальные и давно известные вещи. Но, похоже "вот и выросло поколение"... Так что без скучных банальностей не обойтись.

Прежде чем перейти к конкретным цитатам, нужно обозначить важный момент, который позволит лучше понять, почему статьи, подобные разбираемой здесь "C vs C++", некорректны в принципе, безотносительно элементов бреда, спекуляций, подтасовок и передергиваний внутри.

суббота, 19 ноября 2016 г.

[prog.c++] Нужно ли делать подробный разбор статьи "Почему не лишено смысла писать код на C, а не на C++"?

Upd. Первая статья серии здесь: Разбор asfikon's "C vs C++". Часть 1..

В начале года я уже писал о странной тенденции -- активной агитации за использование чистого C вместо C++. Похоже, за прошедшее время ситуация стала только хуже :(

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

Так вот, в одном из флеймов в качестве аргумента была дана ссылка на статью ув.тов.asfikon-а "Почему не лишено смысла писать код на C, а не на C++". Тогда я не стал особо заострять внимание на данной статье (ограничившись лишь парой ироничных комментариев). Очевидно, что я недооценил пагубность ее влияния.

Вчера специально просмотрел статью еще раз. Блин, ну какой же бред там написан! Но ведь кто-то воспринимает написанное там всерьез. И это пугает.

Посему, есть мысль взять и обсудить основные тезисы данной статьи.

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

Пожалуйста, найдите способ высказать свое мнение: комментарием или +1, распространением данного поста, личным сообщением или еще как-нибудь.

Отдельно подчеркну, что основной целью предполагаемой серии постов будет не попытка доказать, что C++ отличный язык, для которого нет альтернативы. А рассказать о C++ объективно, не скрывая недостатки, но и не обмазывая говном достоинства.

пятница, 18 ноября 2016 г.

[prog.c++.bicycle] Пытаюсь наваять совсем простенькую функцию n_times

Когда-то уже заходила речь (кажется в G+), что хорошо было бы иметь в C++ функцию n_times для того, чтобы не нужно было циклы выписывать руками. Т.е. вместо того, чтобы писать:

const auto iterations = std::stoul(argv[1]);
forunsigned long i = 0; i != iterations; ++i )
   do_some_action();

А можно было бы изобразить что-то вроде:

n_times( std::stoul(argv[1]), []{ do_some_action(); } );

Ну и в итоге дозрел до того, чтобы добавить n_times в нашу маленькую библиотечку cpp_util. Самый первый, черновой вариант, выглядит вот так:

templatetypename COUNT_TYPE, typename LAMBDA >
void
n_times( COUNT_TYPE n, LAMBDA && body )
   {
      loops_details::ensure_non_negative( n );
      for( ; n; --n )
         body();
   }

В нем вспомогательная функция ensure_non_negative в случае, если COUNT_TYPE является знаковым типом, проверит, чтобы n имел неотрицательное значение. В случае же отрицательного значения будет порождено исключение std::invalid_argument.

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

using namespace cpp_util_3;

// Пользователь не против получить исключение, если ошибся
// со значением аргумента.
n_times< checked_loop >( std::stol(argv[1]), []{ do_some_action(); } );

// Пользователь зуб дает, что все будет чики-пуки.
n_times< unchecked_loop >( std::stol(argv[1]), []{ do_some_action(); } );

Или по такому:

using namespace cpp_util_3;

// Пользователь не против получить исключение, если ошибся
// со значением аргумента.
n_times( untrusted_n(std::stol(argv[1])), []{ do_some_action(); } );

// Пользователь зуб дает, что все будет чики-пуки.
n_times( trusted_n(std::stol(argv[1])), []{ do_some_action(); } );

Интересно мнение читателей: вам бы какой вариант был бы более удобен? Вообще без проверок? С проверками всегда? С отключаемыми проверками?

При этом если n -- это беззнаковое значение, то оно не проверяется вообще. Тут в дело вступает совсем простая шаблонная магия, которая устраняет избыточные проверки прям в compile-time.

PS. Кроме n_times еще хочется сделать up_to (или for_every_i):

using namespace cpp_util_3;

for_every_i( 010, [](auto i) { std::cout << i << std::endl; } );

Тут так же можно подумать на счет checked_loop/unchecked_loop...

понедельник, 14 ноября 2016 г.

[prog.flame] Прочел тут давеча "Why Rust"

Проглотил давеча за пару-тройку дней небольшую бесплатную книженцию "Why Rust" от O'Reilly. Книжица толковая. Собрает воедино все основные плюшки Rust-а в небольшом объеме текста. Что для меня очень большой плюс, т.к. эпизодические подходы к разным частям официальной документации позволяли разобраться в том или ином отдельном аспекте, но не создавали целостной картинки того, чем же должен брать Rust. Посему, если кто-то хочет составить себе впечатление об этом языке, но не хочет штудировать официальный "The Book", то имеет смысл взять и одолеть "Why Rust".

После прочтения у меня сформировалось несколько пунктов, по которым можно что-то сказать.

  1. Блин, синтаксис реально имеет значение. Первое, что отталкивает от желания связываться с Rust-ом (и отталкивает весьма сильно) -- это непонятное для меня желание экономить на всем. fn вместо func или function, impl вместо implementation, mut вместо mutable, u8 вместо uint8 и т.д.

    Казалось бы, ну синтаксис и синтаксис, можно привыкнуть к большой вариативности синтаксисов, почему же Rust-овский синтаксис так раздражает?

    Думаю, что это из-за высокой плотности содержимого в Rust-овском коде. Т.е. когда ты видишь что-то вроде:

    fn firsts<'a, 'b>(x: &'a Vec<i32>, y: &'b Vec<i32>) -> (&'a i32, &'b i32)

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

    func firsts<borrow A, borrow B>(x: borrow<A> Vector<int32>, y: borrow<B> Vector<int32>) -> tuple<borrow<A> int32, borrow<B> int32>

    Upd. Или вот так:

    func firsts(x Vector<int32> in A, y Vector<int32> in B) -> tuple<int32 in A, int32 in B>

    То код читался бы быстрее, особенно при наличии подсвеченного синтаксиса: распознавание отдельных частей выражения было бы проще.

    Да и вообще, если язык задумывался как безопасная альтернатива C/C++, то язык следовало бы делать максимально похожим по синтаксису на C и C++, тем более, что шаблоны в С++ и так позволяют записывать довольно сложные вещи. Так что можно было бы придумать нечто вроде:

    template<borrow A, borrow B>
    tuple<borrow<A, int32>, borrow<B, int32>>
    firsts(borrow<A, vector<int32>> x, borrow<B, vector<int32>> y)

    Имхо, мимикрия возможностей Rust-а под синтаксис C++ позволила бы не только упростить вход в язык для C++ников. Но и позволила бы с намного меньшими затратами переделывать уже существующий код под Rust. Если у меня 10MLOC кода на C++, то, понятное дело, на Rust я это все конвертировать не буду. Но, если у меня всего 25KLOC, то вместо того, чтобы переписывать все на Rust заново, я бы мог просто тщательно пройтись напильником по уже имеющемуся у меня коду и с минимальными услилями превратил бы C++ный код в Rust-овый код. Кстати говоря, когда-то на небольших программках я так поступал с переделкой C++ного кода в D-шный код.

  2. У меня складывается впечатление, что Rust стоит относить к той же группе, что и языки вроде Eiffel и Ada. Т.е. на них нельзя "наговнякать что-то по-быстрому". Нужно сесть, подумать, основательно разобраться в том, что нужно сделать, как это сделать, почему именно так. Потом еще побороться немного с компилятором, чтобы договориться о том, где ты ему веришь, а где он тебе должен верить. В итоге получится работающий код, в стабильность и надежность которого ты более-менее веришь (хотя так это или нет -- это отдельный вопрос, т.к. отсутствие утечек памяти и повисших указателей вовсе не гарантирует корректную работу приложения). Но все это будет не быстро, а по-началу и не легко.

    Так что да, если хочешь, чтобы все было "глобально и надежно" и у тебя есть возможность умножить свои сроки и бюджеты на 1.5, а в замен получить увеличение коэффициента спокойного сна на 15-20%, то Rust может быть хорошим выбором.

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

  3. Есть сильное ощущение, что Rust сейчас пользуется тем, что это новый язык, без груза совместимости с чем-то еще (как у C++ с C) и без необходимости поддерживать туеву хучу легаси кода. Поэтому в Rust-е все можно сразу сделать хорошо и "правильно". Отсюда и появляются нештяки, вроде встроенных в язык АлгТД, модулей и системы Cargo, единственного компилятора для всех платформ и пр.

    Пытаясь сопоставить C++ и Rust понимаешь, что не смотря на то, что в C++ можно обеспечить довольно таки высокий уровень безопасности кода (смотрим хелперы, вроде not_null и иже с ними), все-таки без серьезной модификации языка в C++ не получится завести те же самые АлгТД, а так же избавиться от таких вещей, как возможность вернуть ссылку на временный объект, который будет разрушен после возврата. Здесь Rust находится в заведомо лучшей позиции, чем C++.

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

    let a = ...;
    let b = a;

    приводит к тому, что содержимое a переносится в b. Но не всегда. Если a принадлежит примитивному типу или типу, реализующему трейт Copy, то произойдет копирование. Т.е. в каких-то случаях после b=a значением a пользоваться нельзя и компилятор будет бить за это по рукам. А в каких-то случая -- можно. Имхо, такая неоднозначность не есть хорошо, в частности для обобщенного программирования.

    Отдельным вопросом, о который уже сломано множество копий, является подход к обработке ошибок в Rust-а. Типа, везде должен возвращаться Result. Но и паники есть. Поэтому тем, кто озабочен написанием повторно-используемых библиотек, которые будут использоваться хрен знает в каких обстоятельствах, обеспечению exception safety (или правильно называть panic safety?) в Rust-е придется уделять столько же внимания, как и в C++. Но при этом еще и нужно будет долбаться с протягиванием кодов ошибок. Т.е. придется еще и писать код в духе "без try! ни строчки", как, например, это показывается в стандартной документации Rust-а:

    let f = try!(File::create("foo.txt"));
    let metadata = try!(f.metadata());
    let permissions = metadata.permissions();
    println!("permissions: {}", permissions.mode());

    Правда, в намедни вышедшем Rust 1.13 уже можно заменить try! вопросиками:

    let f = File::create("foo.txt")?;
    let metadata = f.metadata()?;
    let permissions = metadata.permissions();
    println!("permissions: {}", permissions.mode());

    Но тут мы опять приходим к тому, что в Rust-е имеет значение каждая закорючка в коде.

    Опять же, возьмем обобщенное программирование, в котором в Rust-е можно назначать аргументам шаблонов требования -- какие трейты должен поддерживать шаблон. Это сильно напоминает уже готовые concept-ы, которые в C++ все никак не могут завезти. Отсутствие ограничений на параметры шаблонов в C++ приводят к более сложной диагностики ошибок. Но, при этом, в C++ действительно возможен duck typing в compile-time: ну буквально, если шаблон ожидает "утку" в качестве параметра, то ему можно подсунуть все, что угодно, если это хоть сколько-нибудь похоже на "утку". И в этом огромная сила C++ных шаблонов и причина успеха шаблонов в C++. А вот в Rust-е, если шаблон ждет "утку", то и подсовывать ему нужно будет именно, что "утку". Может для тех, кто переходит на Rust с Java или C# это кажется нормальным, а вот мне это кажется очередным ограничением. Очередной попыткой ударить разработчика по рукам (см. пункт №2 выше).

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

  4. В самом начале книги "Why Rust" автор сделал заход, который мне показался жульническим и из-за которого я ко всему остальному материалу относился с изрядной долей скепсиса. Речь о наезде на понятие UB (undefined behavior) в языках C и C++. Мол, если вы в C/C++ обратитесь за пределы массива, то возникнет UB. А вот в Rust-е, мол, никакого UB -- вы получите вполне себе панику в run-time.

    Как по мне, так странный заход. Из категории, "мой дедушка боролся за то, чтобы не было бедных, а не за то, чтобы не было богатых". Типа того, что у программиста руки все-равно кривые, поэтому мы не будем заморачиваться тем, как вообще сделать невозможными выходы за пределы буфера. Мы просто сделаем контроль в run-time. А за счет того, что у нас есть итераторы, а так же компилятор умный, мы большинство таких проверок выбросим и вы даже никаких дополнительных накладных расходов не получите.

  5. Есть у меня подозрение, что подход разработчиков Rust-а можно выразить так: "Мы подумали о том, что вам может пригодится, а что не может, и дали вам то, чем вам стоит пользоваться. Этого вам хватит на все времена." Только вот я не уверен, что они [разработчики Rust] правы в своих убеждениях. Это касается разных вещей в языке. Начиная от необходимости ухода в unsafe для написания банального двусвязного списка, и заканчивая подходом к обеспечению thread safety. Так, есть у меня подозрения, что некоторые C++ные навороты, к которым приходится прибегать при работе с реальными проектами, в принципе возможны в Rust. Посему, если в реальной жизни придется столкнуться с чем-то нетривиальным (вроде реализации своего many-readers/single-writer spin-lock-а), то скорее всего с Rust-ом придется бороться, т.е. его сильные стороны могут начать работать против разработчиков.

Пора подводить сухой остаток. При наличии свободного времени, было бы полезно попробовать переписать какой-нибудь из маленьких C++ных проектиков и посмотреть на практике как это -- программировать на Rust-е. Но времени нет, а C++ного кода, который востребован и нуждается в развитии -- больше чем нужно. Посему смысла сейчас бросаться в Rust для меня нет. Не хочу сказать, что не верю в перспективы Rust-а и что у него нет шансов. Скорее всего шансов как раз предостаточно. Слишком уж много C++хейтеров сейчас вынуждены писать на C++ и страдать. Понятно, что большинство из них не имеет никакого влияния на выбор языка реализации для проектов. Тем не менее, Rust может найти свое место под Солнцем (и, скорее всего, найдет). Только вот у меня нет пока желания с Rust-ом связываться. Если Rust достигнет успеха, можно будет на него переключиться. Но пока этого не произошло, дальнейшая работа с C++ выглядит перспективнее.

воскресенье, 13 ноября 2016 г.

[business] Ищу примеры документов "Technical Assistance Agreement" и/или "Support Service Agreement"

Знаю, что некоторые компании бесплатно выпускают OpenSource-продукты под пермиссивными лицензиями (BSD, MIT, Boost и пр.), а продают техподдержку и консультации. Например, есть бесплатная библиотека POCO (не самая плохая, кстати говоря) и есть компания Applied Informatics, которая стоит за POCO и которая продает техподдержку пользователям POCO. Текст соглашения о такой техподдержке можно найти в виде PDF-ки на сайте Applied Informatics.

Поскольку мы выпускали, выпускаем и будем выпускать SObjectizer под BSD-лицензией, которая разрешает бесплатно использовать SObjectizer в каких угодно проектах, хоть открытых, хоть закрытых, то есть мысль попробовать предгалать платную техподдержку для SObjectizer. Для того, чтобы понять, как это все оформить, хочется ознакомиться как можно с большим количеством документов типа "Technical Assistance Agreement", "Support Service Agreement" и тому подобных. Но документов, касающихся техподдержки именно открытого, бесплатного ПО.

Буду признателен за ссылочки на подобные документы, которыми читатели поделятся в комментариях. Заранее спасибо!