I have a class that describes a certain type of function (here I mean function in the mathematical, and not programming sense). Within this class I have a method called integrate(...) which uses std::function and std::string to determine exactly what to do. I know the way I have done things is poor, and I would like some ideas on how I could code this better. Preferably I would like to avoid using std::function and use templates instead. Below is a simplification of what I have that shows the basic idea.
I also don't think what I have is very efficient. The integration method can be quite costly and is called many times, so better efficiency would be quite helpful.
class SomeFunction {
public:
SomeFunction(SomeParameter param) { // do constructor type things // }
double value(double coord) { // return the value of the function // }
double derivative(double coord) { // return the derivative of the function // }
double integrate(std::string case, SomeType domain_of_integration) {
std::function<double(double)> to_be_integrated;
double some_other_parameter;
if (case == "value") {
to_be_integrated = [&](double x) -> double {
return this->value(x);
};
some_other_parameter = foo;
}
else if (case == "derivative") {
to_be_integrated = [&](double x) -> double {
return this->derivative(x);
};
some_other_parameter = bar;
}
else {
std::cerr << "ERROR - invalid integral\n";
exit(1);
}
// evaluate the integral using
// to_be_integrated, some_other_parameter and domain_of_integration
}
private:
// some member variables //
};
Create sub-function:
private:
template <typename F>
double integrate_impl(F to_be_integrated,
double some_other_parameter,
SomeType domain_of_integration)
{
// ...
// evaluate the integral using
// to_be_integrated, some_other_parameter and domain_of_integration
}
public:
double integrate(std::string option, SomeType domain_of_integration) {
if (option == "value") {
return integrate_impl([&](double x) -> double {
return this->value(x);
}, foo, domain_of_integration);
}
else if (option == "derivative") {
return integrate_impl([&](double x) -> double {
return this->derivative(x);
}, bar, domain_of_integration);
} else {
std::cerr << "ERROR - invalid integral\n";
exit(1);
}
}
Currently I am doing:
if constexpr(constexpr_bool_var1) {
auto arg1 = costly_arg1(); auto arg2 = costly_arg2();
if (costly_runtime_function(arg1, arg2)) {
// do X, possibly more constexpr conditions
// do Y
// ...
}
} else {
// do X, possibly more constexpr conditions
// do Y
// ...
}
One possible way is to convert the do X/Y etc. to one function doXY() and call it in both places, however it seems very unwieldy, as I have to write a function that solely exists for convenience of meta programming.
What I want is something like:
if not constexpr(constexpr_bool_var1 && some_magic(costly_runtime_function(arg1, arg2)) {
// do X, do Y
}
Another way is:
auto arg1 = costly_arg1(); // Unneeded extra work out not within constexpr
auto arg2 = costly_arg2();
if (constexpr_bool_var1 && costly_runtime_function(arg1, arg2)) {
} else {
// do X, possibly more constexpr conditions
// do Y
// ...
}
However here arg1 and arg2 are being declared outside the if condition so they will be needlessly instantiated.
I’m not sure I understand your question correctly; your original code and your second alternative don’t express quite the same function (the meaning of costly_runtime_function is reversed from “do X and Y” to “don’t do X and Y”), and in your first propoed alternative I don’t understand what your proposed syntax or what some_magic is. I’m answering your question with the semantics of your original code sample.
The best way to handle this is probably with a flag for whether or not to do X and Y:
bool do_x_y = true;
if constexpr(constexpr_bool_var1) {
// Maybe we don't actually want X and Y
auto arg1 = costly_arg1(); auto arg2 = costly_arg2();
do_x_y = costly_runtime_function(arg1, arg2);
}
if (do_x_y) {
// do X, possibly more constexpr conditions
// do Y
// ...
}
Note that, as Andrei R. points out in the comments, the compiler can probably handle the optimization anyway. This makes it clearer to a human reader that you want this handled at compile time, though.
Here's one way. Whether it's more expressive/maintainable I cannot say.
#include <cstdlib>
#include <utility>
/*
* simulation
*/
void doX() {}
void doY() {}
int costly_arg1() { return 1; }
int costly_arg2() { return 2; }
bool costly_runtime_function(int, int) { return rand() < RAND_MAX / 2; }
constexpr bool constexpr_bool_var1 = true;
/*
* A functor that maybe does something
*/
template<class F>
struct maybe_do
{
constexpr maybe_do(F&& f) : f(std::move(f)) {}
constexpr void operator()() const
{
if (enabled)
f();
}
constexpr void enable(bool e) {
enabled = e;
}
F f;
bool enabled = true;
};
int main()
{
auto thing = maybe_do{
[] {
doX();
doY();
}
};
if constexpr(constexpr_bool_var1)
{
thing.enable(costly_runtime_function(costly_arg1(),
costly_arg2()));
}
thing();
}
You could use a lambda for that:
auto stuff = [&] {
// do X, possibly more constexpr conditions
// do Y
// ...
};
if constexpr(constexpr_bool_var1) {
auto arg1 = costly_arg1(); auto arg2 = costly_arg2();
if (costly_runtime_function(arg1, arg2)) {
stuff();
}
} else {
stuff();
}
And if your lambda can receive auto values, you could also pass variable of different types from inside the if scope.
I have 100 or so trampoline functions. I would like to know whether it is possible to automate wrapping each one inside a try/catch block.
Please be warned in advance, this is not an easy question. I will start by describing the problem with (simplified) code, and will then attempt to answer it as best I can below, so the reader may see where I am at.
Foo has a function pointer table:
EDIT: This is a C function pointer table. So it could accept static W::w.
Signatures are here: http://svn.python.org/projects/python/trunk/Include/object.h
EDIT: I've attempted a test case here:
class Foo {
Table table;
Foo() {
// Each slot has a default lambda.
:
table->fp_53 = [](S s, A a, B b) -> int {cout<<"load me!";};
table->fp_54 = [](S s, C c, D d, E e) -> float {cout<<"load me!";};
// ^ Note: slots MAY have different signatures
// only the first parameter 'S s' is guaranteed
}
// Foo also has a method for loading a particular slot:
:
void load53() { table->fp_53 = func53; }
void load54() { table->fp_54 = func54; }
:
}
If a particular slot is 'loaded', this is what gets loaded into it:
int func53(S s, A a, B b) {
try{
return get_base(s)->f53(a,b);
}
catch(...) { return 42;}
}
float func54(S s, C c, D d, E e) {
try{
return get_base(s)->f54(c,d,e);
}
catch(...) { return 3.14;}
}
I am trying to accomplish this using lambdas, so as to bypass having to define all of these func53 separately. Something like this:
class Foo {
:
void load53() {
table->fp_53 =
[](S s, A a, B b)->int { return get_base(s)->f53(a,b); }
}
void load54() {
table->fp_54 =
[](S s, C c, D d, E e)->float { return get_base(s)->f54(c,d,e); }
}
However, this is failing to trap errors. I need to be putting a try/catch around the return statement:
try{ return get_base(s)->f53(a,b); } catch{ return 42; }
However, this creates a lot of clutter. It would be nice if I could do:
return trap( get_base(s)->f53(a,b); )
My question is: is there any way to write this trap function (without using #define)?
This is what I've come up with so far:
I think this would pass all the necessary information:
trap<int, &Base::f53>(s,a,b)
trap's definition could then look like this:
template<typename RET, Base::Func>
static RET
trap(S s, ...) {
try {
return get_base(s)->Func(...);
}
catch {
return std::is_integral<RET>::value ? (RET)(42) : (RET)(3.14);
}
}
This may allow for a very clean syntax:
class Foo {
:
void load53() { table->fp_53 = &trap<int, &Base::f53>; }
void load54() { table->fp_54 = &trap<float, &Base::f54>; }
}
At this point I'm not even sure whether some laws have been violated. table->fp_53 must be a valid C function pointer.
Passing in the address of a nonstatic member function (&Base::f53>) won't violate this, as it is a template parameter, and is not affecting the signature for trap
Similarly, ... should be okay as C allows varargs.
So if this is indeed valid, can it be cleaned up?
My thoughts are:
1) maybe the ... should be moved back to the template parameter as a pack.
2) maybe it is possible to deduce the return type for trap, and save one template parameter
3) that Base::Func template parameter is illegal syntax. And I suspect it isn't even close to something legal. Which might scupper the whole approach.
#include <utility>
template <typename T, T t>
struct trap;
template <typename R, typename... Args, R(Base::*t)(Args...)>
struct trap<R(Base::*)(Args...), t>
{
static R call(int s, Args... args)
{
try
{
return (get_base(s)->*t)(std::forward<Args>(args)...);
}
catch (...)
{
return std::is_integral<R>::value ? static_cast<R>(42)
: static_cast<R>(3.14);
}
}
};
Usage:
table->fp_53 = &trap<decltype(&Base::f53), &Base::f53>::call;
table->fp_54 = &trap<decltype(&Base::f54), &Base::f54>::call;
DEMO
Note: std::forward can still be used although Args is not a forwarding reference itself.
template<typename RET, typename... Args>
struct trap_base {
template<RET (Base::* mfptr)(Args...)>
static RET
trap(S s, Args... args) {
try {
return (get_base(s).*mfptr)(args...);
}
catch (...) {
return std::is_integral<RET>::value ? (RET)(42) : (RET)(3.14);
}
}
};
Usage:
void load53() { table.fp_53 = &trap_base<int, int>::trap<&Base::f53>; }
void load54() { table.fp_54 = &trap_base<float, int, float>::trap<&Base::f54>; }
Demo.
You can probably also use a partial specialization to extract RET and Args from decltype(&base::f53) etc.
trap_gen is a function that returns a function pointer to a function generated on the fly, the equivalent of your trap function.
Here is how you use it
table->fp_53 = trap_gen<>(Base::f53);
table->fp_54 = trap_gen<>(Base::f54);
...
Where Base::f53 and Base::f54 are static member functions (or function pointers, or global functions in a namespace).
Proof of concept :
#include <iostream>
template<typename R, class...A>
R (*trap_gen(R(*f)(A...)))(A...)
{
static auto g = f;
return [](A... a)
{
try {
return g(a...);
} catch (...) {
return std::is_integral<R>::value ? static_cast<R>(42)
: static_cast<R>(3.14);
}
};
}
int add(int a, int b)
{
return a+b;
}
int main() {
int(*f)(int, int) = trap_gen<>(add);
std::cout << f(2, 3) << std::endl;
return 0;
}
I'm attempting to write a simple ScopeGuard based on Alexandrescu concepts but with c++11 idioms.
namespace RAII
{
template< typename Lambda >
class ScopeGuard
{
mutable bool committed;
Lambda rollbackLambda;
public:
ScopeGuard( const Lambda& _l) : committed(false) , rollbackLambda(_l) {}
template< typename AdquireLambda >
ScopeGuard( const AdquireLambda& _al , const Lambda& _l) : committed(false) , rollbackLambda(_l)
{
_al();
}
~ScopeGuard()
{
if (!committed)
rollbackLambda();
}
inline void commit() const { committed = true; }
};
template< typename aLambda , typename rLambda>
const ScopeGuard< rLambda >& makeScopeGuard( const aLambda& _a , const rLambda& _r)
{
return ScopeGuard< rLambda >( _a , _r );
}
template<typename rLambda>
const ScopeGuard< rLambda >& makeScopeGuard(const rLambda& _r)
{
return ScopeGuard< rLambda >(_r );
}
}
Here is the usage:
void SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptions()
{
std::vector<int> myVec;
std::vector<int> someOtherVec;
myVec.push_back(5);
//first constructor, adquire happens elsewhere
const auto& a = RAII::makeScopeGuard( [&]() { myVec.pop_back(); } );
//sintactically neater, since everything happens in a single line
const auto& b = RAII::makeScopeGuard( [&]() { someOtherVec.push_back(42); }
, [&]() { someOtherVec.pop_back(); } );
b.commit();
a.commit();
}
Since my version is way shorter than most examples out there (like Boost ScopeExit) i'm wondering what specialties i'm leaving out. Hopefully i'm in a 80/20 scenario here (where i got 80 percent of neatness with 20 percent of lines of code), but i couldn't help but wonder if i'm missing something important, or is there some shortcoming worth mentioning of this version of the ScopeGuard idiom
thanks!
Edit I noticed a very important issue with the makeScopeGuard that takes the adquire lambda in the constructor. If the adquire lambda throws, then the release lambda is never called, because the scope guard was never fully constructed. In many cases, this is the desired behavior, but i feel that sometimes a version that will invoke rollback if a throw happens is desired as well:
//WARNING: only safe if adquire lambda does not throw, otherwise release lambda is never invoked, because the scope guard never finished initialistion..
template< typename aLambda , typename rLambda>
ScopeGuard< rLambda > // return by value is the preferred C++11 way.
makeScopeGuardThatDoesNOTRollbackIfAdquireThrows( aLambda&& _a , rLambda&& _r) // again perfect forwarding
{
return ScopeGuard< rLambda >( std::forward<aLambda>(_a) , std::forward<rLambda>(_r )); // *** no longer UB, because we're returning by value
}
template< typename aLambda , typename rLambda>
ScopeGuard< rLambda > // return by value is the preferred C++11 way.
makeScopeGuardThatDoesRollbackIfAdquireThrows( aLambda&& _a , rLambda&& _r) // again perfect forwarding
{
auto scope = ScopeGuard< rLambda >(std::forward<rLambda>(_r )); // *** no longer UB, because we're returning by value
_a();
return scope;
}
so for completeness, i want to put in here the complete code, including tests:
#include <vector>
namespace RAII
{
template< typename Lambda >
class ScopeGuard
{
bool committed;
Lambda rollbackLambda;
public:
ScopeGuard( const Lambda& _l) : committed(false) , rollbackLambda(_l) {}
ScopeGuard( const ScopeGuard& _sc) : committed(false) , rollbackLambda(_sc.rollbackLambda)
{
if (_sc.committed)
committed = true;
else
_sc.commit();
}
ScopeGuard( ScopeGuard&& _sc) : committed(false) , rollbackLambda(_sc.rollbackLambda)
{
if (_sc.committed)
committed = true;
else
_sc.commit();
}
//WARNING: only safe if adquire lambda does not throw, otherwise release lambda is never invoked, because the scope guard never finished initialistion..
template< typename AdquireLambda >
ScopeGuard( const AdquireLambda& _al , const Lambda& _l) : committed(false) , rollbackLambda(_l)
{
std::forward<AdquireLambda>(_al)();
}
//WARNING: only safe if adquire lambda does not throw, otherwise release lambda is never invoked, because the scope guard never finished initialistion..
template< typename AdquireLambda, typename L >
ScopeGuard( AdquireLambda&& _al , L&& _l) : committed(false) , rollbackLambda(std::forward<L>(_l))
{
std::forward<AdquireLambda>(_al)(); // just in case the functor has &&-qualified operator()
}
~ScopeGuard()
{
if (!committed)
rollbackLambda();
}
inline void commit() { committed = true; }
};
//WARNING: only safe if adquire lambda does not throw, otherwise release lambda is never invoked, because the scope guard never finished initialistion..
template< typename aLambda , typename rLambda>
ScopeGuard< rLambda > // return by value is the preferred C++11 way.
makeScopeGuardThatDoesNOTRollbackIfAdquireThrows( aLambda&& _a , rLambda&& _r) // again perfect forwarding
{
return ScopeGuard< rLambda >( std::forward<aLambda>(_a) , std::forward<rLambda>(_r )); // *** no longer UB, because we're returning by value
}
template< typename aLambda , typename rLambda>
ScopeGuard< rLambda > // return by value is the preferred C++11 way.
makeScopeGuardThatDoesRollbackIfAdquireThrows( aLambda&& _a , rLambda&& _r) // again perfect forwarding
{
auto scope = ScopeGuard< rLambda >(std::forward<rLambda>(_r )); // *** no longer UB, because we're returning by value
_a();
return scope;
}
template<typename rLambda>
ScopeGuard< rLambda > makeScopeGuard(rLambda&& _r)
{
return ScopeGuard< rLambda >( std::forward<rLambda>(_r ));
}
namespace basic_usage
{
struct Test
{
std::vector<int> myVec;
std::vector<int> someOtherVec;
bool shouldThrow;
void run()
{
shouldThrow = true;
try
{
SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesNOTRollbackIfAdquireThrows();
} catch (...)
{
AssertMsg( myVec.size() == 0 && someOtherVec.size() == 0 , "rollback did not work");
}
shouldThrow = false;
SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesNOTRollbackIfAdquireThrows();
AssertMsg( myVec.size() == 1 && someOtherVec.size() == 1 , "unexpected end state");
shouldThrow = true;
myVec.clear(); someOtherVec.clear();
try
{
SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesRollbackIfAdquireThrows();
} catch (...)
{
AssertMsg( myVec.size() == 0 && someOtherVec.size() == 0 , "rollback did not work");
}
}
void SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesNOTRollbackIfAdquireThrows() //throw()
{
myVec.push_back(42);
auto a = RAII::makeScopeGuard( [&]() { HAssertMsg( myVec.size() > 0 , "attempt to call pop_back() in empty myVec"); myVec.pop_back(); } );
auto b = RAII::makeScopeGuardThatDoesNOTRollbackIfAdquireThrows( [&]() { someOtherVec.push_back(42); }
, [&]() { HAssertMsg( myVec.size() > 0 , "attempt to call pop_back() in empty someOtherVec"); someOtherVec.pop_back(); } );
if (shouldThrow) throw 1;
b.commit();
a.commit();
}
void SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesRollbackIfAdquireThrows() //throw()
{
myVec.push_back(42);
auto a = RAII::makeScopeGuard( [&]() { HAssertMsg( myVec.size() > 0 , "attempt to call pop_back() in empty myVec"); myVec.pop_back(); } );
auto b = RAII::makeScopeGuardThatDoesRollbackIfAdquireThrows( [&]() { someOtherVec.push_back(42); if (shouldThrow) throw 1; }
, [&]() { HAssertMsg( myVec.size() > 0 , "attempt to call pop_back() in empty someOtherVec"); someOtherVec.pop_back(); } );
b.commit();
a.commit();
}
};
}
}
Even shorter: I don't know why you guys insist on putting the template on the guard class.
#include <functional>
class scope_guard {
public:
template<class Callable>
scope_guard(Callable && undo_func) try : f(std::forward<Callable>(undo_func)) {
} catch(...) {
undo_func();
throw;
}
scope_guard(scope_guard && other) : f(std::move(other.f)) {
other.f = nullptr;
}
~scope_guard() {
if(f) f(); // must not throw
}
void dismiss() noexcept {
f = nullptr;
}
scope_guard(const scope_guard&) = delete;
void operator = (const scope_guard&) = delete;
private:
std::function<void()> f;
};
Note that it is essential that the cleanup code does not throw, otherwise you get in similar situations as with throwing destructors.
Usage:
// do step 1
step1();
scope_guard guard1 = [&]() {
// revert step 1
revert1();
};
// step 2
step2();
guard1.dismiss();
My inspiration was the same DrDobbs article as for the OP.
Edit 2017/2018: After watching (some of) Andrei's presentation that André linked to (I skipped to the end where it said "Painfully Close to Ideal!") I realized that it's doable. Most of the time you don't want to have extra guards for everything. You just do stuff, and in the end it either succeeds or rollback should happen.
Edit 2018: Added execution policy which removed the necessity of the dismiss call.
#include <functional>
#include <deque>
class scope_guard {
public:
enum execution { always, no_exception, exception };
scope_guard(scope_guard &&) = default;
explicit scope_guard(execution policy = always) : policy(policy) {}
template<class Callable>
scope_guard(Callable && func, execution policy = always) : policy(policy) {
this->operator += <Callable>(std::forward<Callable>(func));
}
template<class Callable>
scope_guard& operator += (Callable && func) try {
handlers.emplace_front(std::forward<Callable>(func));
return *this;
} catch(...) {
if(policy != no_exception) func();
throw;
}
~scope_guard() {
if(policy == always || (std::uncaught_exception() == (policy == exception))) {
for(auto &f : handlers) try {
f(); // must not throw
} catch(...) { /* std::terminate(); ? */ }
}
}
void dismiss() noexcept {
handlers.clear();
}
private:
scope_guard(const scope_guard&) = delete;
void operator = (const scope_guard&) = delete;
std::deque<std::function<void()>> handlers;
execution policy = always;
};
Usage:
scope_guard scope_exit, scope_fail(scope_guard::execution::exception);
action1();
scope_exit += [](){ cleanup1(); };
scope_fail += [](){ rollback1(); };
action2();
scope_exit += [](){ cleanup2(); };
scope_fail += [](){ rollback2(); };
// ...
Boost.ScopeExit is a macro that needs to work with non-C++11 code, i.e. code that has no access to lambdas in the language. It uses some clever template hacks (like abusing the ambiguity that arises from using < for both templates and comparison operators!) and the preprocessor to emulate lambda features. That's why the code is longer.
The code shown is also buggy (which is probably the strongest reason to use an existing solution): it invokes undefined behaviour due to returning references to temporaries.
Since you're trying to use C++11 features, the code could be improved a lot by using move semantics, rvalue references and perfect-forwarding:
template< typename Lambda >
class ScopeGuard
{
bool committed; // not mutable
Lambda rollbackLambda;
public:
// make sure this is not a copy ctor
template <typename L,
DisableIf<std::is_same<RemoveReference<RemoveCv<L>>, ScopeGuard<Lambda>>> =_
>
/* see http://loungecpp.net/w/EnableIf_in_C%2B%2B11
* and http://stackoverflow.com/q/10180552/46642 for info on DisableIf
*/
explicit ScopeGuard(L&& _l)
// explicit, unless you want implicit conversions from *everything*
: committed(false)
, rollbackLambda(std::forward<L>(_l)) // avoid copying unless necessary
{}
template< typename AdquireLambda, typename L >
ScopeGuard( AdquireLambda&& _al , L&& _l) : committed(false) , rollbackLambda(std::forward<L>(_l))
{
std::forward<AdquireLambda>(_al)(); // just in case the functor has &&-qualified operator()
}
// move constructor
ScopeGuard(ScopeGuard&& that)
: committed(that.committed)
, rollbackLambda(std::move(that.rollbackLambda)) {
that.committed = true;
}
~ScopeGuard()
{
if (!committed)
rollbackLambda(); // what if this throws?
}
void commit() { committed = true; } // no need for const
};
template< typename aLambda , typename rLambda>
ScopeGuard< rLambda > // return by value is the preferred C++11 way.
makeScopeGuard( aLambda&& _a , rLambda&& _r) // again perfect forwarding
{
return ScopeGuard< rLambda >( std::forward<aLambda>(_a) , std::forward<rLambda>(_r )); // *** no longer UB, because we're returning by value
}
template<typename rLambda>
ScopeGuard< rLambda > makeScopeGuard(rLambda&& _r)
{
return ScopeGuard< rLambda >( std::forward<rLambda>(_r ));
}
You might be interested in seeing this presentation by Andrei himself on his own taken on how to improve scopedguard with c++11
You can use std::unique_ptr for that purpose which implements the RAII pattern.
For example:
vector<int> v{};
v.push_back(42);
unique_ptr<decltype(v), function<void(decltype(v)*)>>
p{&v, [] (decltype(v)* v) { if (uncaught_exception()) { v->pop_back(); }}};
throw exception(); // rollback
p.release(); // explicit commit
The deleter function from the unique_ptr p rolls the formerly inserted value back, if the scope was left while an exception is active. If you prefer an explicit commit, you can remove the uncaugth_exception() question in the deleter function and add at the end of the block p.release() which releases the pointer. See Demo here.
I use this works like a charm, no extra code.
shared_ptr<int> x(NULL, [&](int *) { CloseResource(); });
There's a chance this approach will be standardized in C++17 or in the Library Fundamentals TS through proposal P0052R0
template <typename EF>
scope_exit<see below> make_scope_exit(EF &&exit_function) noexcept;
template <typename EF>
scope_exit<see below> make_scope_fail(EF && exit_function) noexcept;
template <typename EF>
scope_exit<see below> make_scope_success(EF && exit_function) noexcept;
On first glance this has the same caveat as std::async because you have to store the return value or the destructor will be called immediately and it won't work as expected.
Most of the other solutions involve a move of a lambda, e.g. by using the lambda argument to initialize a std::function or an object of type deduced from the lambda.
Here's one that is quite simple, and allows using a named lambda without moving it (requires C++17):
template<typename F>
struct OnExit
{
F func;
OnExit(F&& f): func(std::forward<F>(f)) {}
~OnExit() { func(); }
};
template<typename F> OnExit(F&& frv) -> OnExit<F>;
int main()
{
auto func = []{ };
OnExit x(func); // No move, F& refers to func
OnExit y([]{}); // Lambda is moved to F.
}
The deduction guide makes F deduce as lvalue reference when the argument is an lvalue.
Without commitment tracking, but extremely neat and fast.
template <typename F>
struct ScopeExit {
ScopeExit(F&& f) : m_f(std::forward<F>(f)) {}
~ScopeExit() { m_f(); }
F m_f;
};
template <typename F>
ScopeExit<F> makeScopeExit(F&& f) {
return ScopeExit<F>(std::forward<F>(f));
};
#define STRING_JOIN(arg1, arg2) STRING_JOIN2(arg1, arg2)
#define STRING_JOIN2(arg1, arg2) arg1 ## arg2
#define ON_SCOPE_EXIT(code) auto STRING_JOIN(scopeExit, __LINE__) = makeScopeExit([&](){code;})
Usage
{
puts("a");
auto _ = makeScopeExit([]() { puts("b"); });
// More readable with a macro
ON_SCOPE_EXIT(puts("c"));
} # prints a, c, b
makeScopeGuard returns a const reference. You can't store this const reference in a const ref at the caller's side in a line like:
const auto& a = RAII::makeScopeGuard( [&]() { myVec.pop_back(); } );
So you are invoking undefined behaviour.
Herb Sutter GOTW 88 gives some background about storing values in const references.
FWIW I think that Andrei Alexandrescu has used a pretty neat syntax in his CppCon 2015's talk about "Declarative Control Flow" (video, slides).
The following code is heavily inspired by it:
Try It Online
GitHub Gist
#include <iostream>
#include <type_traits>
#include <utility>
using std::cout;
using std::endl;
template <typename F>
struct ScopeExitGuard
{
public:
struct Init
{
template <typename G>
ScopeExitGuard<typename std::remove_reference<G>::type>
operator+(G&& onScopeExit_)
{
return {false, std::forward<G>(onScopeExit_)};
}
};
private:
bool m_callOnScopeExit = false;
mutable F m_onScopeExit;
public:
ScopeExitGuard() = delete;
template <typename G> ScopeExitGuard(const ScopeExitGuard<G>&) = delete;
template <typename G> void operator=(const ScopeExitGuard<G>&) = delete;
template <typename G> void operator=(ScopeExitGuard<G>&&) = delete;
ScopeExitGuard(const bool callOnScopeExit_, F&& onScopeExit_)
: m_callOnScopeExit(callOnScopeExit_)
, m_onScopeExit(std::forward<F>(onScopeExit_))
{}
template <typename G>
ScopeExitGuard(ScopeExitGuard<G>&& other)
: m_callOnScopeExit(true)
, m_onScopeExit(std::move(other.m_onScopeExit))
{
other.m_callOnScopeExit = false;
}
~ScopeExitGuard()
{
if (m_callOnScopeExit)
{
m_onScopeExit();
}
}
};
#define ON_SCOPE_EXIT_GUARD_VAR_2(line_num) _scope_exit_guard_ ## line_num ## _
#define ON_SCOPE_EXIT_GUARD_VAR(line_num) ON_SCOPE_EXIT_GUARD_VAR_2(line_num)
// usage
// ON_SCOPE_EXIT <callable>
//
// example
// ON_SCOPE_EXIT [] { cout << "bye" << endl; };
#define ON_SCOPE_EXIT \
const auto ON_SCOPE_EXIT_GUARD_VAR(__LINE__) \
= ScopeExitGuard<void*>::Init{} + /* the trailing '+' is the trick to the call syntax ;) */
int main()
{
ON_SCOPE_EXIT [] {
cout << "on scope exit 1" << endl;
};
ON_SCOPE_EXIT [] {
cout << "on scope exit 2" << endl;
};
cout << "in scope" << endl; // "in scope"
}
// "on scope exit 2"
// "on scope exit 1"
For your usecase, you might also be interested in std::uncaught_exception() and std::uncaught_exceptions() to know whether your exiting the scope "normally" or after an exception has been thrown:
ON_SCOPE_EXIT [] {
if (std::uncaught_exception()) {
cout << "an exception has been thrown" << endl;
}
else {
cout << "we're probably ok" << endl;
}
};
HTH
Here is one I came up with in C++17. It is trivial to port it to C++11 and/or add deactivation option:
template<class F>
struct scope_guard
{
F f_;
~scope_guard() { f_(); }
};
template<class F> scope_guard(F) -> scope_guard<F>;
Usage:
void foo()
{
scope_guard sg1{ []{...} };
auto sg2 = scope_guard{ []{...} };
}
Edit: In same key here is the guard that goes off only "on exception":
#include <exception>
template<class F>
struct xguard
{
F f_;
int count_ = std::uncaught_exceptions();
~xguard() { if (std::uncaught_exceptions() != count_) f_(); }
};
template<class F> xguard(F) -> xguard<F>;
Usage:
void foobar()
{
xguard xg{ []{...} };
...
// no need to deactivate if everything is good
xguard{ []{...} }, // will go off only if foo() or bar() throw
foo(),
bar();
// 2nd guard is no longer alive here
}
Here's another one, now a variation on #kwarnke's:
std::vector< int > v{ };
v.push_back( 42 );
std::shared_ptr< void > guard( nullptr , [ & v ] ( auto )
{
v.pop_back( );
} );
You already chosen an answer, but I'll take the challenge anyway:
#include <iostream>
#include <type_traits>
#include <utility>
template < typename RollbackLambda >
class ScopeGuard;
template < typename RollbackLambda >
auto make_ScopeGuard( RollbackLambda &&r ) -> ScopeGuard<typename
std::decay<RollbackLambda>::type>;
template < typename RollbackLambda >
class ScopeGuard
{
// The input may have any of: cv-qualifiers, l-value reference, or both;
// so I don't do an exact template match. I want the return to be just
// "ScopeGuard," but I can't figure it out right now, so I'll make every
// version a friend.
template < typename AnyRollbackLambda >
friend
auto make_ScopeGuard( AnyRollbackLambda && ) -> ScopeGuard<typename
std::decay<AnyRollbackLambda>::type>;
public:
using lambda_type = RollbackLambda;
private:
// Keep the lambda, of course, and if you really need it at the end
bool committed;
lambda_type rollback;
// Keep the main constructor private so regular creation goes through the
// external function.
explicit ScopeGuard( lambda_type rollback_action )
: committed{ false }, rollback{ std::move(rollback_action) }
{}
public:
// Do allow moves
ScopeGuard( ScopeGuard &&that )
: committed{ that.committed }, rollback{ std::move(that.rollback) }
{ that.committed = true; }
ScopeGuard( ScopeGuard const & ) = delete;
// Cancel the roll-back from being called.
void commit() { committed = true; }
// The magic happens in the destructor.
// (Too bad that there's still no way, AFAIK, to reliably check if you're
// already in exception-caused stack unwinding. For now, we just hope the
// roll-back doesn't throw.)
~ScopeGuard() { if (not committed) rollback(); }
};
template < typename RollbackLambda >
auto make_ScopeGuard( RollbackLambda &&r ) -> ScopeGuard<typename
std::decay<RollbackLambda>::type>
{
using std::forward;
return ScopeGuard<typename std::decay<RollbackLambda>::type>{
forward<RollbackLambda>(r) };
}
template < typename ActionLambda, typename RollbackLambda >
auto make_ScopeGuard( ActionLambda && a, RollbackLambda &&r, bool
roll_back_if_action_throws ) -> ScopeGuard<typename
std::decay<RollbackLambda>::type>
{
using std::forward;
if ( not roll_back_if_action_throws ) forward<ActionLambda>(a)();
auto result = make_ScopeGuard( forward<RollbackLambda>(r) );
if ( roll_back_if_action_throws ) forward<ActionLambda>(a)();
return result;
}
int main()
{
auto aa = make_ScopeGuard( []{std::cout << "Woah" << '\n';} );
int b = 1;
try {
auto bb = make_ScopeGuard( [&]{b *= 2; throw b;}, [&]{b = 0;}, true );
} catch (...) {}
std::cout << b++ << '\n';
try {
auto bb = make_ScopeGuard( [&]{b *= 2; throw b;}, [&]{b = 0;}, false );
} catch (...) {}
std::cout << b++ << '\n';
return 0;
}
// Should write: "0", "2", and "Woah" in that order on separate lines.
Instead of having creation functions and a constructor, you limited to just the creation functions, with the main constructor being private. I couldn't figure out how to limit the friend-ed instantiations to just the ones involving the current template parameter. (Maybe because the parameter is mentioned only in the return type.) Maybe a fix to that can be asked on this site. Since the first action doesn't need to be stored, it's present only in the creation functions. There's a Boolean parameter to flag if throwing from the first action triggers a roll-back or not.
The std::decay part strips both cv-qualifiers and reference markers. But you can't use it for this general purpose if the input type is a built-in array, since it'll apply the array-to-pointer conversion too.
Yet another answer, but I am afraid I find the others all lacking in one way or another. Notably, the accepted answer dates from 2012, but it has an important bug (see this comment). This shows the importance of testing.
Here is an implementation of a >=C++11 scope_guard that is openly available and extensively tested. It is meant to be/have:
modern, elegant, simple (mostly single-function interface and no macros)
general (accepts any callable that respects preconditions)
carefully documented
thin callback wrapping (no added std::function or virtual table penalties)
proper exception specifications
See also the full list of features.