Попробуем теперь использовать получившуюся программу рисования диаграммы наследования с реальным кодом, т.е. взятым из реального мира. Вопрос в том, какой именно код взять? Лучший кандидат для проверки - наша программа рисования, testclang.cpp! Что может быть реальнее? :)
— Потому что ты тут, — просто сказал Кот. — Иначе бы ты сюда не попала.
21 марта 2016 г.
10 февраля 2016 г.
CLang API: рисуем диаграмму наследования (clang-3.7)
Попробуем использовать CLang API для чего-нибудь полезного, например, для рисования диаграммы наследования.
В Clang API уже есть подобный функционал - функция CXXRecordDecl::viewInheritance(). Функция viewInheritance рисует диаграмму наследования для заданного класса используя библиотеку GraphViz. Но делает она это не совсем так, как мне бы хотелось. Поэтому я попробую написать свою собственную процедуру рисования диаграммы наследования, используя программу dot из пакета GraphViz.
3 февраля 2016 г.
CLang API: обход AST (clang-3.7)
Попробуем теперь реализовать обход AST встроенными методами (по мотивам статьи "How to write RecursiveASTVisitor based ASTFrontendActions"). На мой взгляд, реализация этой задачи стала намного проще по сравнению с версией 2.8.
20 января 2016 г.
CLang API: минимальный проект (clang-3.7)
После долгого перерыва я решил вернуться к попыткам использования clang API по нескольким причинам. Во-первых, clang API за это время продвинулся довольно далеко и стал "мощнее", так что хочется попробовать новые версии. Во-вторых, в старых статьях используется более ранние версии clang, а мне регулярно задают вопросы типа "почему у меня не компилируется/линкуется", поэтому хочется и обновить свои знания и добавить статьям актуальности. В-третьих, за пять лет внятных примеров с использованием clang так и не появилось, надо исправить такую несправедливость. Ну и в конце концов меня не оставляет надежда реализовать одну идею со статическим анализом кода, о которой, может быть, напишу позже.
Итак, попробуем написать и собрать минимальный проект для clang API на платформе Linux, используя последнюю стабильную версию llvm и clang. На момент написания статьи это версия 3.7.0.
23 августа 2013 г.
Цитата: Купер
Примечательная статья на Хабре про то, как дети не умеют пользоваться компьютерами, и про то, что это (почему-то) должно нас беспокоить. Это крик души человека, которого достали тупые пользователи, не умеющие настроить браузер для работы через прокси-сервер, а он один тут в белой тунике стоит. Напоминает анекдот про сантехника — "Учись! А то так и будешь всю жизнь ключи подавать."
Всё бы ничего, да только комментарии и количество народа, заплюсовавшего статью, наводят на мысли о том, что автор далеко не одинок, и да! в этом мире полно людей, желающих сделать мир лучше...
Ну что ж, удачи им всем! Главное — не бояться тунику запачкать.
Программисты вовсе не злодеи. Они много работают, чтобы сделать свои программы легкими в использовании. К сожалению, судят они по себе, так что программы получаются легкими в использовании лишь для других разработчиков программного обеспечения, но не для обычных людей.Алан Купер, "Психбольница в руках пациентов"
11 июня 2013 г.
Десять очень полезных возможностей C++11
Хорошая статья на Хабре: Десять возможностей C++11, которые должен использовать каждый C++ разработчик. Название, правда, слегка преувеличенное, хотя менее полезной статья от этого не становится.
В статье обсуждаются такие возможности как:
- auto
- nullptr
- range-based циклы
- override и final
- строго-типизированный enum
- интеллектуальные указатели
- лямбды
- non-member begin() и end()
- static_assert и классы свойств
- семантика перемещения
5 июня 2012 г.
String Similarity
Очень долго долбался с задачей "String Similarity" с сайта InterviewStreet. И так, и эдак ковырял, но удавалось уложиться только в 8 тестов из 10.
А на самом деле оказалось, что нужно просто применить Z-функцию. Мда, пошел читать букварь... Кстати, не могу найти английское название Z-функции. Если кто знает, напишите, плз.
З.Ы. На Хабре можно почитать хороший разбор алгоритмов поиска подстрок.
17 апреля 2012 г.
Visual Studio и googletest
В инете довольно много вопросов типа "как перенаправить вывод googletest с консоли в окно VS Output" от людей, которые пытаются подружить Visual Studio и googletest. Советы им дают самые разные, например, использовать PostBuildEvent или установить какой-нибудь плагин к Студии.
Хотя на мой взгляд самый удобный (гибкий, масштабируемый, переносимый) способ - это создать свою реализацию класса Printer и подключить её к фреймворку параллельно с остальными принтерами. Такой подход описан в примере №9 док-ции по GoogleTest Framework.
Сначала реализовать класс принтера, наследованный от EmptyTestEventListener:
// Provides alternative output mode which produces minimal amount of
// information about tests.
class TersePrinter : public EmptyTestEventListener {
void outDebugStringA (const char *format, ...)
{
va_list args;
va_start( args, format );
int len = _vscprintf( format, args ) + 1;
char *str = new char[len * sizeof(char)];
vsprintf(str, format, args );
OutputDebugStringA(str);
delete [] str;
}
// Called after all test activities have ended.
virtual void OnTestProgramEnd(const UnitTest& unit_test) {
outDebugStringA("TEST %s\n", unit_test.Passed() ? "PASSED" : "FAILED");
}
// Called before a test starts.
virtual void OnTestStart(const TestInfo& test_info) {
outDebugStringA(
"*** Test %s.%s starting.\n",
test_info.test_case_name(),
test_info.name());
}
// Called after a failed assertion or a SUCCEED() invocation.
virtual void OnTestPartResult(const TestPartResult& test_part_result) {
outDebugStringA(
"%s in %s:%d\n%s\n",
test_part_result.failed() ? "*** Failure" : "Success",
test_part_result.file_name(),
test_part_result.line_number(),
test_part_result.summary());
}
// Called after a test ends.
virtual void OnTestEnd(const TestInfo& test_info) {
outDebugStringA(
"*** Test %s.%s ending.\n",
test_info.test_case_name(),
test_info.name());
}
}; // class TersePrinter
Теперь подключить к юнит тестам вместе с другими принтерами:
UnitTest& unit_test = *UnitTest::GetInstance(); TestEventListeners& listeners = unit_test.listeners(); listeners.Append(new TersePrinter);
Всё. Теперь результаты будут выводиться без проблем и в окно Output, и в консоль, и куда-то еще, если подключены дополнительные принтеры. Формат вывода можно изменять по своему вкусу. Кроме этого, по клику в окне Output можно будет "прыгать" на код "свалившегося" теста.
26 марта 2012 г.
Проблемы переносимости: mutex
О проблемах переносимости приложений с использованием мьютексов. Мьютексы есть и в Windows и в POSIX, только работают они по-разному.
В Windows мьютексы позволяют выполнять рекурсивные локи из одного и того же потока без каких-либо deadlock'ов. В этом случае просто инкрементируется счетчик рекурсивных локов. Соответственно, чтобы освободить мьютекс для других потоков нужно будет столько же раз выполнить unlock(). Это их стандартное поведение, которое никак не изменить. Нужно просто учитывать это при дизайне алгоритмов приложения.
В POSIX мьютексы по-умолчанию не позволяют выполнять рекурсивные локи (т.е. второй вызов лока переведет поток в состояние ожидания), однако такое поведение может быть изменено путем изменения типа мьютекса. Возможно три варианта: 1) рекурсивные локи возможны (PTHREAD_MUTEX_RECURSIVE); 2) рекурсивные локи невозможны (PTHREAD_MUTEX_NORMAL); 3) режим errorcheck - рекурсивные локи невозможны, но вместо ожидания функция lock() возвращает ошибку (PTHREAD_MUTEX_ERRORCHECK).
Получается, что при необходимости портирования приложения с одной платформы на другую возможны некоторые проблемы. Например, при переходе с POSIX на Windows поведение PTHREAD_MUTEX_NORMAL невозможно, если не предусмотрено архитектурой. Как вариант, нужно будет заменить "родной" Windows-мьютекс на искусственный - можно использовать для этого семафор со значением 1, либо использовать какую-то стороннюю реализацию мьютекса, например из библиотеки Boost.
11 марта 2012 г.
Бесплатная альтернатива valgrind под Windows
Бесплатная утилита с функциональностью memcheck — Dr.Memory. Я давно такую искал, наконец-то она появилась.
Работает под Windows и Linux. Запускается из консоли, довольно легко настраивается (например, на отсеивание ненужных срабатываний). Отслеживает утечки памяти, неправильное освобожение, чтение/запись в "неправильные" области и т.п.
При этом позиционируется как самый быстрый, уверяют, что обгоняет даже valgrind. Думаю, что коммерческим инструментам он всё же будет проигрывать, но пользоваться им вполне можно — пробовал сам под Windows, рекомендую.
Вообще хороший список таких альтернатив: http://stackoverflow.com/questions/413477/is-there-a-good-valgrind-substitute-for-windows
26 декабря 2011 г.
Как получить системную строку форматирования времени
Получение системной строки форматирования даты и/или времени:
- В Linux:
char *nl_langinfo(nl_item item);в параметре
itemзадать значенияD_T_FMT,D_FMTилиT_FMT. - В Windows:
int GetLocaleInfo(в параметре
__in LCID Locale,
__in LCTYPE LCType,
__out LPTSTR lpLCData,
__in int cchData);
LCTypeзадать значенияLOCALE_SSHORTDATE,LOCALE_SSHORTDATEилиLOCALE_SSHORTTIME.
19 сентября 2011 г.
Выключение компрессии в OpenSSL 0.9.8
В OpenSSL реализована встроенная поддержка zlib и по умолчанию включена компрессия пересылаемых пакетов данных. Причем при сжатии очередного пакета используется информация об уже сжатых и отосланых пакетах. Например, если вы отсылаете три одинаковых пакета по 400 байт, и первый пакет сжимается до 275 байт, то второй и третий пакеты в сжатом виде займут всего по 12 байт. Такой подход очень экономит трафик, но применим только к протоколу TLS, потому что он гарантирует доставку пакетов и сохраняет последовательность, в которой пакеты были отосланы. Понятно, что для протоколов с негарантированной доставкой такой подход к сжатию не может быть применён. Это подтверждает RFC 3749 "TLS Compression Methods", в котором написано следующее:
Some compression methods have the ability to maintain state/history information when compressing and decompressing packet payloads. The compression history allows a higher compression ratio to be achieved on a stream as compared to per-packet compression, but maintaining a history across packets implies that a packet might contain data needed to completely decompress data contained in a different packet. History maintenance thus requires both a reliable link and sequenced packet delivery.
Протокол DTLS, выбранный для нашего проекта, был разработан специально для UDP и не гарантирует доставку пакетов. Но большей частью он основан на TLS, и по идее реализация протокола DTLS должна учитывать требования RFC 3749.
К сожалению, в OpenSSL этот момент не учитывается и в DTLS сжатие используется точно также, как и в TLS. Совершенно очевидно, что при потере одного из пакетов вся передача будет нарушена, и даже если этот пакет придет позже, то восстановить исходную последовательность принимающая сторона уже не сможет. Такой неприятный момент должен решаться простым отключением компрессии при использовании DTLS, однако в OpenSSL 0.9.8 это превращается в проблему, потому что в API просто нет функции отключающей компрессию. Вот цитата из документации на метод SSL_COMP_add_compression_method:
An OpenSSL server will match the identifiers listed by a client against its own compression methods and will unconditionally activate compression when a matching identifier is found. There is no way to restrict the list of compression methods supported on a per connection basis.
Начиная с OpenSSL 1.0.0 добавлена опция SSL_OP_NO_COMPRESSION, которая делает то, что мне нужно, но в версии 0.9.8 ее нет. В поисках решения я порылся в сети, но ничего внятного, кроме вот этого не нашел:
void disable_openssl_compression()
{
STACK_OF(SSL_COMP)* comp_methods = SSL_COMP_get_compression_methods();
sk_SSL_COMP_zero(comp_methods);
}
Этот хак помогает решить проблему с отключением компрессии, но приносит другую — утечку памяти. Пользоваться таким сомнительным методом не нужно.
Между тем, изучив исходный код OpenSSL, я обнаружил, что информация о доступных компрессорах хранится в простом списке, в который можно добавлять свои компрессоры с помощью функции SSL_COMP_add_compression_method. А раз можно добавлять, то значит должен быть способ и удалять элементы списка. Такой функции в OpenSSL API нет, но после изучения исходников функции SSL_COMP_add_compression_method можно написать свою, не пользуясь хаками, а только предоставленным API:
void disable_openssl_compression (void)
{
int n = sk_SSL_COMP_num(SSL_COMP_get_compression_methods());
for (int j = 0; j < n; ++j)
{
SSL_COMP *comp = sk_SSL_COMP_pop(SSL_COMP_get_compression_methods());
OPENSSL_free(comp);
}
}
12 сентября 2011 г.
Этот загадочный SecurityError (Flex 3)
После очередной порции изменений моё Flash-приложение, загруженное на сервер, перестало работать. То есть не совсем перестало, потому что по click-событиям выполнялось то, что нужно, но визуальные элементы вообще перестали хоть как-то отображаться — кнопки не хайлайтились, текст не скроллился, картинки не отображались... Сложилось такое ощущение, что перестали обрабатываться сообщения, отвечающие за отображение компонентов.
Из-за того, что часть приложения продолжала функционировать, я даже не сразу заметил проблему. При этом локально всё замечательно продолжало работать. Я довольно долго тупил, думая, что это проблемы совместимости установленных Flash-плееров в браузерах и в системе. Но через какое-то время понял, что такое различие в поведении локального приложения и приложения на сервере может вызвать SecurityError. Только где? (Тут я в очередной раз порадовался своей привычке делать маленькие, логически законченные коммиты. Локализовывать баг при таком подходе одно удовольствие и всего лишь вопрос времени.)
Причина обнаружилась в установленном smoothBitmapContent="true" для одного из тегов mx:Image. Так как картинка для тега загружалась не с сервера приложения, то контент по определению считался небезопасным. Доступ к битовым данным в этом случае вызовет SecurityError, а smoothBitmapContent="true" как раз требует такого доступа. Это изменение попало в репозиторий совершенно случайно, в результате неудавшихся экспериментов, но оказалось фатальным для приложения.
Для меня остается загадкой, почему SecurityError вызвал именно такое поведение, когда одна половина приложения работает, а другая половина — нет.
25 августа 2011 г.
Наши герои
ЗЫ. Что-то я не смог вставить сюда HTML5, так что вот iframe.
27 декабря 2010 г.
Нововведения в C++0x, которые не могут не радовать (меня)
Просмотрел повнимательнее список новшеств в будущем стандарте C++0x и обнаружил, что многие грабли, которые приходилось раньше реализовывать самостоятельно или городить воркэраунды, теперь будут поддерживаться конструкциями языка или добавлены в стандартную библиотеку. Это не может не радовать, потому что это сильно облегчает жизнь.
- Первым пунктом у меня идут потоки и объекты синхронизации (§30 [thread]):
thread,mutex,lock,condition_variable,future, ... Наконец-то! Надеюсь, теперь можно будет выкинуть весь самописный код, который реализовывал классы с подобной функциональностью для использования на различных платформах. Конечно, было бы неплохо расширить список поддерживаемых Стандартом объектов синхронизации, но и существующий список вполне хорош. static_assert— проверка условий на этапе компиляции, которой так не хватало в предыдущих версиях Стандарта. Очень полезна для проверки размеров типов данных и кратности этих типов каким-то заданным значениям. (§7 [dcl.dcl])- Списки инициализации и универсальная инициализация — новая синтаксическая конструкция (§8.5.4 [dcl.init.list]), которая позволяет инициализировать массивы, структуры данных и контейнеры следующим образом:
double ad[] = { 1, 2.0 };
S2 s22 { 1.0, 2, 3 };
std::map<std::string, int> anim =
{ {"bear",4}, {"cassowary",2}, {"tiger",7} };
shared_ptr— умные указатели (§20.9.11 [util.smartptr]). Объект, хранимый умным указателем, уничтожается только после того, как счетчик ссылок на объект будет равен нулю.- Лямбда-выражения (§5.1.2 [expr.prim.lambda]). К самим по себе лямбда-выражениям я отношусь достаточно спокойно. Даже, пожалуй, скептически, потому что мне кажется, что фанатичное использование лямбда-выражений приведёт к хаосу (особенно выражений, которые изменяют состояние классов). Но у лямбда-выражений есть свойство, которого мне раньше не хватало — их можно передавать в классы и алгоритмы стандартной библиотеки (и в пользовательские шаблоны) в качестве предикатов. Раньше надо было городить кучу кода, определяя дополнительные функции и классы, причем передавать в шаблон локальный класс запрещено. А с новым стандартом можно сделать так:
std::sort(x, x + N,
[](float a, float b)
{ return std::abs(a) < std::abs(b); });
- Автоматический тип данных
auto— тип объявляемой переменной определяется самостоятельно из типа инициализатора. Особенно удобно использовать при работе с итераторами контейнеров и другими типами, от которых зависит шаблон, чтобы не париться с горой typedef'ов. (§7.1.6.4 [dcl.spec.auto]) - Возможность вызова конструкторов класса из других конструкторов этого же класса (§12.6.2 [class.base.init]). Очень сильно облегчает написание конструкторов классов, помогает избежать дублирования кода инициализации.
- Цикл
forпо коллекциям (§6.5.4 [stmt.ranged]). Не очень актуально, учитывая появлениеautoи лямбда-выражений, но объём кода уменьшает.
int array[5] = { 1, 2, 3, 4, 5 };
for (int& x : array)
x *= 2;
- Из маленьких приятных мелочей можно упомянуть поддержку генераторов случайных чисел (§26.5 [rand]), атомарных операций (§29 [atomics]), регулярных выражений (§28 [re]) и новых алгоритмов. Например, алгоритм
iota(§27.6.5 [numeric.iota]), который заполняет заданный интервал инкрементирующимися значениями, начиная с заданного (подобный функционал я реализовывал с помощью алгоритма generate и самописного класса UniqueNumberGenerator).
9 декабря 2010 г.
STL: есть ли жизнь без исключений
-fno-exceptions в GCC, или /EHs- в Visual Studio). Однако, отключение обработки исключений вовсе не означает, что программа не будет генерировать исключения. Это всего лишь означает, что компилятор не будет создавать код для обработки исключений и уничтожения автоматических объектов. А исключения будут продолжать генерироваться — операторами new при нехватке памяти, методом at() класса std::vector при выходе за пределы массива, просто операторами throw расставленными программистом по своему коду terminate().Классы стандартной библиотеки сообщают об ошибках только с помощью исключений и никак иначе. Проблема использования STL в том, что когда мы отключаем поддержку исключений в проекте, то лишаемся механизма обработки ошибок. И если проблему с оператором
new можно решить его заменой на new (nothrow), то исключение, генерируемое в аллокаторе при вызове vector::resize(), вроде как ничем не заменишь.6 декабря 2010 г.
Clang API: обход AST (clang-2.8)
26 ноября 2010 г.
Clang API: получение AST в виде XML
Задача: с помощью Clang API получить XML-файл, описывающий абстрактное синтаксическое дерево (AST) исходного кода. Это можно сделать и простым запуском команды
clang с параметром -ast-print-xml в командной строке, но через API — интереснее :)24 ноября 2010 г.
Исключения C++ и производительность (2)
Итак, я знаю, что в общем случае при обработке сгенерированного исключения происходят дополнительные затраты процессорного времени. Какие они и чем мне это грозит?
23 ноября 2010 г.
Исключения C++ и производительность (1.1)
Наверное, нужно упомянуть предстоящие изменения в стандарте C++. Херб Саттер, который уже лет десять пытается бороться со спецификаторами
throw(), все-таки добился того, чтобы в новом стандарте спецификаторы throw(A, B, ...) считались устаревшими, а спецификаторы throw() были заменены на новый спецификатор noexcept именно с той целью, чтобы компиляторы могли выполнять оптимизацию, если возможно.Лично я считаю, что как спецификатор не назови (
throw или noexcept), то компилятору всё равно. Для программиста — да, noexcept куда более читабельно, но смысл при этом не меняется. Что касается спецификаторов
throw(A, B, ...), то если их все-таки упразднят в окончательной версии стандарта — то и бог с ними, если нет — тоже не страшно. Но мне интересно почему спецификаторы throw(A, B, ...) не использовались для углубленного статического анализа кода компиляторами, это ведь было возможно?.. Вместо того, чтобы их ругать, их можно было превратить в оружие. Ну да ладно, до принятия нового стандарта еще дожить надо :)Продолжение: Исключения C++ и производительность (2)