вторник, 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++ была бы еще более непредсказуемой и сложной задачей, чем даже сейчас. И, кстати, поведение компилятора в этом плане во втором случае тоже вызывает много вопросов.