Иногда нужно быстро проверить работоспособность какой-то идеи или алгоритма. Хорошо, когда у вас Linux и какая-то система из семейства Unix с установленным компилятором (имхо, в 90% случаев это будет правдой), вам просто нужно открыть консоль вызвать vim/emacs/joe/mcedit/etc набросать программку и вызвать компилятор. Но иногда вы в гостях/командировке/интернет-кафе, в общем тогда, когда компилятора нет под рукой, но есть доступ в интернет. Тут помогут онлайн-компиляторы.
Есть некая система (embedded), у которой вся память поделена на пулы различного размера и способы
выделения память (фиксированный размер, переменный размер). Есть механизм для выделения памяти
из этих пулов (на основе размера аллокации подбирает подходящий пул и делает от туда выделение) и
возвращение её туда, примем его таким:
void *pool_alloc(size_t size); // выделение памяти из пула
void *pool_calloc(size_t size); // выделение памяти из пула и зануление её
void pool_free(void *ptr); // возврат (освобождение) памяти в пул
есть так же определённые операторы new/new[]/delete/delete[]
для использования этого механизма в
C++ коде (код сильно упрощён):
void* operator new (size_t size)
{
return pool_alloc(size);
}
void* operator new[] (size_t size)
{
return pool_alloc(size);
}
void operator delete (void *ptr)
{
pool_free(ptr);
}
void operator delete[] (void *ptr)
{
pool_free(ptr);
}
На этом вводная часть закончена. Переходим к сути.
Существовала испокон веков некая большая структура (в терминологии C++ - POD тип), и код, использующий
её был повсеместно на чистом C. Назовём эту структуру так:
struct BigStruct
{
// a lot of fields
};
Соответственно память под эту структуру выделялась и освобождалась так:
...
big_struct_ptr = (struct BigStruct*)pool_calloc(sizeof(struct BigStruct)); // память сразу занулялась
...
pool_free(big_struct_ptr);
...
Время шло, код постепенно “переписывался” на C++. В кавычках, потому как это “переписывание” сводилось
к изменению расширения файла на .cpp
и исправлению очевидных ошибок и предупреждений (тут, с одной
стороны, используется -Werror
как опция для GCC, с другой стороны, предупреждение включены далеко
не все). И вот, в один прекрасный момент, весь код, который использует эту структуру, уже компилируется
C++ компилятором. Как результат, при очередной фиче или багофиксе в структуре появляется поле типа
std::vector<Foo> some_field;
То есть, одним мановением руки, структура перестаёт быть POD-типом, но… Механизм выделения и
освобождения памяти для неё не меняется!
Теперь сделайте паузу и пофантазируйте, к чему это приводит.
На практике, это решение жило около двух лет (на момент написания статьи) и никак себя не проявляло.
А приводило оно к постепенной утечке памяти: для std::vector
не вызывался ни конструктор ни
деструктор. Отсутствие вызова конструктора проходило незаметно, в этом конкретном примере “спасало”
то, что память занулялась и у класса нет vtable. А вот отсутствие вызова деструктора приводило к тому,
что все аллокации, сделанные внутри вектора, не освобождались. Маскировало эту проблему то, что
аллокации были очень маленькие (около 4 байт), и их было немного за весь период нормальной работы
аппарата от момента включения до выключения.
Выявило нагрузочное тестирование, где один алгоритм прогонялся атипичное, для нормальной работы,
количество раз (помогло логирование вызовов new
при помощи gcc’шной __builtin_return_address()
,
objdump -d
и базовые знания ассемблера).
UPD: Изменения, на основе этого патча, заинтегрированы в Qt Creator другим человеком. От меня была только ревью кода.
Во времена, когда проект Qt и все смежные тулы были под управлением VCS Perforce, появился плагин для Qt Creator’а для интеграции работы с этой системой, да вот только в своём развитии он конкретно и застрял в тех стародавних временах. Причина проста: проект переехал на Git, а перфорс нафиг никому не сдался. Как результат: плагин есть, и даже собирается, да вот только считать его хоть малость рабочим… не получается.
В воскресенье уже самолёт. Следить за нами можно по карте ниже, точки отсылаются при помощи трекера Delorme Inreach.
Фото с похода:
Собственно основу можно заметить невооружённым глазом:
- Убрана большая шапка
- Убраны большие закруглённые углы
Менее заметные:
- Большой логотип претерпел некоторый “ребрендинг” (этот логотип предлагается как картинка по умолчанию при репостинге в G+)
- Сделаны миникопии логотипа под разные размеры, один из них выводится в верхней навигационной строке, другой используется как favicon (теперь видно, что там картинка, а не чёрная клякса, но чёткости так и не получилось добиться)
- Возвратил стандартный поиск по сайту, потому как гугловский внезапно стал глючить и выдавать пустые ответы (при этом поисковые запросы типа “site:htrd.su ЗАПРОС” работают исправно), разбираться лень, т.к. сам не веб-разработчик и тратить на это время просто жалко (на крайний случай:
https://www.google.ru/search?q=site%3Ahtrd.su). Кроме того, это несколько ускорило загрузку страницы.
- Небольшая косметика правой колонки: убрано лишнее, подкорректированы размеры шрифтов
- Немного откорректированы шрифты заголовков
Чисто заметка, без вдавания в детали.
Для начала нужно поставить MinGW и MSYS.
Сделать это можно двумя путями. Первый, это поставить и то и другое средствами mingw-get-inst:
http://sourceforge.net/projects/mingw/files/Installer/mingw-get-inst/
Но там только GCC 4.7.2, а мне нужен был GCC 4.8 (где брать свежие сборки MinGW я уже
ранее писал, замечу только, что брать нужно версию с поддержкой threads-posix и dwarf, если вдруг захочется использовать бинарные сборки Qt). MSYS поставил при помощи mingw-get
(
http://sourceforge.net/projects/mingw/files/Installer/mingw-get/, а можно и вручную, скачивая и распаковывая файлы отсюда:
http://sourceforge.net/projects/mingw/files/MSYS/).
Распаковываем его, к примеру, в C:\msys
, далее:
cd C:\msys\bin
mingw-get.exe update
mingw-get.exe install msys
После этого MSYS будет расположен в C:\msys\msys\1.0
.
Если потребуется ещё что-то от msys: mingw-get list
вам в помощь.
Далее сборка Boost (у меня 1.53.0).
Распаковываем его, например в D:\boost_1_53_0
Настраиваем пути до компилятора (делаем это из запущенной копии cmd
):
set PATH=c:\msys\msys\1.0\bin;c:\mingw\bin;%PATH%
Тут обращаю ваше внимание на один факт, за который разработчикам буста нужно малость по рукам настучать:
буст соберётся хорошо только в случае, если MinGW поставлен в C:\MinGW
и ни как иначе!
После чего собираем bjam
:
cd D:\boost_1_53_0\tools\build\v2
bootstrap.bat gcc
b2 --prefix=C:\boost install
set PATH=%PATH%;C:\boost\bin
Теперь мы готовы собирать сам буст:
cd D:\boost_1_53_0\
bjam -j2 toolset=gcc --build-type=complete --prefix=C:\boost install
Вместо -j2
ставим нужное количество потоков сборки.
После продолжительной сборки буст будет расположен:
- заголовки:
C:\boost\include\boost-1_53
- библиотеки:
C:\boost\lib
И полезные параметры при сборке:
-Wno-unused-local-typedefs -DGLIBCXX_FORCE_NEW -D_WIN32_WINNT=0x0501 -DBOOST_THREAD_USE_LIB -DBOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN
Для совсем ленивых, напоминаю про
альтернативную сборку MinGW от Стефана, в ней он уже обновил GCC до 4.8 и Boost до 1.53. Кстати, Стефан работает в Microsoft :)
Надеюсь мои ощущения, что космические программы стали более активными, не просто ощущения. Поздравляю всех причастных!
Некоторые компиляторы (если не все) поддерживают такую функциональность: через параметр командной строки можно указать файл или файлы, которые будут подключаться автоматически к каждому обрабатываемому файлу. У GCC это опция `-include ````.
В некоторых проектах используют эту особенность, как результат - явного включения файла нет, а парсер в Qt Creator’е не видит части объявлений.
Решения единого нет: для каждого типа проекта (qmake, cmake, generic & etc), теоретически, оно своё (если есть вообще). В моём случае используется Generic project, для него решение существует.
И решение достаточно простое: Generic Project Mananger использует несколько файлов для хранения настроек проекта:
ProjectName**.creator**
- головной файл, по сути не содержит никакой полезной информации
ProjectName**.files**
- список файлов проекта (может содержать и не исходники)
ProjectName**.includes**
- пути поиска заголовочных файлов
ProjectName**.config**
- как гласит комментарий внутри этого файла: “ADD PREDEFINED MACROS HERE!
”
Вот последний файл самый интересный. Комментарий немного наводит в ступор и мы решаем, что тут можно только писать код вида:
#define MACRO some_value
На самом деле, парсером этот файл воспринимается как обычный заголовочный файл и информация из него используется для парсинга всего остального. Чуете профит? Если нет, подсказываю: по сути это и есть наш “pre-included” header для парсера! И писать в нём мы можем всё, что понимается препроцессором (если быть точнее: парсером скеатора). Т.е. мы можем сделать такое:
#include “includes/pre-included.h”
И всё, что объявлено в нашем pre-included.h
станет доступно парсеру при обработке каждого файла. Дело сделано!
Было бы любопытно узнать как такое можно реализовать для Qmake и Cmake проектов.
PS вопрос этот я поднял в списке рассылки Qt Creator’а, пока ждал ответ, в голову пришла идея с #include
, в результате список только подтвердил моё изыскание :)
UPDATE: рано обрадовался, внезапно, решение отказалось работать. Имхо, причина в несовсем корректном использовании .config
файла.
Что-то не нашёл официального русского перевода книги, только сделанный в частном порядке (читать сложновато):
http://enotcpp.blogspot.ru/2011/11/c-template-metaprogramming-david.html
На английском:
http://ultra.sdk.free.fr/misc/docs/Addison.Wesley.C++.Template.Metaprogramming.Concepts.Tools.and.Techniques.from.Boost.and.Beyond.pdf или среди торрентов:
http://rutracker.org/forum/viewtopic.php?t=1140883
Будет дополняться и оформляться.