понедельник, 6 февраля 2017 г.

Перенесу мой пост на хабре.

RAII и необрабатываемые исключения 

Наверняка все знают прописную (в книгах про С++) истину о чудесной методологии RAII, если нет — приведу краткое описание из википедии.

Это шаблонное описание этой техники
Получение ресурса есть инициализация (англ. Resource Acquisition Is Initialization (RAII)) — программная идиома объектно-ориентированного программирования, смысл которой заключается в том, что с помощью тех или иных программных механизмов получение некоторого ресурса неразрывно совмещается с инициализацией, а освобождение — с уничтожением объекта.

Типичным (хотя и не единственным) способом реализации является организация получения доступа к ресурсу в конструкторе, а освобождения — в деструкторе соответствующего класса. Поскольку деструктор автоматической переменной вызывается при выходе её из области видимости, то ресурс гарантированно освобождается при уничтожении переменной. Это справедливо и в ситуациях, в которых возникают исключения. Это делает RAII ключевой концепцией для написания безопасного при исключениях кода в языках программирования, где конструкторы и деструкторы автоматических объектов вызываются автоматически, прежде всего — в C++.

Последнее предложение вроде как обещает 100% гарантию результата, но как всегда в жизни, а особенно в С++, есть ньюанс. 

Пример кода использующего RAII:

Допустим, есть какой-то класс, инкапсулирующий доступ к сети:

class Network{
public:
    Network(const URL &url) : m_url(url)
    {
    }

private:
    Url m_url;

};

Создаём класс, который будет реализовывать RAII:

class LockNet{
public:
    LockNet(const Url &url){
        m_net = new Network(url);
    }
    ~LockNet (){
        delete m_net;
    }

    operator Network * (){
        return network;
    }

private:

    Network *m_net;
};

Теперь в функции main мы можем безопасно использовать этот ресурс:

int main(int argc, char *argv[])
{
    LockNet net("http://habrahabr.ru")
    //здесь какие-то другие функции, 
    //которые могут генерировать исключения
    return 0; 
}

Вроде бы всё нормально, как обещает RAII, даже если будет сгенерировано исключение, указатель m_net в классе LockNet будет корректно удалён. Правильно?

Увы, нет.

Почему-то в описании RAII обычно забывают написать, что для работы этой техники исключение ОБЯЗАНО быть перехвачено обработчиком исключений этого типа, иначе, если обработчик не будет найден, будет вызвана std::terminate(), которая аварийно завершит выполнение программы. Страуструп описывает это в книге «Язык программирования С++ (03)», глава 14.7.

Удаление локальных объектов зависит от реализации, где-то они будут удалены, где-то наоборот, чтобы разработчик мог увидеть состояние локальных объектов на момент исключения, в дебагере когда загрузит coredump. И рекомендует если вам нужно гарантированное удаление локальных объектов оборачивать код в функции main блоком try — catch (...), который перехватывает любые исключения.

Т.ч. в коде функции main, если будет исключение до оператора return 0;, мы получаем обычную утечку ресурсов.
Она не фатальная, так как ОС сохранит coredump и освободит ресурсы, занятые программой.

Как в этом убедиться? Пишем проверочный код!

В данном коде мы используем умные указатели, которые реализовывают технику RAII:

#include 
#include 
#include 
#include 
#include 

using namespace std;

class MyExc
{

};

class Slot
{
public:
    Slot(const std::string &str = "NONAME") : m_name(str)
    {
        cout << "Constructor of slot: " << m_name << endl;
    }

    virtual ~Slot()
    {
        cout << "Destructor of slot: " << m_name << endl;
    }

    void sayName()
    {
        cout << "Slot name is: " << m_name << endl;
       throw MyExc();
    }

private:
    string m_name;
};


void testShared(shared_ptr & m_share)
{
    m_share->sayName();
   
}

int main()
{
    vector<shared_ptr> vec {make_shared("0"), make_shared("1"), make_shared("2"), make_shared("3"), make_shared("4")};

    for (auto& x:vec)
            testShared(x);



    return 0;
}

Скомпилировав и запустив эту программу, получаем вывод:

Constructor of slot: 0
terminate called after throwing an instance of 'MyExc'
Constructor of slot: 1
Constructor of slot: 2
Constructor of slot: 3
Constructor of slot: 4
Slot name is: 0

Переписываем функцию main, заворачивая вызов функции генерирующей исключение в try — catch блок:

int main()
{
    try {
        vector> vec{
            make_shared("0"),
            make_shared("1"),
            make_shared("2"),
            make_shared("3"),
            make_shared("4")
        };

        for (auto &x:vec)
            testShared(x);

    } catch (std::exception & ex){

        cout<
    } catch (...){

        cout<<"Unexpected exception"<    }
}
  


И, вуаля — всё начинает работать как и должно. 

Вызываются не только конструкторы, но и деструкторы объектов, хранящихся в умных указателях.

Вывод программы:

Constructor of slot: 0
Constructor of slot: 1
Constructor of slot: 2
Constructor of slot: 3
Constructor of slot: 4
Slot name is: 0
Unexpected exception
Destructor of slot: 0
Destructor of slot: 1
Destructor of slot: 2
Destructor of slot: 3
Destructor of slot: 4


Тем не менее, всё согласно стандарту:

15.2 Constructors and destructors

1. As control passes from a throw-expression to a handler, destructors are invoked for all automatic objects
constructed since the try block was entered. The automatic objects are destroyed in the reverse order of the
completion of their construction.

3. The process of calling destructors for automatic objects constructed on the path from a try block to a
throw-expression is called “stack unwinding.” If a destructor called during stack unwinding exits with an
exception, std::terminate is called (15.5.1).

