finally
От: Karamat Беларусь  
Дата: 03.12.15 16:47
Оценка: -1 :))
Задумался этим вечером, как страуструп и ко предлагают разруливать такие ситуации:

void MyFun()
{
    std::vector<MyClass1*> v1;
    std::list<MyClass2*> v2;
    ...
    std::map<MyClassN*, MyClassN1*> vN;
    
    //дальше идет тяжелая работа с коллекциями, где создаются и добавляются в эти коллекции экземпляры
    //MyClass1..MyClassN, при этом в любой момент может выскочить исключение
}


при генерации исключения надо прибить все объекты, которые накопились в коллекциях

есть ли возможность это сделать без десятков строк идиотского кода ?
Re: finally
От: _niko_ Россия  
Дата: 03.12.15 16:53
Оценка:
Здравствуйте, Karamat, Вы писали:

K>при генерации исключения надо прибить все объекты, которые накопились в коллекциях


K>есть ли возможность это сделать без десятков строк идиотского кода ?



умные указатели?
Отредактировано 10.12.2015 1:06 kaa.python . Предыдущая версия . Еще …
Отредактировано 03.12.2015 16:54 _niko_ . Предыдущая версия .
Re: finally
От: _hum_ Беларусь  
Дата: 03.12.15 16:55
Оценка: +1
Здравствуйте, Karamat, Вы писали:

K>при генерации исключения надо прибить все объекты, которые накопились в коллекциях


K>есть ли возможность это сделать без десятков строк идиотского кода ?



std::vector<std::unique_ptr<MyClass1> >

?
Отредактировано 10.12.2015 1:06 kaa.python . Предыдущая версия .
Re: finally
От: watchmaker  
Дата: 03.12.15 16:56
Оценка:
Здравствуйте, Karamat, Вы писали:

K>при генерации исключения надо прибить все объекты, которые накопились в коллекциях

K>есть ли возможность это сделать без десятков строк идиотского кода ?
Использовать умные указатели.
Re[2]: finally
От: watchyourinfo Аргентина  
Дата: 03.12.15 17:54
Оценка:
__>
__>std::vector<std::unique_ptr<MyClass1> >
__>

__>?

да, но нужно понимать, что это было невозможно до появления emplace_back и move семантики в стандарте
Re[3]: finally
От: DarkEld3r  
Дата: 03.12.15 18:12
Оценка: +1
Здравствуйте, watchyourinfo, Вы писали:

W>да, но нужно понимать, что это было невозможно до появления emplace_back и move семантики в стандарте

До этого были всякие boost::ptr_vector.
Re[2]: finally
От: Karamat Беларусь  
Дата: 03.12.15 19:26
Оценка: -1 :)
Здравствуйте, _hum_, Вы писали:

K>>при генерации исключения надо прибить все объекты, которые накопились в коллекциях


K>>есть ли возможность это сделать без десятков строк идиотского кода ?



__>
__>std::vector<std::unique_ptr<MyClass1> >
__>

__>?

в функции используется АПИ, требующее ссылок типа std::vector<MyClass1*>
Re: finally
От: T4r4sB Россия  
Дата: 03.12.15 19:49
Оценка:
Здравствуйте, Karamat, Вы писали:

K>при генерации исключения надо прибить все объекты, которые накопились в коллекциях

K>есть ли возможность это сделать без десятков строк идиотского кода ?

Да, воспользуйся мощью автодеструкторов. Которая уже реализована в умных указателях.
Re[3]: finally
От: _hum_ Беларусь  
Дата: 03.12.15 21:18
Оценка: +1
Здравствуйте, Karamat, Вы писали:

K>Здравствуйте, _hum_, Вы писали:


K>>>при генерации исключения надо прибить все объекты, которые накопились в коллекциях


K>>>есть ли возможность это сделать без десятков строк идиотского кода ?



__>>
__>>std::vector<std::unique_ptr<MyClass1> >
__>>

__>>?

K>в функции используется АПИ, требующее ссылок типа std::vector<MyClass1*>



тогда, может, так попробовать:
template<typename value_type>
    class vector_of_pointed_values: public std::vector<value_type*>
    {
    public:
        //---
        virtual
            ~vector_of_pointed_values(void)
        {
            //<код удаления>
        }
        //---
    };
Re[3]: finally
От: _niko_ Россия  
Дата: 03.12.15 22:02
Оценка:
Здравствуйте, Karamat, Вы писали:

K>Здравствуйте, _hum_, Вы писали:


K>>>при генерации исключения надо прибить все объекты, которые накопились в коллекциях


