Как стать автором
Обновить

Аналог scope(exit) на С++

Время на прочтение2 мин
Количество просмотров6.2K
Прочитал, недавно, на хабре, статью про scope(exit) в языке D и проникся красотой идеи.
Ведь так часто бывает, что нужно выполнить какой-либо код по выходу из функции, а создавать каждый раз страж — утомительно, да и не всегда возможно.
Но, использую новый стандарт, в С++ можно сделать не хуже, кому интересно — прошу под кат.

Сама идея — очень простая, нужно создать класс, принимающий функтор в конструкторе, и выполняющий его в деструкторе.
class exit_scope
{
public:
	template< typename Callback >
	exit_scope( const Callback& callback ):caller_( new caller<Callback>(callback) ){}
	~exit_scope(){ caller_->call(); delete caller_; }
private:
	exit_scope();
	exit_scope(const exit_scope&);
	exit_scope& operator =(const exit_scope&);


	struct caller_interface
	{
		virtual ~caller_interface(){}
		virtual void call()=0;
	};

	template< typename Callback >
	struct caller: caller_interface
	{
		caller( const Callback& callback ): callback_(callback){}
		virtual void call(){ callback_(); }
		Callback callback_;
	};

	caller_interface* caller_;
};

Теперь можно написать что-то вроде:
int test()
{
	std::string omega;
	std::cin >> omega;
	exit_scope guard1 = [&](){ std::cout<< "First exit scope "<<omega<<std::endl; };
	return 0;
}

Пока не очень удобно.
Раз уж на хабре неделя макросов, попробуем упростить использование с помощью них:
#define EXIT_SCOPE_CREATE_UNIQ_NAME2(line) exit_scope_guard_##line
#define EXIT_SCOPE_CREATE_UNIQ_NAME(line) EXIT_SCOPE_CREATE_UNIQ_NAME2(line)
#define EXIT_SCOPE exit_scope EXIT_SCOPE_CREATE_UNIQ_NAME(__LINE__) = [&]()

Теперь в коде можно написать так:
int test1()
{
	std::string omega;
	std::cin >> omega;
	EXIT_SCOPE{ std::cout << "Second exit scope" << omega<< std::endl; };
	EXIT_SCOPE{ std::cout << "Third exit scope" << omega<<std::endl; };
	return 0;
}

По-моему — намного лучше.
Вот только сам класс выглядит не очень, попробуем его причесать, используя std::function.
class exit_scope
{
public:
	template< typename Callback >
	exit_scope( const Callback& callback ):callback_(callback){}
	~exit_scope(){ callback_(); }
private:
	exit_scope();
	exit_scope(const exit_scope&);
	exit_scope& operator =(const exit_scope&);
	std::function<void(void)> callback_;
};

Раз уж зашла речь о boost( вернее о том, что с него пришло в стандарт ), нельзя не упомянуть, что в нём есть похожий механизм. Но он не использует преимущества нового стандарта, и использовать его, на мой вкус, не совсем удобно.
Используя boost/scope_exit.hpp можно было-бы написать такой код:
int test1()
{
	std::string omega;
	std::cin >> omega;
    BOOST_SCOPE_EXIT( (&omega) )
    {
        std::cout << "Fourth exit scope" <<omega<< std::endl;
    } BOOST_SCOPE_EXIT_END

	return 0;
}

Из недостаков моей реализации можно назвать некоторый оферхэд на выполнение и проблема с эксепшенами в вызываемом коде.
Теги:
Хабы:
+17
Комментарии36

Публикации

Изменить настройки темы

Истории

Работа

Программист C++
123 вакансии
QT разработчик
6 вакансий

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн