-
-
Save mpusz/dab4bec98a0baa2ebabe to your computer and use it in GitHub Desktop.
D like scopes trial implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "scope.h" | |
#include <iostream> | |
class Action { | |
static unsigned _count; | |
unsigned _id; | |
public: | |
Action(bool valid): | |
_id(++_count) | |
{ | |
std::cout << "Action " << _id << "\n"; | |
if(!valid) { | |
std::cout << "Throwing exception from action " << _id << "!!!\n"; | |
throw std::exception(); | |
} | |
} | |
void Rollback() | |
{ | |
std::cout << "Rollback " << _id << "\n"; | |
} | |
void Cleanup() | |
{ | |
std::cout << "Cleanup " << _id << "\n"; | |
} | |
}; | |
unsigned Action::_count = 0; | |
void test() | |
{ | |
Action a1(true); | |
scope s1(at_exit( [&]{ a1.Cleanup(); }), | |
at_failure([&]{ a1.Rollback(); })); | |
{ | |
Action a2(true); | |
scope s2(at_exit( [&]{ a2.Cleanup(); }), | |
at_failure([&]{ a2.Rollback(); })); | |
} | |
Action a3(true); | |
scope s3(at_failure([&]{ a3.Rollback(); }), | |
at_exit( [&]{ a3.Cleanup(); })); | |
Action a4(true); | |
scope s4(at_failure([&]{ a4.Rollback(); })); | |
Action a5(true); | |
scope s5(at_exit( [&]{ a5.Cleanup(); })); | |
Action a6(false); | |
scope s6(at_exit( [&]{ a6.Cleanup(); }), | |
at_failure([&]{ a6.Rollback(); })); | |
} | |
void test2() | |
{ | |
Action a1(true); | |
scope s1(at_exit([&]{ a1.Cleanup(); }), | |
at_failure([&] | |
{ | |
Action a3(true); | |
scope s3(at_exit([&]{ a3.Cleanup(); }), | |
at_failure([&]{ a3.Rollback(); })); | |
a1.Rollback(); | |
})); | |
Action a2(false); | |
} | |
int main() | |
{ | |
try { | |
test(); | |
} | |
catch(...) { | |
std::cout << "Exception caught\n"; | |
} | |
std::cout << "\n"; | |
try { | |
test2(); | |
} | |
catch(...) { | |
std::cout << "Exception caught\n"; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef SCOPE_H | |
#define SCOPE_H | |
#include <functional> | |
#include <exception> | |
namespace details { | |
struct rollback_cb_t { }; | |
struct cleanup_cb_t { }; | |
template<typename T> | |
class scope_cb { | |
std::function<void()> _cb; | |
public: | |
scope_cb() = default; | |
scope_cb(std::function<void()> &&cb): _cb(std::move(cb)) {} | |
scope_cb(const scope_cb &) = delete; | |
scope_cb(scope_cb &&other) | |
{ | |
std::swap(_cb, other._cb); | |
} | |
scope_cb &operator=(scope_cb other) | |
{ | |
std::swap(_cb, other._cb); | |
return *this; | |
} | |
~scope_cb() { if(_cb) _cb(); } | |
}; | |
} | |
typedef details::scope_cb<details::cleanup_cb_t> cleanup_t; | |
typedef details::scope_cb<details::rollback_cb_t> rollback_t; | |
class scope { | |
cleanup_t _cleanup; | |
rollback_t _rollback; | |
public: | |
scope(cleanup_t &&cleanup, rollback_t &&rollback = rollback_t()): | |
_cleanup(std::move(cleanup)), _rollback(std::move(rollback)) {} | |
scope(rollback_t &&rollback, cleanup_t &&cleanup = cleanup_t()): | |
_cleanup(std::move(cleanup)), _rollback(std::move(rollback)) {} | |
scope(const scope &) = delete; | |
scope(scope &&) = delete; | |
scope &operator=(const scope &) = delete; | |
scope &operator=(scope &&) = delete; | |
~scope() = default; | |
}; | |
inline cleanup_t at_exit(std::function<void()> &&cb) | |
{ | |
return cleanup_t(std::move(cb)); | |
} | |
inline rollback_t at_failure(std::function<void()> &&cb) | |
{ | |
// do not schedule anything if already in an exception handler | |
return std::uncaught_exception() ? rollback_t() : rollback_t([=]{ if(std::uncaught_exception()) cb(); }); | |
} | |
#endif |
I have implemented D's scope(failure) and scope(success) in C++, check https://github.com/panaseleus/stack_unwinding
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@Mankarse: Thanks for interesting reading. I was familiar with Boost.ScopeExit and loki::ScopeGuard but I always wondered why they did not use std::uncaught_exception(). Now I know :-)
Thank you also for pointing std::function move related problem in my code. I modified the code a bit and added your test that now works as expected. However that sollution probably is still not exactly what D scopes can do?