K>>>есть ли возможность это сделать без десятков строк идиотского кода ?



__>>
__>>std::vector<std::unique_ptr<MyClass1> >
__>>

__>>?

K>в функции используется АПИ, требующее ссылок типа std::vector<MyClass1*>



ниже приведенный код на ideone
  Скрытый текст
#include <list>
#include <memory>
#include <iostream>
#include <exception>


struct MyClass
{
    static int last_number;
    MyClass(bool hasBreak)
        : number(++last_number)
        , hasBreak(hasBreak)
    {
        std::cout << "MyClass(" << number << ", " << hasBreak << ")" << std::endl;
    }
    ~MyClass()
    {
        std::cout << "~MyClass(" << number << ", " << hasBreak << ")" << std::endl;
    }
    int number;
    bool hasBreak;
};

 int MyClass::last_number = 0;


std::list<std::unique_ptr<MyClass>> create_smart_ptr()
{
    std::list<std::unique_ptr<MyClass>> result;
    result.emplace_back(new MyClass(false));
    result.emplace_back(new MyClass(false));
    result.emplace_back(new MyClass(false));
    result.emplace_back(new MyClass(false));
    result.emplace_back(new MyClass(true));
    result.emplace_back(new MyClass(false));
    result.emplace_back(new MyClass(false));

    return result;
}

std::list<MyClass*> build_no_smart_ptr(std::list<std::unique_ptr<MyClass>>& list)
{
    std::list<MyClass*> result;

    for (std::unique_ptr<MyClass>& item : list)
    {
        result.push_back(item.get());
    }

    return result;
}

void work(std::list<MyClass*>& list)
{
    for (MyClass* item : list)
    {
        if (item->hasBreak)
        {
            std::cout << "break" << std::endl;
            throw std::runtime_error("failed");
        }
    }
}

int main()
try
{
    std::cout << std::boolalpha;

    std::list<std::unique_ptr<MyClass>> guard_ptrs(create_smart_ptr());

    std::list<MyClass*> no_guard_ptrs(build_no_smart_ptr(guard_ptrs));

    work(no_guard_ptrs);

    return 0;
}
catch (const std::exception& except)
{
    std::cout << except.what();
    return 1;
}
Re[4]: finally
От: Evgeny.Panasyuk Россия  
Дата: 03.12.15 22:08
Оценка:
Здравствуйте, DarkEld3r, Вы писали:

W>>да, но нужно понимать, что это было невозможно до появления emplace_back и move семантики в стандарте

DE>До этого были всякие boost::ptr_vector.

И boost::container::vector
Re[4]: finally
От: Karamat Беларусь  
Дата: 04.12.15 07:39
Оценка:
Здравствуйте, _hum_, Вы писали:


K>>в функции используется АПИ, требующее ссылок типа std::vector<MyClass1*>



__>тогда, может, так попробовать:

__>
__>template<typename value_type>
__>    class vector_of_pointed_values: public std::vector<value_type*>
__>    {
__>    public:
__>        //---
__>        virtual
__>            ~vector_of_pointed_values(void)
__>        {
__>            //<код удаления>
__>        }
__>        //---
__>    };

__>


Код удаления не просто delete p, перед этим надо еще что-то сделать, для каждого типа свое, в зависимости от значений локальных переменных:

int a1, aN;
//...
MyClass1* p1;
//...
if(a1 == 0)
    p1->send1();
else
    p1->send2();

delete p1;
//...
MyClassN* pN;
//...
if(aN == 1000)
    pN->send3();
else
    pN->send5();

delete pN;
Re: finally
От: so5team https://stiffstream.com
Дата: 04.12.15 08:32
Оценка:
Здравствуйте, Karamat, Вы писали:

K>есть ли возможность это сделать без десятков строк идиотского кода ?


В современном C++ можно написать свой собственный аналог finally. Например, один из вариантов. И пример использования:
void launch_work_threads()
    {
        work_thread_t * started_threads[ total_priorities_count ];
        fill( begin(started_threads), end(started_threads), nullptr );

        do_with_rollback_on_exception( [&] {
            for( std::size_t i = 0; i != total_priorities_count; ++i )
                {
                    m_agents_per_priority[ i ].store( 0, std::memory_order_release );

                    m_threads[ i ]->start();

                    started_threads[ i ] = m_threads[ i ].get();
                }
            },
            [&] {
                invoke_noexcept_code( [&] {
                    for( auto t : started_threads )
                        if( t )
                            {
                                t->shutdown();
                                t->wait();
                            }
                        else
                            break;
                } );
            } );
    }
