/**
|
|
* Copyright (c) 2018 Niklas Rosenstein
|
|
* MIT licensed.
|
|
*
|
|
* @description This header provides an interface for creating context
|
|
* managers in C++ similar to what you have in Python.
|
|
*/
|
|
|
|
#pragma once
|
|
#include <functional>
|
|
#include <vector>
|
|
|
|
namespace niklasrosenstein {
|
|
|
|
/* Base class for context managers. A context manager can only be moved and
|
|
* not copied. It will call a function when it is destroyed (be careful
|
|
* about closure scope being destroyed when returned from a function). */
|
|
class ctx_manager {
|
|
friend class exit_stack;
|
|
std::function<void()> _func;
|
|
public:
|
|
ctx_manager() : _func() {}
|
|
ctx_manager(ctx_manager const& other) = delete;
|
|
ctx_manager(ctx_manager&& other) : _func(std::move(other._func)) { other._func = {}; }
|
|
ctx_manager(std::function<void()>&& f) : _func(std::move(f)) {}
|
|
ctx_manager(std::function<void()> const& f) : _func(f) {}
|
|
|
|
~ctx_manager() { if (_func) _func(); }
|
|
|
|
ctx_manager& operator = (ctx_manager const& other) = delete;
|
|
ctx_manager& operator = (ctx_manager&& other) {
|
|
_func = std::move(other._func);
|
|
other._func = {};
|
|
return *this;
|
|
}
|
|
|
|
operator bool () const { return (bool) _func; }
|
|
};
|
|
|
|
/* Represents a stack of functions that will be called when the
|
|
* exit_stack is destroyed. */
|
|
struct exit_stack {
|
|
std::vector<std::function<void()>> callbacks;
|
|
|
|
inline exit_stack() {}
|
|
inline exit_stack(std::function<void()>&& func) { callbacks.push_back(std::forward<std::function<void()>>(func)); }
|
|
inline exit_stack(ctx_manager&& func) { this->operator << (std::forward<ctx_manager>(func)); }
|
|
|
|
inline ~exit_stack() {
|
|
for (auto it = callbacks.rbegin(); it != callbacks.rend(); ++it) {
|
|
(*it)();
|
|
}
|
|
}
|
|
|
|
inline exit_stack& operator << (std::function<void()>&& func) {
|
|
callbacks.push_back(std::forward<std::function<void()>>(func));
|
|
return *this;
|
|
}
|
|
|
|
inline exit_stack& operator << (ctx_manager&& ctx) {
|
|
if (ctx._func) {
|
|
callbacks.push_back(std::move(ctx._func));
|
|
ctx._func = {};
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
inline void clear() {
|
|
callbacks.clear();
|
|
}
|
|
};
|
|
|
|
} // namespace niklasrosenstein
|