15.3 Handling an exception

9 If no matching handler is found, the function std::terminate() is called; whether or not the stack is
unwound before this call to std::terminate() is implementation-defined (15.5.1).


Проверялось на g++ (Ubuntu 4.9.2-0ubuntu1~14.04) 4.9.2, Visual Studio 2013 CE.

Ссылки


RAII
ISO C++

вторник, 1 ноября 2011 г.

Ubuntu 11.10 vs Intel 5150 wifi

Установил Ububntu 11.10 на нетбук Acer Aspire One 531. После установки не заработала эта wifi карта.
NetworkManager в своём меню выводил: "управление беспроводными адаптерами отключено".

После добавления строки
blacklist acer-wmi в файл /etc/modprobe.d/blacklist.conf

и последующей перезагрузки wifi чудесным образом оживает.

суббота, 20 ноября 2010 г.

Build Gtkmm application in Eclipse CDT

После того, как вы установили пакет gtkmm-x.x-dev соответствующей версии, может возникнуть желание использовать удобный инструмент для разработки, к примеру весьма популярный Eclipse  с плагином CDT - для разработки С++ программ.
итак создаём стандартный ппроект С++, и пигем тестовую заготовку, но сразу видим, что Eclipse не видит заголовочных файлов библиотеки.
Main window

















Добавляем каталог с заголовочными файлами в свойствах проекта, для нужной конфигурации (Debug\Release).

Include settings


















Однако попытка собрать проект, успехом не увенчалась, так как нужно сообщить компилятору и линковщику о нахождении библиотек и всех заголовочных  файлов gtkmm
Build error

сообщяем компилятору, будьте внимательны с указанием правильной версии, которую вы установили или хотите использовать, в строке:
`pkg-config gtkmm-2.4 --cflags --libs`

compiler settings

сообщяем линковщику, будьте внимательны с указанием правильной версии, которую вы установили или хотите использовать, в строке:
`pkg-config gtkmm-2.4 --cflags --libs`
Linker settings

















Компилируем


Compiling













Запускаем

Runing

воскресенье, 12 сентября 2010 г.

Mac OS X in VirtualBox

Запустил Retail OS X в VirtualBox по этому рецепту


  1. Запускаем virtualbox и создаем виртуальную машину с именем «MacOS», указываем в качестве гостевой операционной системы Mac OS X Server, остальное по умолчанию.
  2. Открываем настройки созданной виртуальной машины. В настройках приводов, если присутствует SATA, меняем его на IDE (ICH6). Там же для привода компакт-дисков устанавливаем галочку «passthrough» — без этого виртуальная машина под windows не сможет загрузится с установочного компакт диска MacOS X.
  3. Закрываем virtualbox, запускаем консоль, делаем cd в папку с virtualbox и выполняем следующую команду:
    VBoxManage setextradata "MacOS" "VBoxInternal2/SmcDeviceKey" "ourhardworkbythesewordsguardedpleasedontsteal(с)AppleComputerInc"
    Где «MacOS» — имя созданной виртуальной машины.  .
  4. Запускаем virtualbox, стартуем виртуальную машину, в появившемся окне визарда выбираем что cd-приводом будет физический привод, вставляем в него установочный диск с MacOS X и, если все сделано правильно, то virtualbox грузится с диска и начинается установка. Если же вместо этого показывается желто-черное EFI Menu — то с диска загрузиться не получилось. Либо процессор не тот, либо не сделано что-то из вышеописанного.

воскресенье, 16 мая 2010 г.

Skype && Ubuntu 10.10 x86-64

Переустанавливал пакеты, и случаем удалились ia32-libs, как не имеющие зависимости, после чего skype, установленный в версии с динамической линковкой, скачанный с сайта Skype.com, перестал запускаться. Просто не запускаясь и ничего не выводя в консоль.
После установки этого пакета (ia32-libs) всё заработало нормально.

четверг, 25 марта 2010 г.

Git config

При конфигурации git,  настоятельно рекомендуется указать информацию о себе, для того чтобы отследить свои коммиты, и не вносить сумятицу в репозиторий.
Можно использовать комманду git config  в текущем репозитории.

Если же вы хотите прописать настройки глобально, чтобы они были общими для всех ваших репозиториев, или как-то настроить внешний вид, который будет одинаковым для всех - есть опция git config --global.
Я делаю так:

git config --global user.name "My name"
git config --global user.email "my@email.com"

Помимо этого я  прошу git отобрать состояния репозитория разными цветами, следующими  коммандами:

git config --global color.branch "auto"
git config --global color.status "auto"
git config --global color.diff "auto"

После чего изменения и новые файлы будут выделены цветом.
Примерно вот так:

воскресенье, 21 февраля 2010 г.

CNTLM - NTLM proxy для Linux

После установки NTLMAPS (написана на питоне) решил поискать аналоги из мира бинарного кода. Коллеги напомнили про программу зовущуюся CNTLM. (plain C)
Авторы программи приводят доводы о том, что программа более быстрая чем NTLMAPS и намного более производительная...
Останавливаем NTLMAPS
$sudo service ntlmaps stop
Удаляем NTLMAPS
$sudo aptitude remove ntlmaps
Устанавливаем конкурента
$sudo aptitude install сntlm
Настройка производится в конфигурационном файле
$sudo kate /etc/cntlm.conf
В котором указываем:
Имя пользователя
Домен
Пароль
Порт, который слушает программа

Примерно вот так:


После чего перезапускаем сервис
$sudo service cntlm restart

Пользуемся...
правда через неё Akregator не работает... Ох придётся взять как-нибудь сорцы в руки и диалог настройки прокси добавить...
Зато работает SVN, который через ntlmaps не работает.