Охрана выхода C ++ 11, хорошая идея? - PullRequest
24 голосов
/ 08 сентября 2010

Я написал небольшой класс утилит для C ++ 11, который я использую в качестве защиты области видимости для упрощения обработки безопасности исключений и подобных вещей.

Похоже, взломать. Но я удивлен, что не видел этого где-то еще, используя функции C ++ 11. Я думаю, что в Boost есть что-то похожее для C ++ 98.

Но разве это хорошая идея? Или есть потенциальные проблемы, которые я пропустил? Есть ли уже подобное решение (с функциями C ++ 11) в boost или аналогичное?

    namespace detail 
    {
        template<typename T>
        class scope_exit : boost::noncopyable
        {
        public:         
            explicit scope_exit(T&& exitScope) : exitScope_(std::forward<T>(exitScope)){}
            ~scope_exit(){try{exitScope_();}catch(...){}}
        private:
            T exitScope_;
        };          

        template <typename T>
        scope_exit<T> create_scope_exit(T&& exitScope)
        {
            return scope_exit<T>(std::forward<T>(exitScope));
        }
    }


#define _UTILITY_EXIT_SCOPE_LINENAME_CAT(name, line) name##line
#define _UTILITY_EXIT_SCOPE_LINENAME(name, line) _UTILITY_EXIT_SCOPE_LINENAME_CAT(name, line)
#define UTILITY_SCOPE_EXIT(f) const auto& _UTILITY_EXIT_SCOPE_LINENAME(EXIT, __LINE__) = ::detail::create_scope_exit(f)

и используется что-то вроде.

int main () 
{
  ofstream myfile;
  myfile.open ("example.txt");
  UTILITY_SCOPE_EXIT([&]{myfile.close();}); // Make sure to close file even in case of exception
  myfile << "Writing this to a file.\n"; // Imagine this could throw
  return 0;
}

Ответы [ 9 ]

20 голосов
/ 08 сентября 2010

Но разве это хорошая идея?

Конечно. Смежной темой является парадигма RAII .

Или есть потенциальные проблемы, которые я пропустил?

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

Является там уже подобное решение (с Особенности C ++ 0x) в Boost или аналогичный?

Александреску придумал ScopeGuard давным-давно. И Boost, и std::tr1 имеют функцию, называемую scoped_ptr и shared_ptr (с пользовательским средством удаления), которая позволяет вам выполнить только это.

17 голосов
/ 08 сентября 2010

Для записи есть Boost ScopeExit .

9 голосов
/ 01 октября 2010

Охрана прицела определенно хорошая идея.Я думаю, что концепция охраны границ - это мощный инструмент для обеспечения безопасности исключений.Если вы можете сделать более безопасную, более чистую версию, которая * Boost's ScopeExit с использованием синтаксиса C ++ 0x, я думаю, что это стоило бы вашего времени.

Подобно ScopeGuard * от Александреску* и Boost's ScopeExit, D язык программирования имеет прямой синтаксис для такого рода вещей.Команда программистов D посчитала, что защита области видимости была достаточно хорошей идеей, что они добавили ее непосредственно к языку (т.е. он не реализован в библиотеке).

Пример.

void foo( bool fail )
{
   scope(exit)
   {
      writeln("I'm always printed");
   }

   scope(success) writeln("The function exited normally");

   scope(error)
      writeln("The function exited with an exception.");

   if( fail )
      throw new Exception("Die Die Die!");
}

Охрана, основанная на прицеле, не нова.Его функциональность можно легко скопировать с помощью деструктора класса (RAII и все такое).Также возможно заменить на try/finally в C # или Java.Черт, даже pthreads предоставляет элементарную защиту области, называемую pthread_cleanup_push .

Что делает защиту области действия настолько мощной, когда у вас есть несколько операторов scope(*) в функции.Он невероятно хорошо масштабируется, в отличие от try/finally, которые требуют сверхчеловеческих способностей для управления чем-то более чем двумя.

5 голосов
/ 14 июня 2011

Если заменить create_scope_exit бинарным оператором, мы можем удалить скобки:

class at_scope_exit
{
    template<typename F>
    struct scope_exit_fn_holder : boost::noncopyable
    {
        scope_exit_fn_holder(F&& f) : f(std::forward<F>(f)) {}

        F f;
        ~scope_exit_fn_holder() { f(); }
    };

