вторник, 27 марта 2012 г.

char to TCHAR

Очень популярная задача при использовании WinAPI и предлагается куча решений. Но большинство из них почему-то сходятся к выделению массива TCHAR в куче с помощью new либо использованию массива фиксированной длины. Мне больше всего нравится такое решение:

            //исходная строка
            const std::string dev_name = "COM4";
            //создаем вектор нужной длины с учетом терминального нуля в конце
            std::vector<TCHAR> tchar_dev_name(dev_name.size() + 1);
            //важно! используем std::copy(), а не memcpy()
            std::copy(dev_name.begin(), dev_name.end(), tchar_dev_name.begin());
            //нулевой символ в конце обязателен 
            tchar_dev_name[tchar_dev_name.size() - 1] = 0;
             
            //дальше все как обычно
            m_port = CreateFile(
                    &tchar_dev_name[0],
                    GENERIC_READ | GENERIC_WRITE, 
                    0, 
                    NULL, 
                    OPEN_EXISTING, 
                    0, 
                    NULL 
                    ); 

Не надо выделять память руками и освобождать ее тоже не надо - за нас все делает STL. Уже на пару строчек меньше. И надежнее. Почему используем std::copy() вместо memcpy()? Потому что TCHAR, вообще говоря, это не тип, а макрос, который определяет тип (char или wchar_t) в зависимости от. То есть, в общем случае нельзя утверждать, что sizeof(char)==sizeof(wchar_t), а значит и memcpy() нам тоже не подходит и вместо него мы используем функцию std::copy(), которая копирует символы из одной строки в другую поштучно.

четверг, 22 марта 2012 г.

Исключение надо всегда перехватывать по ссылке!

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

#include <exception>
#include <string>
#include <iostream>
class MyException : public std::exception {
public:
    MyException(const std::string & msg)
        : std::exception(), m_msg(msg)
    {
    }
    const char * what() const throw()
    {
        return m_msg.c_str();
    }
    ~MyException() throw()
    {
    }
private:
    std::string m_msg;
};
 
 
int main()
{
    try
    {
        throw MyException("MyException");
    }
    catch(std::exception e) //вот тут происходит т.н. "расщепление"
    {
        std::cout << e.what();
    }
    return 0;
}


Этот код выведет "std::exception". Это происходит потому, что в строке с инструкцией catch(std::exception e) происходит преобразование к значению базового типа (т.н. "срезка" или "расщепление"), в котором нет строки-члена  m_msg, который хранит сообщение об ошибке. То есть, до блока обработки долетает совсем не то, что было сгенерировано внутри try. И выводится именно то, что выводится в std::exception::what(), а не MyException::what(). Чтобы получать правильный тип исключения, надо получать значение по ссылке. То есть заменить "catch(std::exception e)" на "catch(std::exception & e)". Тогда не происходит преобразования к значению класса базового типа и корректно работает механизм полиморфизма. Как с указателями. И в результате мы получаем именно тот объект-исключение, который был сгенерирован в блоке try.
На самом деле не вижу ни одной причины, чтобы не использовать перехват исключений по ссылке всегда. Перехват по значению может привести к описанным выше проблемам, а перехват по указателю обычно ведет к необходимости удалять объект исключения после его  обработки. А это лишнее действие, которое можно и забыть сделать. Поэтому проще всего перехватывать исключения по ссылке и не забивать себе голову лишними действиями. Ну а генерировать исключения надо, в таком случае, всегда по значению.

пятница, 2 марта 2012 г.

Любоыптное поведение константной ссылки в C++

Очень любопытно поведение константных ссылок в C++. Потому что константная ссылка - это не просто указатель. Если присвоить ей значение временного объекта, то он будет существовать столько же, сколько и константная ссылка. Это, как ни странно, записано в стандарте. О чем я прочитал в блоге алены-цпп. Но это еще не все:

