Сборка и тестирование проекта при помощи CMake и CTest.

Статья дико устарела. Новая, актуальная на 2025 год версия: How to compile C++ in 2025. Bazel or CMake?

Думаю что большинство C++ разработчиков так или иначе сталкивались с CMake, в то же время что такое CTest и как с его помощью можно автоматизировать модельное тестирование знают далеко не все.
Для того, что бы показать как можно использовать CMake и CTest вместе, я в качестве примера создал маленькую библиотечку, состоящую из пары строк кода, которое умеет складывать и делить Кроме приложений CMake и CTest, понадобится какая-либо UnitTest библиотека (в данном примере я взял Boost.Test, хотя, можно взять и GoogleTest). Тут стоит обратить внимание на то, что CTest не является фрэймворком для написания тестов. CTest – это приложение для запуска тестов и, если это необходимо, отправки результатов в какое-либо из поддерживаемых хранилищ. Continue reading

Когнитивный диссонанс

Вчера я был поражен на столько, что усомнился в том, во что верил уже лет 10. А именно в том, что при выравнивании 8, размер структур с полями {u64, u32, u16} и {u8, u32, u16} будет одинаков, т.е. равен 16. А ведь приложение собранное Rust утверждает что в первом случае размер 16, что и следовало ожидать, а во втором 12(!!!).

type tuple_type1 = (u64, u32, u16);
type tuple_type2 = (u8, u32, u16);

fn main() {
    io::println(#fmt("size of tuple_type1 = %u, size of tuple_type2 = %u",
        sys::size_of::<tuple_type1>(), sys::size_of::<tuple_type2>()));
    io::println(#fmt("align of tuple_type1 = %u, align of tuple_type2 = %u",
        sys::align_of::<tuple_type1>(), sys::align_of::<tuple_type2>()));
}

Содержимое консоли:

> ./main
size of tuple_type1 = 16, size of tuple_type2 = 12
align of tuple_type1 = 8, align of tuple_type2 = 8

Continue reading

using namespace std

В кругах разработчиков C++ периодически возникает спор на тему “почему нельзя/нужно использовать using namespace std“. Наткнулся на довольно показательный пример того, почему этого делать ни в коем случае нельзя.
Товарищ пытается написать удаление пробелов из строки:

#include <iostream>

#include <string>

using namespace std;

int main(int argc, char* argv[]) {
    string str = "";
    cin >> str;
    remove(str.begin(), str.end(), ' ');
    cout << str;
    cin.ignore();
}

и получет довольно неожиданную ошибку компиляции.

'remove': function does not take 3 arguments (C2660)

А все почему? Да потому-что никогда, ни при каких условиях, не надо давать символам из пространства std глобальную видимость! Ведь в данном случае, можно было получить куда более понятное “error: ‘remove’ was not declared in this scope” просто убрав using namespace std.

А GCC предупреждал…

Есть некий код, приблизительно следующего содержания (упрощенный вариант):

TRACE((ERROR, "Bla-bla-bla. Can't create struct (%terr)", err));

Причем, как мы выяснили с большим трудом, переменная err, это не некий интовый код ошибки, а млять C++ класс! (луч поноса автору этого говна). G++ это дело находит, и, пишет предупреждение:

warning: cannot pass objects of non-POD type ‘struct cERROR’ through ‘...’; call will abort at runtime

Но это предупреждение остается незамеченным среди остальных over 9000 предупрежденией. Но G++ не просто намекает, он еще и генениует вот такой код:

0x01d4fe77 :    mov    DWORD PTR [ebp-0x20],eax
0x01d4fe7a :    jmp    0x1d4fe7e <_ZN8KLParams11DeserializeERP13cSerializablej+668>
0x01d4fe7c :    ud2a  
0x01d4fe7e :    mov    eax,DWORD PTR [ebp-0x20]

Где “ud2a” is the guaranteed illegal opcode on ia32. Пацан сказал – пацан сделал.

stringstream сливает :(

Не то чтобы я большой любитель C++, тем не менее всегда считал что форматирование через stringstream работает быстрее чем форматирование через sprintf. В очередной раз обсудив плюсы и минусы форматирования с коллегами, решили что проще “достать да померять”. Итог оказался довольно неожиданный для меня: stringstream позорно слил в лучшем случае в 2 раза, а в худшем в 3!
Continue reading

Инициализация статического объекта в GCC

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

template <typename T>
T& singleton()
{
    static T obj;
    return obj;
}

Если по каким-то причинам такое поведение кажется возмутительным, можно воспользоваться флагом -fno-threadsafe-statics

Кстати, кто знает как обстоят дела с инициализацией статических объектов у других компиляторов?

Легенда о константной ссылке.

Уже несколько раз в кругах C++ разработчиков слышал Легенду о константной ссылке. Легенда варьируется от “из функции можно вернуть константную ссылку на временный объект” до “можно сохранить константную ссылку на временный объект”. Как раз на прошлой неделе мы обсудили этот вопрос с коллегами, и мне захотелось в нем детально разобраться.
В стандарте данная ситуация освещена очень вскользь, так что пришлось покопаться. Во-первых, в пункте 12.2.5 сказано следующее:
Continue reading

Получаем стек вызовов в Mac OS X

Я давно хотел добавить поддержку получения стека вызовов к своему Tasks Explorer, но никакой публичной информации на этот счет мне не попадалось, а ковырять систему было некогда да и лень (зима, чтоб ее). С первыми лучиками весеннего солнца задача с получением стека вызов стала казаться не такой уж и скучной и дело сдвинулось.
Начал я с того, что нашел утилиту stackshot разработки Apple, только вот исходники приложения закрыты. Как вариант, stackshot можно было использовать напрямую, но в целом такая идея мне не понравилась, да и никаких новых и полезных знаний это не даст. Тем не менее, stackshot вполне сгодился в качестве отправной точки.
Continue reading

C++ man(3)

Давно хотел разжиться маном по плюсам ибо задрало лазить в инет за этой информацией.
Подходящий ман берется вот тут: ftp://ftp.gwdg.de//pub/misc/gcc/libstdc++/doxygen/
И копируется вот сюда: /usr/share/man/man3
Ну а если к Vim-у добавить вот этот плагин, жизнь становится еще прекраснее :)))