История языков программирования

Краткая, неполная и практически полностью неправильная. Здесь.

1972 – Dennis Ritchie invents a powerful gun that shoots both forward and backward simultaneously. Not satisfied with the number of deaths and permanent maimings from that invention he invents C and Unix.

Сипласпласное

Сегодня провели с коллегой полдня в пространных рассуждениях о многопоточности и с чем ее едят.

Езь код

std::map<int, bool> myMap;

// Map is filled in with some data here

void SetValue(int key, bool newValue)
{
myMap[key] = newValue;
}

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

С моей точки зрения, с данными допущениями код SetValue можно считать потокобезопасным (присвоение bool считаем атомарной операцией, не углубляясь особенно в детали). Конечно, во всех этих допущениях просвечивает некоторое джидайство (к примеру, operator[] у мапа вообще-то для вставки используется), но если их принять, то никакого криминала в подобном грубом обращении с этим STL контейнером не будет.

Моя аргументация тут такова – SetValue() модифицирует только значения, не изменяя состояние контейнера, поэтому для обеспечения потокобезопасности кода в данном конкретном случае необходимо и достаточно обеспечить потокобезопасность оператора присвоения для типа значения. Контр-аргумент коллеги тоже имеет под собой все основания, ведь нигде прямо не написано, что присвоение элементу мапа нового значения – операция потокобезопасное. Я парировал тем, что в MSDN четко описаны случаи инвалидации итераторов, в число которых изменение уже хранимого значения без вставки или удаления нового ключа никаким боком не входит.

Впрочем, мы джедаи опытные и на личности не переходим. Но в итоге трехчасовой беседы с отсылкой к исходникам STL в коде SetValue появился таки появился лок с моим каментом “Меня заставили”.

Я не отрицаю, что в общем случае так делать не надо. Бо и правда, первый же залетевший дятел завтра заменит мап на какую другую неведому зверюшку и все развалится при первом же неосторожном чихе. Или какой юный падаван проигнорирует жЫрные ворнинги в каментах (а ворнинги там знатные – я, в отличие от, код без комментариев не пишу) выкорчует код и без изменений применит там, где не надо, в результате чего и огребет как тумаков от обладателей синего меча, так и поджопников – от владельцев красного, плюс еще щелбанов от юзверей на закуску.

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

Вопрос  – я и правда провафлил какое-нибудь условие или мне стоило стоять на своем до конца?

Шок и трепет

Android,  оказывается, не поддерживает Ad-hoc wireless networks. Причем, вовсе не потому, что эту фичу решили не реализовывать – ее, напротив, выпилили в версии 2.2

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

Ну и как дальше жыдь, я вас спрашиваю?

Теория и практика макаронолитейного дела

Наверное, однажды я напишу книгу. Это будет руководство о том, как писать самый страшный, ужасный и неподдерживаемый код.

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

class MyCoolClass
{
private:
BigFatObject* m_pOtherObject;
public:
MyCoolClass(BigFatObject* _pPtr):
m_pOtherObject(_pPtr)
{
}

void DoSmth()
{
std::string sName = m_pOtherObject->GetName();
// do smth
}

void DoMoreThings()
{
std::string sName = m_pOtherObject->GetName();
// do smth
}

// and other methods. All like this.

}

Вот такого вот горбатого лепят сплошь и рядом. И даже искры сомнения о том, что тут что-то не так, не проскакивает. Зато другим людям, которым посчастливится изменять BigFatObject или которые захотят переиспользовать код класса в другом месте, придется метать гром и молнии.

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

Рабоче-крестьянские зарисовки

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

(more…)

Историческое

По старой памяти установил DevPartner на VS 2010.

Это даже не УГ, это намного хуже.

А когда-то вполне себе рулезная штуковина была.

Оптимизированные грабли

Тут на днях коллеги подложили нам свинью.

#define true false    // happy debugging!

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

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

(more…)

Паттерн “observer” на tr1::function или почти настоящие делегаты

Про C++ делегаты на основе boost::function (tr1::function) я уже писал. В отличие от стандартного варианта обсервера для C++, который подразумевает фиксированный контракт (интерфейс) для подписчиков, вариант с функциональными объектами обладает определенными весомыми преимуществами (все они перечислены в посте по ссылке).

Но за удобство приходится платить. В данном случае расплачиваться приходится невозможностью реализовать отписку. И причина тому банально проста. Вот простейший пример

class Subject
{
   public:
       typedef std::tr1::function<void (const MyData&)> typeDelegate;

       void Subscribe(typeDelegate);
       void Unsubscribe(typeDelegate);
}

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

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

Отсутствие возможности сравнивать ставит жиирный такой знак вопроса на самой возможности отписки. Конечно, можно усложнить интерфейс субъектов и обязать подписчиков передавать какой-нибудь уникальный идентификатор при подписке и отписке. Таким идентификатором, например, может быть и указатель на подписчика, совсем как в классическом обсервере.