class SomeClass {
public:
    SomeClass(const std::string & name)  
      : obj_name(name)  
    { 
       std::cout << "object \"" << obj_name.c_str() << "\" created" << std::endl; 
    }
    ~SomeClass()  
    { 
       std::cout << "object \"" << obj_name.c_str() << "\" destroyed" << std::endl;  
    }
private:
    std::string obj_name;
};
SomeClass get_some_class_heap() {
    return *(new SomeClass("HeapObjectFromFunc"));
}
SomeClass get_some_class_val() {
    return SomeClass("ValueObjFromFunc");
}
void test_const_ref_func() {     
    const SomeClass & value_obj_from_func_ref(get_some_class_val()); // 1    
    const SomeClass & heap_obj_from_func_ref(get_some_class_heap()); // 2    
    const SomeClass & heap_obj_ref(*(new SomeClass("HeapObj"))); // 3   
    // 
    std::cout << "end of const_ref_func()" << std::endl;
}
int main(int argc, char *argv[])
{  
 
    test_const_ref_func();
 
}

Получаем вот такой вот вывод:

object "HeapObjectFromFunc" created
object "ValueObjFromFunc" created
object "HeapObj" created
end of const_ref_func()
object "ValueObjFromFunc" destroyed
object "HeapObjectFromFunc" destroyed

 
Первый случай - все понятно: константная ссылка получает временный value-объект и "держит" его, не давая ему умереть, пока сама не выйдет за пределы области видимости функции test_const_ref_func().
Второй случай - то же самое, но возвращается ссылка не на value-объект, а на объект, созданный в куче. Казалось бы - вот они, умные указатели! Но... см.третий случай.
Третий случай - умный указатель получает ссылку на тут же динамически созданный объект. Разумеется, он не удаляет объект после того, как выходит за пределы видимости. Если бы было иначе - отладка программ на C++ была бы еще более непредсказуемой и сложной задачей, чем даже сейчас. И, кстати, поведение компилятора в этом плане во втором случае тоже вызывает много вопросов.
 
 

среда, 29 февраля 2012 г.

Бьярн страуструп про необходимость сборщика мусора в C++

Прочитал статью на хабре про недавнюю конференцию по C++11 - новому стандарту языка C++. Бьярн Страуструп очень тонко прошелся по всем языкам с автоматическим управлением памятью:
“But I think that Garbage Collection is not as critical for C++ as it is for many of the other languages. We just don’t generate that much garbage”

Что в вольном изложении можно перевести  так: "Мы не так много срем, чтобы нам понадобился сборщик мусора".

воскресенье, 15 января 2012 г.

Компилирование библиотек boost

У меня есть QtCtreator под Windows и необходимо скомпилировать библиотеки буста. Сначала скачиваем последнюю версию исходников буста. На текущий момент это версия 1.48.
1) Распаковываем архив (я распаковал его на диск C:\ ).
2) Создаем и устанавливаем следующие переменные окружения:
      BOOST_ROOT="C:\boost_1_48_0"
      BOOST_BUILD_PATH="%BOOST_ROOT%\tools\build\v2\engine"
3) Компилируем утилиту bjam.exe, которая потом поможет нам скомпилировать сам буст. Для этого в командной строке меняем текущий каталог на "C:\boost_1_48_0\tools\build\v2\engine", после чего компилируем bjam, вызывая из командной строки скрипт "build.bat" с параметром "mingw":
      >  build.bat  mingw
Варианты параметров запуска командного фала для других компиляторов можно найти в самом командном файле в комментариях: "Toolsets supported by this script are: borland, como, gcc, gcc-nocygwin,  intel-win32, metrowerks, mingw, msvc, vc7, vc8, vc9, vc10".
Итак, теперь в результате у нас есть папка "C:\boost_1_48_0\tools\build\v2\engine\bin.ntx86", а в ней два файла:  "b2.exe" и "bjam.exe". Скопируем их в "C:\Windows\System32". Теперь их можно вызывать из командной строки.
4) Собственно компиляция библиотек boost. Для этого в командной строке переходим в в папку "C:\boost_1_48_0", после чего выполняем команду компиляции библиотек буста:
    >  bjam --toolset=gcc link=static stage

Вот и все.

Ссылки по теме:
Mismatched versions of Boost.Build engine and core
Building Boost for static linking (MinGW)
Сборка библиотеки Boost

четверг, 5 января 2012 г.

Подсчет количества ненулевых бит с помощью "магических чисел"

