Skip to content

Instantly share code, notes, and snippets.

@mpusz

mpusz/main.cpp Secret

Created April 13, 2012 11:47
Show Gist options
  • Save mpusz/dab4bec98a0baa2ebabe to your computer and use it in GitHub Desktop.
Save mpusz/dab4bec98a0baa2ebabe to your computer and use it in GitHub Desktop.
D like scopes trial implementation
#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";
}
}
#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
@mpusz
Copy link
Author

mpusz commented Apr 15, 2012

@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?

@evgeny-panasyuk
Copy link

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