понедельник, 31 марта 2008 г.
Holly Wars
Оказывается компилятор фирмы Borland(входящий в поставку Turbo C++) генерирует в 3!!! раза более медленный код чем компилятор Microsoft (спасибо форуму Vingrad и Mayk-у в частности). В принципе я не сомневался, что он медленнее, но то что на столько, говорит о многом. В качестве benchmark теста использовалась игра life. Profile Guided оптимизацию я не использовал.
Из всего этого можно сделать один вывод - компиляторы CodeGear не стали лучше со времен С++ Builder 6, а это очень и очень давно, просто целая вечность.
Если переписать все это на С#, наверное, будет работать не намного медленней, чем код на Delphi. :-D
среда, 19 марта 2008 г.
Мистичесские 5%
Довольно занятная, статья о том, почему 5% программистов в 20 раз продуктивнее остальных 95.
пятница, 14 марта 2008 г.
Мне в последнее время очень жалко свое время, которое тратится иногда на не совсем нужные вещи, например чтение rss, форумов, почты и тд. Еще больше времени и сил тратится на не очень полезную работу, например я недавно потратил рабочий день на то, что-бы улучшить код для записи данных в лог. Логгер теперь конечно работает круто, но это меня ни на шаг не приблизило к цели, не позволило добавить новый функционал в продукт. У меня иногда очень много времени уходит на такие фрустрации))
Что-бы стать немного эффективней, я сделал вот что: завел текстовый файлик, в который пишу что я уже сделал. Ничего особенного, просто несколько записей в день, но это позволяет мне потом определить что я что-то делаю не так, трачу время в пустую. К тому-же это немного подстегивает, если я нихрена не делаю, то и писать мне нечего.
Многие пишут todo на день или на неделю, но мне лично это не помогает, так как я просто болт на это забиваю :).
И еще я недавно установил плагин ViEmu для студии. Теперь я могу не делать лишних движений за клавиатурой ^_^.
четверг, 13 марта 2008 г.
Post mortem
Этот пост начинает серию о диагностике windows приложений.
Традиционно, для того что-бы найти ошибки в работающей программе, используются лог файлы. Программа пишет себе в лог, а в случае возникновения проблем разработчик по логам может найти баг.
Здесь есть несколько проблем, главная - нельзя писать много, во первых сложно анализировать, во вторых возникает оверхед, особенно, когда одновременно работает множество программ работающих с диском... Ну и конечно проблема с тем, что писать, а что нет. Значения переменных, вызовы функций, все это записывается только за тем, что-бы в случае возникновения проблем, можно бы было приблизительно восстановить состояние стека.
Есть еще один путь, использование средств ОС для диагностирования программы. Самое вкусное, на мой взгляд, отладочный дамп.
Для создания дампа нужно использовать средства библиотеки DbgHelp. А именно функцию MiniDumpWriteDump. Функция получает на вход структуру EXCEPTION_POINTERS, которая содержит контекст в котором произошло исключение. Эту структуру в принципе можно получить при обработке исключения, с помощю функции GetExceptionInformation (эту функцию можно вызывать только внутри filter expression(__except) обработчика исключения), а так-же при срабатывании фильтра необработанных исключений (поставить можно функцией SetUnhandledExceptionFilter).
Так же функция принимает хэндл открытого для записи файла, идентификатор и хэндл процесса.
В опциях проекта нужно сказать компоновщику, что нужно генерировать отладочную информацию (указать *.pdb файл).
код:
LONG WINAPI UExceptFilter(_EXCEPTION_POINTERS* ExceptionInfo) { char block[_MAX_PATH]; SYSTEMTIME st; GetLocalTime(&st); _snprintf(block, _MAX_PATH, "CrashDump-%04d%02d%02d%02d%02d%02d%03d.dmp", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); HANDLE hProc = GetCurrentProcess(); HANDLE hFile = CreateFile( block, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); MINIDUMP_EXCEPTION_INFORMATION eInfo; eInfo.ExceptionPointers = ExceptionInfo; eInfo.ThreadId = ::GetCurrentThreadId(); eInfo.ClientPointers = NULL; BOOL ret = MiniDumpWriteDump( hProc, GetProcessId(hProc), hFile, MiniDumpWithFullMemory, ExceptionInfo ? &eInfo : NULL, NULL,NULL ); CloseHandle(hFile); return EXCEPTION_CONTINUE_SEARCH; } int _tmain(int argc, _TCHAR* argv[]) { SetUnhandledExceptionFilter(&UExceptFilter); //......... }Далее, после возникновения ошибки, просим клиента отправить нам отладочный дамп, можно это дело автоматизировать, и отправлять средствами самой программы. Потом открываем его студией (OpenSolution, потом выбираем тип файла memory dump), и запускаем отладчик. Нужно что-бы в папке с дампом находился исполняемый файл, файл с отладочной информацией (*.pdb), и исходники - все это должно быть той-же версии что и экзешник записавший дамп, желательно, что-бы версии используемых библиотек так-же совпадали (хотя есть подозрение что будет работать в любом случае). После запуска можно будет в окне call stack просматривать состояние стека так, как-будто запущена программа, можно так-же смотреть значения локальных переменных. Но есть одна проблема, место возникновения исключения нельзя узнать точно из за оптимизаций компилятора, который может инлайнить многие функции. Самое сложное в этом - хранить исходники, exe и pdb файлы для каждого релиза :)
пятница, 7 марта 2008 г.
вторник, 4 марта 2008 г.
Дизайн класса исключения
В большинстве случаев от класса исключения не требуется больше, чем то что уже есть в классе std::exception, то что я напишу дальше можно и не читать
Мне недавно понадобилось привязать к исключению произвольные данные, строку, или число, или адрес из контекста в котором произошло исключение. До этого я пользовался обычным, унаследованным от std::exception классом. Самым приемлемым для меня вариантом оказалось использование вариантных данных. Задавать тип данных шаблонным параметром оказалось не удобно, точнее неудобно оказалось потом обрабатывать такие исключения, так как код, обрабатывающий исключение должен знать данные какого типа содержит исключение. В общем как-то так:
template<typename T> class my_exception_t : public std::exception { T meta_data_; .... try { do_something_hazardous(); } catch(???) { обработка исключения }в блоке catch можно конечно ловить std::exception, но тогда нельзя получить доступ к метаданным исключения, иначе нужно знать как инстанциируется my_exception_t. Альтернативный вариант - определять тип метаданных в рантайме. Для этого я выбрал boost::any, boost::variant нужно собирать, потом добавлять в каждый проект lib, что очень лень)) Получилось такое:
using boost::any_cast; using boost::any; class EFailure : public std::exception { any error_code; public: template <typename Param> EFailure(Param ec, const char* m) : std::exception(m), error_code(ec) { } EFailure(const char* m) : std::exception(m), error_code() { } EFailure(const EFailure& e) : std::exception(e), error_code(e.error_code) { } virtual any get_error_code() const {return error_code;} };Ну а для вывода всего этого в поток пришлось сделать страшное))
namespace { typedef boost::tuple<int, unsigned int, long, unsigned long, double, float, char, unsigned char, std::string> predefined_types;//типы которые может принимать error_code //форматирует error_code и выводит в поток template<typename T> struct error_code_provider_t; template<typename Head, typename Tail> struct error_code_provider_t< boost::tuples::cons<Head, Tail> > { void operator() (any& value, std::ostream& os) { if ( value.type() == typeid(Head) ) { os << any_cast<Head>(value); } else { error_code_provider_t<Tail> next_provider; next_provider (value, os); } } }; template<> struct error_code_provider_t<boost::tuples::null_type> { void operator() (any& value, std::ostream& os) { os << "can't read this value"; } }; }; std::ostream& operator << (std::ostream& os, const EFailure& f) { error_code_provider_t< predefined_types::cons > error_code_provider; os << f.what(); if ( ! f.get_error_code().empty() ) { os << " ["; error_code_provider(f.get_error_code(), os); os << "]" << std::endl; } return os; }
понедельник, 3 марта 2008 г.
boost::mpl - первые впечатления
Недавно состоялось мое знакомство с библиотекой boost::mpl, надо сказать что просто с ходу начать ее использовать как например boost::any или boost::shared_ptr довольно проблематично. Знакомиться нужно по книге "C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond" таких известных людей как David Abrahams, Aleksey Gurtovoy. Я пока-что осилил первые 3 главы, но мне уже есть что сказать)))
Начинается все с того, что - "In C++, it was discovered almost by accident"(имеется ввиду возможность мета-программирования). Вот оно оказывается как, а я думал шаблоны специально для этого создавались , а на самом деле все банально :-D.
Далее постепенно раскрываются основы организации библиотеки, любая мета-функция имеет вид: metafunction-name::type, с точки зрения языка metafunction-name - это имя класса, а type - это объявление типа. С точки зрения библиотеки metafunction-name, это имя мета-функции, которая отображает множество своих аргументов (параметров шаблона), в множество возвращаемых значений (type). Числовые значения то-же являются типами, если какая-либо мета-функция возвращает константу, то делает она это через wrapper(что-то вроде IntToType в Loki) вот так: metafunction-name::type::value.
Для того что-бы передать в мета-функцию другую мета-функцию (higer-order metafunctions), используется интересный прием, вместо использования шаблон шаблонных параметров(дикое название :)) используется простая идиома.
В место привычного:
template<typename arg, template<typename T> class Pred> struct higer_order_fn { typedef typename Pred<arg>::type type; }; там пишут так: struct foo { template <typename arg> struct apply { typedef ... type; }; }; template<typename arg, typename Pred> struct higer_order_fn { typedef typename Pred::apply<arg>::type type; };что поваляет писать мета-функции возвращающие другие мета-функции, вот такой вот approach)) Если подитожить, то мета-класс это простой класс(не шаблонный), содержащий вложенный шаблонный класс(мета-функцию), который называется apply. Единственное что мне не нравится, это placeholders. В двух словах, это штука которая позволяет писать вот так: mpl::transform< D1,D2,mpl::minus<_1,_2> >::type, вместо того, что-бы описывать мета-класс, так как показано выше. Не нравится мне в них даже не то, что как они реализованы я и представить не могу, а то-что код не выглядит как корректный код на С++. Для code review это не очень подходит, но к счастью у нас такое не практикуется))) P.S. Посмотрел на выходных фильм - "Ванильные небеса". Как могло такое случиться, что я его не видел раньше, фильм просто супер, производит очень сильное впечатление.
Подписаться на:
Сообщения (Atom)