    template<typename F>
    friend scope_exit_fn_holder<F> operator==(at_scope_exit, F&& f)
    {
        return scope_exit_fn_holder<F>(std::forward<F>(f));
    }
};

Использование:

auto atScopeExit = at_scope_exit() == [&]
{
    ...
};

UPD:
Соответствующий макрос:

#include <boost/preprocessor/cat.hpp>

#define AT_SCOPE_EXIT auto BOOST_PP_CAT(scopeExit_, __LINE__) = at_scope_exit() == [&]
#define AT_SCOPE_EXIT_EX(...) auto BOOST_PP_CAT(scopeExit_, __LINE__) = at_scope_exit() == [__VA_ARGS__]
0 голосов
/ 14 января 2018

Использование Boost:

#include <boost/preprocessor/cat.hpp>

template<class Fn>
class ScopeGuardDetails {
    const Fn m_fn;
public:
    constexpr ScopeGuardDetails(Fn &&fn) : m_fn(fn) {}
    ~ScopeGuardDetails() { m_fn(); }
};
#define ScopeGuardName BOOST_PP_CAT(BOOST_PP_CAT(__scope_guard, _), BOOST_PP_CAT(BOOST_PP_CAT(__LINE__, _), __COUNTER__))
#define defer(stmt) const auto ScopeGuardName = [](const auto _fn) { \
    return ScopeGuardDetails<decltype(_fn)> { std::move(_fn) }; \
}([&] { stmt });

Использование:

if (gdiplus::GdiplusStartup(&token, &startupInput, nullptr) == Gdiplus::Ok) {
    defer({
        gdiplus::GdiplusShutdown(token);
    });
    ...
}
0 голосов
/ 19 июля 2013

Это хорошая идея, но есть пара проблем с вами, класс.

  1. Вы должны отключить новый оператор (вы не хотите, чтобы пользователь использовал его в таких случаях.способ, который вынуждает вызывать delete для этого, верно?)
  2. вам нужна функция "commit", для того чтобы это было scope guard вместо простого RAII

обратите внимание, что если вы реализуете пункт 2, вам понадобится осмысленное имя для каждого объекта охраны, который вы создаете.В общем, это не проблема, но это может быть в вашем приложении (или на ваш вкус).

Наконец, этот вопрос, вероятно, был бы более уместным для CodeReview .

0 голосов
/ 14 ноября 2012

Мы могли бы опустить уродливые вещи [&], поместив их в определение:

#define UTILITY_SCOPE_EXIT(f) const auto& _UTILITY_EXIT_SCOPE_LINENAME(EXIT, __LINE__) = ::detail::create_scope_exit([&]f)

Тогда:

UTILITY_SCOPE_EXIT({myfile.close();});

Протестировано с MSVC ++ 11.0 (VS2012). С уважением.

0 голосов
/ 21 января 2012

мои $ 0,02

struct at_scope_end
{
    std::function < void () > Action;

    at_scope_end (std::function < void () > Action) :
        Action (Action)
    {
    }

    ~at_scope_end ()
    {
        Action ();
    }
};

#define AT_SCOPE_END_CAT(x,y)    x##y
#define AT_SCOPE_END_ID(index)   AT_SCOPE_END_CAT(__sg, index)
#define AT_SCOPE_END(expr)      at_scope_end AT_SCOPE_END_ID(__LINE__) ( [&] () { expr; } );
0 голосов
/ 18 октября 2011

Реализация может быть очень упрощена с использованием tr1::function и tr1::unique_ptr, как показано ниже:

namespace detail
{
    class ScopeGuard
    {
    public:
        explicit ScopeGuard(std::function<void()> onExitScope) 
            : onExitScope_(onExitScope), dismissed_(false)
        { }

        ~ScopeGuard()
        {
            try
            {
                if(!dismissed_)
                {
                    onExitScope_();
                }
            }
            catch(...){}
        }

        void Dismiss()
        {
            dismissed_ = true;
        }
    private:
        std::function<void()> onExitScope_;
        bool dismissed_;

        // noncopyable
    private:
        ScopeGuard(ScopeGuard const&);
        ScopeGuard& operator=(ScopeGuard const&);
    };
}

inline std::unique_ptr<detail::ScopeGuard> CreateScopeGuard(std::function<void()> onExitScope)
{
    return std::unique_ptr<detail::ScopeGuard>(new detail::ScopeGuard(onExitScope));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...