class TSomeObject {
private:
int * data = nullptr;
public:
.......
void play(int* external_data) {
data = external_data;
....
some_action();
....
data = nullptr;
}
void some_action() {
....
throw std::exception("very bad code");
}
}
Какой-то внешний код вызывает метод play, передавая указатель на внешние данные. чтобы не передавать по всем методам класса эти данные, приняли решение их сохранить на уровне класса.
Хотелось бы чтобы после завершения метода play data был равен nullptr. Понимаю что можно сделать try {... data = nullptr; } catch (...){data = nullptr; throw; }, но мне кажется это не очень правильный патерн в рамках идеологии RAII. Хотелось бы понять какие патерны вы используете в такой ситуации. Заранее ответивщим спасибо.
Здравствуйте, yaser, Вы писали:
Y>Какой-то внешний код вызывает метод play, передавая указатель на внешние данные. чтобы не передавать по всем методам класса эти данные, приняли решение их сохранить на уровне класса. Y>Хотелось бы чтобы после завершения метода play data был равен nullptr. Понимаю что можно сделать try {... data = nullptr; } catch (...){data = nullptr; throw; }, но мне кажется это не очень правильный патерн в рамках идеологии RAII. Хотелось бы понять какие патерны вы используете в такой ситуации. Заранее ответивщим спасибо.
А вообще, лично я избегаю делать членами класса данные, которые бесполезны вне вызова какой-то одной функции (play, в нашем случае). Это явное показание к пересмотру дизайна.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, uncommon, Вы писали:
U>Не люблю Boost.ScopeExit из-за её монстроидальной реализации.
Да я согласен. Конечно же, C++0x предоставляет возможности написать все значительно привлекательнее. Но, в то же время, у BOOST_SCOPE_EXIT есть одно важное преимущество — он написан в библиотеке самого общего использования и избавляет от засоренияя кода собстенными Guard-ами.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, yaser, Вы писали:
Y>Хотелось бы чтобы после завершения метода play data был равен nullptr. Понимаю что можно сделать try {... data = nullptr; } catch (...){data = nullptr; throw; }, но мне кажется это не очень правильный патерн в рамках идеологии RAII.
try-catch вполне себе работает, читаем и достаточно компактен. Значит паттрерн правильный
Здравствуйте, kr510, Вы писали:
K>try-catch вполне себе работает, читаем и достаточно компактен. Значит паттрерн правильный
Блок catch отработает только при выбрасывании исключения. Автору же топика, если я правильно понимаю, нужна секция кода, которая гарантированно отработает при любом сценарии выхода из скоупа. Т.е. нечто равноценное finally или RAII.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, kr510, Вы писали:
K>>try-catch вполне себе работает, читаем и достаточно компактен. Значит паттрерн правильный
R>Блок catch отработает только при выбрасывании исключения. Автору же топика, если я правильно понимаю, нужна секция кода, которая гарантированно отработает при любом сценарии выхода из скоупа. Т.е. нечто равноценное finally или RAII.
Это ты автору топика предложи. Вариант, конечно, очевидный, только я сомневаюсь, что это то, о чем он мечтал. Внутири cleanup нет никакой связи с локальными объектами вызывающей фунции. Плодить для каждого метода свой cleanup — вариант не слишком привлекательный. И то, что вызов cleanup не достаточно сделать один раз, а надо не забыть его вставить в каждой точке возврата — тоже не есть хорошо.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
R>Внутири cleanup нет никакой связи с локальными объектами вызывающей фунции.
cleanup может быть либо лямдой, если "void play()" только один, либо методом TSomeObject.
R>И то, что вызов cleanup не достаточно сделать один раз, а надо не забыть его вставить в каждой точке возврата — тоже не есть хорошо.
Точек куда надо вставить только 2: выход по exception и выход по успеху. Это предельно простой и надежный код.
Здравствуйте, kr510, Вы писали:
R>>Внутири cleanup нет никакой связи с локальными объектами вызывающей фунции. K>cleanup может быть либо лямдой, если "void play()" только один, либо методом TSomeObject.
А можешь привести пример, как ты собираешься вызвать в блоке catch лямбду, определенную в блоке try?
И потом, это уже не просто catch, как ты предлагал изначально, а catch плюс лямбды. Еще один шажок и приходим к тому, что лямбду можно поместить в деструктор какого-нибудь объекта, как предлагали здесь
. А там, глядишь, и придем к выводу, что решение с catch не единственное, а, возможно даже, не самое лучшее.
K>Точек куда надо вставить только 2: выход по exception и выход по успеху. Это предельно простой и надежный код.
Ну во-первых две это уже хуже, чем одна. А во-вторых, остается таки открытым вопрос, что делать в случаях, когда точек возврата больше, чем две. Например, захотели мы добавить еще один блок catch для std::exception, как быть? Нормальное же желание, нет ничего предосудительного?
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, watchmaker, Вы писали:
W>Здравствуйте, yaser, Вы писали:
Y>>Хотелось бы понять какие патерны вы используете в такой ситуации. Заранее ответивщим спасибо.
W>Ну а какие тут ещё варианты? либо используешь одну из многочисленных реализаций scope_exit, либо пишешь свою простую adhoc версию:
In RAII, holding a resource is tied to object lifetime: resource allocation (acquisition) is done during object creation (specifically initialization), by the constructor, while resource deallocation (release) is done during object destruction, by the destructor.
В его случае под овладением ресурсом можно понимать овладение указателем на истоник данных. Ну так, чего ж тогда мучаться — нужно просто и создать указатель на этот указатель:
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, kr510, Вы писали:
R>>>Внутири cleanup нет никакой связи с локальными объектами вызывающей фунции. K>>cleanup может быть либо лямдой, если "void play()" только один, либо методом TSomeObject.
R>А можешь привести пример, как ты собираешься вызвать в блоке catch лямбду, определенную в блоке try?
class TSomeObject {
private:
int * data ;
public:
void play(int* external_data) {
auto init = [&]{ data = external_data; };
auto cleanup = [&]{ data = nullptr; };
init();
try{
some_action();
}
catch (...){
cleanup();
throw;
}
cleanup();
}
void some_action() {
throw std::exception("very bad code");
}
};
Здравствуйте, kr510, Вы писали:
R>>А можешь привести пример, как ты собираешься вызвать в блоке catch лямбду, определенную в блоке try?
K>... K>class TSomeObject { K>private: K> int * data ; K>public: K> void play(int* external_data) {
K> auto init = [&]{ data = external_data; }; K> auto cleanup = [&]{ data = nullptr; };
K> init();
K> try{ K> some_action(); K> } K> catch (...){ K> cleanup(); K> throw; K> } K> cleanup(); K> } K> void some_action() { K> throw std::exception("very bad code"); K> } K>}; K>[/ccode]
И где здесь лямбда, определенная в блоке try? Я, вообще полагал, что мы обсуждаем более общий случаю, чем приведен в стартовом сообщении. А в более общем случае лямбдам бывает нужно захватывать объекты из локального скоупа. А расширение скоупа объектов, вообще, не есть хорошо, а в некоторых случаях просто не приемлемо.
А главное, не понятна твоя настойчивость в отстаивании варианта с catch. Ну недостатков же целый ряд — как уже упомянутых, так и неупомянутых. А преимущества? Какие преимущества у варианта с catch перед вариантами с RAII?
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, Vamp, Вы писали:
V>Ну какой еще scope_exit в 2016 году???
Я, конечно же, в курсе, что лямбду можно запихнуть в умный указатель. Только для регулярного использования я бы предпочел что-нибудь более заточенное для этих целей. Реализуется и просто и юзабельно, странно, что в стандарной библиотеке нет ничего подобного. Или я чего-то не знаю?
--
Не можешь достичь желаемого — пожелай достигнутого.