Re[4]: finally
От: Кодт Россия  
Дата: 04.12.15 13:34
Оценка:
Здравствуйте, _hum_, Вы писали:

__>тогда, может, так попробовать:

__> class vector_of_pointed_values: public std::vector<value_type*>

Наследоваться от стандартных контейнеров — моветон.
Лучше уж тогда умные указатели на контейнеры:

http://ideone.com/vSlz1i

template<class T, class D>
auto make_unique_ptr(T* p, D d) { return std::unique_ptr<T,D>(p,d); }

.....

std::vector<X*> v;
std::list<Y*> l;
std::map<Z*,T*> m;
 
auto vcleaner = make_unique_ptr(&v,
    [](auto pv) {
        for(auto item : *pv) delete item;
    }
);
 
auto lcleaner = make_unique_ptr(&l,
    [](auto pv) {
        for(auto item : *pv) delete item;
    }
);
 
auto mcleaner = make_unique_ptr(&m,
    [](auto pm) {
        for(const auto& kv : *pm) { // pair<T* const, U*> kv
            delete kv.first;
            delete kv.second;
        }
    }
);

.....

// отпускаем, не удаляя
lcleaner.release();
vcleaner.release();
mcleaner.release();
return;
Перекуём баги на фичи!
Re[5]: finally
От: jazzer Россия Skype: enerjazzer
Дата: 04.12.15 14:21
Оценка: +1
Здравствуйте, Karamat, Вы писали:

K>Код удаления не просто delete p, перед этим надо еще что-то сделать, для каждого типа свое, в зависимости от значений локальных переменных:


Мне одному кажется, что ты на ходу досочиняешь требования?
Можно сразу весь список огласить?
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[5]: finally
От: _hum_ Беларусь  
Дата: 04.12.15 14:45
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, _hum_, Вы писали:


__>>тогда, может, так попробовать:

__>> class vector_of_pointed_values: public std::vector<value_type*>

К>Наследоваться от стандартных контейнеров — моветон.

К>Лучше уж тогда умные указатели на контейнеры:

К>http://ideone.com/vSlz1i


К>
К>template<class T, class D>
К>auto make_unique_ptr(T* p, D d) { return std::unique_ptr<T,D>(p,d); }

К>.....

К>std::vector<X*> v;
К>std::list<Y*> l;
К>std::map<Z*,T*> m;
 
К>auto vcleaner = make_unique_ptr(&v,
К>    [](auto pv) {
К>        for(auto item : *pv) delete item;
К>    }
К>);
 
К>auto lcleaner = make_unique_ptr(&l,
К>    [](auto pv) {
К>        for(auto item : *pv) delete item;
К>    }
К>);
 
К>auto mcleaner = make_unique_ptr(&m,
К>    [](auto pm) {
К>        for(const auto& kv : *pm) { // pair<T* const, U*> kv
К>            delete kv.first;
К>            delete kv.second;
К>        }
К>    }
К>);

К>.....

К>// отпускаем, не удаляя
К>lcleaner.release();
К>vcleaner.release();
К>mcleaner.release();
К>return;
К>


ну, а если кто-то сделает два клинера одного объекта, или не отпустит, или отпустит раньше времени и т.п.
все-таки ооп в первую очередь и задумывалось для того, чтобы не разбрасывать по коду объект и действия над ним, дабы максимально уменьшить вероятность подобных ошибок.
Re[6]: finally
От: Кодт Россия  
Дата: 04.12.15 15:38
Оценка:
Здравствуйте, _hum_, Вы писали:

__>ну, а если кто-то сделает два клинера одного объекта, или не отпустит, или отпустит раньше времени и т.п.


или забудет написать finally, или возьмёт невладеющий контейнер вместо владеющего, и т.п.

__>все-таки ооп в первую очередь и задумывалось для того, чтобы не разбрасывать по коду объект и действия над ним, дабы максимально уменьшить вероятность подобных ошибок.


Это не вполне вопрос ООП.
Напротив, здесь больше пахнет ФП, т.к. по месту выполняется слияние трёх задач
— хранение коллекции
— управление временем жизни элементов
— реакция на коммит/ролбек

Если сценарий "хранить и убивать элементы при смерти контейнера" не одноразовый, — то нужны соответсвующие контейнеры. Тот же boost::ptr_vector.
И желательно протащить их в интерфейс, чтоб принимающая сторона тоже работала с ними.
А если именно по месту, то — либо писать финализатор (заделывать прореху синтаксиса С++), либо RAII-обёртку.