Делать так можно. Только это кривовато сразу по нескольким статьям. Поэтому нам нужно пойти другим путем.

Вышеозначенный делегат можно создавать динамически и заворачивать в умный указатель. А умные указатели уже можно сравнивать! Значит, получим

class Subject
{
   public:
       typedef std::tr1::function<void (const MyData&)> typeDelegate;
       typedef std::tr1::shared_ptr<typeDelegate> typeDelegatePtr;

       void Subscribe(typeDelegatePtr);
       void Unsubscribe(typeDelegatePtr);
}

Теперь, чтобы подписаться на события субъекта, подписчик должен сначала динамически создать объект типа typeDelegate, завернуть его в shared_ptr, а затем уже этот умный указатель использовать для подписки и отписки:

// Somewhere in subscriber's constructor, for example
Subject::typeDelegatePtr m_Delegate(
    new Subject::typeDelegate(bind(&Subscriber::Process, this, _1))
    );
//...
//   Subscribe for events on _pSubject
_pSubject->Subscribe(m_Delegate);
//
//
//   Unsubscribe from _pSubject
_pSubject->Unsubscribe(m_Delegate);

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

Как видно, если у объекта есть необходимость отписки, он должен сохранять оригинал делегата для этих целей. Если отписка не требуется (а такое бывает часто), локальная копия m_Delegate не нужна – временный экземпляр его можно будет создать при вызове Subscribe().

Можно еще сделать следующий финт ушами – слегка изменить интерфейсы и в субъект передавать не shared_ptr, а weak_ptr, образованный от оригинального умного указателя на делегат, который является членом класса-подписчика. При этом бесплатно решается проблема курицы и яйца: иногда требуется обеспечить, чтобы объект-подписчик автоматически отписывался при удалении, но никакой гарантии, что субъект еще жив, нет и быть не может. Достаточно лишь обеспечить, чтобы субъект проверял, не протухли ли “слабые” указатели (или делал им lock()) перед вызовом делегата, а подписчик владел оригинальным shared_ptr<typeDelegate>. При этом удаление подписчика будет вызывать инвалидацию всех инстанций weak_ptr, образованных от этого указателя, тем самым делая невозможным вызов убитого объекта через делегат, что и даст нам такую вожделенную возможность подписчика замести все свои следы даже без информации о том, на события каких объектов он подписан и живы ли они еще.

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

Fuck the standards!

TR1 уже давно вышел. Это хорошо. Студия 2008 и 2010 уже включают его по умолчанию. Это просто отлично.

Стандарт (а TR1 – это де-факто уже стандарт) – это всегда хорошо. Больше нельзя называться C++ программистом и закатывать истерики при виде безобидного bind. Теперь нужно либо напрячь мозги и таки освоить основы функциональщины, либо идти заниматься гуевой мышевозней.

А еще стандарты – это плохо. Потому что любое, даже самое разумное и оправданное изменение стандарта превращается в процесс перемещения и горы и Магомета к единому стандартизированному месту встречи. И стандартно занимает до чертиков времени, да и других восполнимых и не очень ресурсов тоже дофига требует.

Вот включили они bind в стандарт. И чо (что, шо, So what, нужное подчеркнуть)? А перегруженные операторы не включили! Бо не было их еще в бусте на момент подачи черновиков в стандарт. В итоге в TR1 вот так вот не сделать:

vector<Person> myVector;
//
// Some stuff here
//
vector<Person>::iterator iter = find_if(
myVector.begin(),
myVector.end(),
bind(&Person::GetLastName, _1) == "Pupkin");

А все из-за того, что тот bind, что в TR1, не перегружает оператор “==”, сцуко такой! В итоге, чтобы сделать то же самое, что на куске кода вверху, нужно городить двухуровневый bind с equal_to посередине, от вида которого даже у привычных к boost пассажиров может случится истерика:

vector<Person>::iterator iter = find_if(
myVector.begin(),
myVector.end(),
bind(equal_to<string>, "Pupkin", bind(&Person::GetLastName, _1)));

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

Нинавижу.

А следующего стандарта еще 10 лет ждать.

Workaround day

В 2010 студии M$ столько всего напеределывали, что создание новго проекта превратилась в поиск воркэраунда к воркэраунду. Например, зачем-то напрочь переделали определение custom build tool, причем гуй для его создания и редактирования привинтить забыли. В результате сегодня нашел, что сами  M$ рекомендуют проекты, которые используют custom build tool (ну, мало ли, вам Yacc’нуть чего надо или еще чего страшного натравить на исходники) создавать сначала в 2008 студии, а потом конвертировать в 2010.

Воистину, стоя и в гамаке. Еще и в противогазе.

К чести Студии, конвертирует она проекты без запинки.

Продолжаем есть кактус.

12 visitors online now
12 guests, 0 members
Max visitors today: 14 at 12:10 pm MST
This month: 14 at 02-05-2012 12:10 pm MST
This year: 29 at 01-23-2012 02:50 am MST
All time: 45 at 02-23-2011 09:11 am MST