Подтвердить что ты не робот

С++ 11 с выходом на охрану, хорошая идея?

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

Кажется, что-то вроде хака. Но я удивлен, что не видел его где-то еще, используя возможности С++ 11. Я думаю, что boost имеет что-то подобное для С++ 98.

Но это хорошая идея? Или есть потенциальные проблемы, которые я пропустил? Есть ли аналогичное решение (с возможностями С++ 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;
}
4b9b3361

Ответ 1

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

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

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

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

Есть уже существует аналогичное решение (с С++ 0x) в boost или подобном?

Alexandrescu долгое время возвращался с ScopeGuard. У обоих Boost и std::tr1 есть вещь, называемая scoped_ptr и shared_ptr (с пользовательским удалением), который позволяет выполнить только это.

Ответ 3

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

Подобно Alexandrescu ScopeGuard и Boost 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 на С# или Java. Чек, даже pthreads обеспечивает рудиментарную защиту области, называемую pthread_cleanup_push.

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

Ответ 4

Если заменить 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__]

Ответ 5

Реализация может быть очень упрощена с помощью 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));
}

Ответ 6

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

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

Тогда:

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

Протестировано с помощью MSVС++ 11.0 (VS2012). С наилучшими пожеланиями.

Ответ 7

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

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

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

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

Ответ 8

Использование 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);
    });
    ...
}

Ответ 9

my $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; } );