А в рамках ООП — наследоваться от стандартных контейнеров — моветон.
Потому что они не заточены на полиморфизм времени исполнения. И писать "virtual ~my_derived_vector()" — это обманывать сам себя.
Но если не отдавать такие контейнеры на сторону (и гарантировать это!) то можно сплавить хранение и удаление в один флакон через наследование. Но лучше (имхо) всё же через обёртку.


Кстати, коммит (лучше) делать явно, а не как противодействие ролбеку:
void foo(vector<X*>& resultX, list<Y*> resultY) {
  my_vector_scope_guard gX(&resultX);
  my_list_scope_guard   gY(&resultY);
  ....
  resultX.push_back(new X);
  resultY.push_back(new Y);
  ....
  gX.release(); // отказываемся от ролбека
  gY.release();
}

void bar(vector<X*>& result, list<Y*> resultY) {
  my_owning_vector<X*> tmpX;
  my_owning_list<Y*>   tmpY;
  ....
  tmpX.push_back(new X);
  tmpY.push_back(new Y);
  ....
  tmpX.swap(resultX); // коммит
  tmpY.swap(resultY);
}
Перекуём баги на фичи!
Re[6]: finally
От: Karamat Беларусь  
Дата: 04.12.15 17:20
Оценка:
Здравствуйте, jazzer, Вы писали:

K>>Код удаления не просто delete p, перед этим надо еще что-то сделать, для каждого типа свое, в зависимости от значений локальных переменных:


J>Мне одному кажется, что ты на ходу досочиняешь требования?


В данном конкретном случае досочинил про локальные переменные, просто потому, что в перспективе могут быть нужны и они.

J>Можно сразу весь список огласить?


Вроде все — есть несколько коллекций разных типов, каждый элемент которых нужно в конце функции обработать, вне зависимости от того, вылетело ли исключение или нет.

Пока я вижу два пути

1. от товарища _hum_ — сделать для каждой коллекции класс наследник, в деструктор засунуть обработку. 10 коллекций — надо 10 классов сделать вместо слова finally и двух скобок, а если нужны локальные переменные то решение вообще не работает.

2. от so5team — через лямбды, оно похоже на правду, но уж больно дико выглядит с непривычки к 11 стандарту, боюсь неожиданных спецэффектов.
Re: finally
От: PM  
Дата: 04.12.15 20:26
Оценка:
Здравствуйте, Karamat, Вы писали:

K>Задумался этим вечером, как страуструп и ко предлагают разруливать такие ситуации:


K>
K>void MyFun()
K>{
K>    std::vector<MyClass1*> v1;
K>    std::list<MyClass2*> v2;
K>    ...
K>    std::map<MyClassN*, MyClassN1*> vN;
    
K>    //дальше идет тяжелая работа с коллекциями, где создаются и добавляются в эти коллекции экземпляры
K>    //MyClass1..MyClassN, при этом в любой момент может выскочить исключение
K>}
K>


K>при генерации исключения надо прибить все объекты, которые накопились в коллекциях


K>есть ли возможность это сделать без десятков строк идиотского кода ?


По приведённому примеру кода не видно, и явно не указано требование, чтобы объекты в коллекциях были полиморфными. C++11 поддерживает семантику перемещения, так что добавление и удаление элементов в контейнере может быть и не таким тяжелым сценарием, как это представляется в теории. А профайлер покажет, так ли оно на практике. Используйте value типы, там где это возможно, обычно это упрощает код:

void MyFun()
{
    std::vector<MyClass1> v1;
    // и никаких сложностей с ручным управлением памятью
    // зато получаем локальность данных в случае с std::vector
}
Re[2]: finally
От: Karamat Беларусь  
Дата: 07.12.15 06:11
Оценка:
Здравствуйте, PM, Вы писали:

PM>По приведённому примеру кода не видно, и явно не указано требование, чтобы объекты в коллекциях были полиморфными.


там все гораздо хуже

в функции используется АПИ, требующее ссылок типа std::vector<MyClass1*>


PM>Используйте value типы, там где это возможно, обычно это упрощает код


Там по настоящему более общая задача — в функции надо гарантированно выполнить определенные действия, даже если возникнет исключение. Поэтому не важно, указатель или value. Для такого придумали finally, а в С++, на мой взгляд, минимальный оверхед будет при таком коде:

void FunA(int a, int b, int c)
{
    try
    {
        //код функции
    }
    catch(...)
    {
        FunAFinally(a, b, c);
        throw;
    }
    FunAFinally(a, b, c);
}

void FunAFinally(int a, int b, int c)
{
    //код финализатора
}
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.