Подсчёт количества ненулевых бит в числе v за log2(v) проходов (на примере 8-битного числа):
    v = (v & 0x55) + ((v >> 1) & 0x55);
    v = (v & 0x33) + ((v >> 2) & 0x33);
    return (v & 0x0f) + ((v >> 4) & 0x0f);


Число разбивается на группы бит одной длины (сперва по одному биту). Затем значения соседних пар одновременно складываются и сохраняются в новых группах в два раза большей длины до тех пор, пока не будут сложены половинки числа.
Поскольку длина суммы двух чисел равна длине большего числа с битом для переноса, поэтому для каждой группы в маске групп достаточно иметь единиц не больше номера шага (то есть 1+log2 от длины группы). Hапример, для подсчёта единичных бит в 32-битном числе (с оптимизацией):


#define g21 0x55555555ul // = 0101_0101_0101_0101_0101_0101_0101_0101
#define g22 0x33333333ul // = 0011_0011_0011_0011_0011_0011_0011_0011
#define g23 0x0f0f0f0ful // = 0000_1111_0000_1111_0000_1111_0000_1111



v = (v & g21) + ((v >> 1) & g21);
v = (v & g22) + ((v >> 2) & g22);
v = (v + (v >> 4)) & g23;
return (v + (v >> 8) + (v >> 16) + (v >> 24)) & 0x3f;



Для сокращения количества шагов можно суммировать тройки и т.д.:

#define g31 0x49249249ul // = 0100_1001_0010_0100_1001_0010_0100_1001
#define g32 0x381c0e07ul // = 0011_1000_0001_1100_0000_1110_0000_0111



v = (v & g31) + ((v >> 1) & g31) + ((v >> 2) & g31);
v = ((v + (v >> 3)) & g32) + ((v >> 6) & g32);
return (v + (v >> 9) + (v >> 18) + (v >> 27)) & 0x3f;




Оригинальный пост (датирован 2003-м годом)  находится здесь. А у себя я его продублировал на тот случай, если вдруг что-то случится с этой веткой форума.

Крайне полезные статьи

Приведу список отличных статей, которые очень сильно помогли мне:

Перевод статьи про d-указатели (aka pimpl, чеширский кот и т.п.). Рассказано, что такое д-указатели, зачем они нужны, как их использование помогает соблюдению бинарной совместимости, как увеличивается производительность при перемещении pimpl-объектов и многое другое. Попутно рассказывается, что такое бинарная совместимость вообще, зачем она нужна и для чего в программах на C++ крайне важно в целях бинарной совместимости все детали реализации писать в cpp-файлах, оставляя в h-файлах только самый необходимый минимум объявлений.
Использование dll в Visual C++. Рассказывается, как подключать dll-библиотеки к программе. Рассматривается три способа использования библиотек: явное подключение, неявное подключение (с использованием lib-файлов) и отложенное подключение библиотек. Описывается, что такое lib-файл и для чего он нужен. Описываются особенности использования библиотечных классов, переменных и функций.
Threading without headache. Описывается, как в Qt писать многопоточные приложения без использования таких базовых примитивов, как, например, мьютексы. Если кто не читал - обязательно прочитать. Я был так впечатлен, что тут же за пару дней переделал свой проект, полностью убрав из него мьютексы. Все вызовы методов интерфейсов заменил механизмом сигналов и слотов. Можно сказать, что я вообще абстрагировался от того факта, что приложение многопоточное- за меня все делает великолепный фреймворк Qt. Описывается, зачем нужна функция moveToThread(). Также советую почитать дополнительно про отправку/прием событий QEvent - иногда вместо механизма сигналов и слотов может быть удобнее использовать сообщения. Еще советую почитать это
Виртуальное наследование. В моей практике еще не попадался случай, когда в этом бы возникла необходимость. Вообще довольно сложно представить такой случай, если честно. Но на собеседованиях этот вопрос почти всегда задают, так что читать в обязательном порядке.
Подсчет количества битов в числе. Тут описан наиболее шаманский способ "магических чисел". Остальные способы (последовательный сдвиг и предварительный подсчет) слишком тривиальны, чтобы давать по ним ссылки.
Статья про boost::bind. Большая и полезная для понимания внутреннего устройства буста статья.