I am trying to mimic a finally like effect. So i thought i should run a quick dirty test.
The idea was to use Most Important const to stop destruction and to put the finally block in a lambda. However apparently i did something wrong and its being called at the end of MyFinally(). How do i solve this problem?
#include <cassert>
template<typename T>
class D{
T fn;
public:
D(T v):fn(v){}
~D(){fn();}
};
template<typename T>
const D<T>& MyFinally(T t) { return D<T>(t); }
int d;
class A{
int a;
public:
void start(){
int a=1;
auto v = MyFinally([&]{a=2;});
try{
assert(a==1);
//do stuff
}
catch(int){
//do stuff
}
}
};
int main() {
A a;
a.start();
}
My Solution code (Note: You can not have two finally in the same block. as expect. But still kind of dirty)
#include <cassert>
template<typename T>
class D{
T fn; bool exec;
public:
D(T v):fn(v),exec(true){}
//D(D const&)=delete //VS doesnt support this yet and i didnt feel like writing virtual=0
D(D &&d):fn(move(d.fn)), exec(d.exec) {
d.exec = false;
}
~D(){if(exec) fn();}
};
template<typename T>
D<T> MyFinally(T t) { return D<T>(t); }
#define FINALLY(v) auto OnlyOneFinallyPlz = MyFinally(v)
int d;
class A{
public:
int a;
void start(){
a=1;
//auto v = MyFinally([&]{a=2;});
FINALLY([&]{a=2;});
try{
assert(a==1);
//do stuff
}
catch(int){
FINALLY([&]{a=3;}); //ok, inside another scope
try{
assert(a==1);
//do other stuff
}
catch(int){
//do other stuff
}
}
}
};
void main() {
A a;
a.start();
assert(a.a==2);
}
Funny enough, if you remove the & in MyFinally in the original code it works -_-.
// WRONG! returning a reference to a temporary that will be
// destroyed at the end of the function!
template<typename T>
const D<T>& MyFinally(T t) { return D<T>(t); }
You can fix it my introducing a move constructor
template<typename T>
class D{
T fn;
bool exec;
public:
D(T v):fn(move(v)),exec(true){}
D(D &&d):fn(move(d.fn)), exec(d.exec) {
d.exec = false;
}
~D(){if(exec) fn();}
};
And then you can rewrite your toy
template<typename T>
D<T> MyFinally(T t) { return D<T>(move(t)); }
Hope it helps. No "const reference" trick is needed when you work with auto
. See here for how to do it in C++03 with const references.
Your code and Sutter's are not equivalent. His function returns a value, yours returns a reference to an object that will be destroyed when the function exits. The const reference in the calling code does not maintain the lifetime of that object.
The problem stems from the use of a function maker, as demonstrated by Johannes.
I would argue that you could avoid the issue by using another C++0x facility, namely std::function
.
class Defer
{
public:
typedef std::function<void()> Executor;
Defer(): _executor(DoNothing) {}
Defer(Executor e): _executor(e) {}
~Defer() { _executor(); }
Defer(Defer&& rhs): _executor(rhs._executor) {
rhs._executor = DoNothing;
}
Defer& operator=(Defer rhs) {
std::swap(_executor, rhs._executor);
return *this;
}
Defer(Defer const&) = delete;
private:
static void DoNothing() {}
Executor _executor;
};
Then, you can use it as simply:
void A::start() {
a = 1;
Defer const defer([&]() { a = 2; });
try { assert(a == 1); /**/ } catch(...) { /**/ }
}
Well the problem is explained by others, so I will suggest a fix, exactly in the same way Herb Sutter has written his code (your code is not same as his, by the way):
First, don't return by const reference:
template<typename T>
D<T> MyFinally(T t)
{
D<T> local(t); //create a local variable
return local;
}
Then write this at call site:
const auto & v = MyFinally([&]{a=2;}); //store by const reference
This became exactly like Herb Sutter's code.
Demo : http://www.ideone.com/uSkhP
Now the destructor is called just before exiting the start()
function.
A different implementation which doesn't use auto keyword anymore:
struct base { virtual ~base(){} };
template<typename TLambda>
struct exec : base
{
TLambda lambda;
exec(TLambda l) : lambda(l){}
~exec() { lambda(); }
};
class lambda{
base *pbase;
public:
template<typename TLambda>
lambda(TLambda l): pbase(new exec<TLambda>(l)){}
~lambda() { delete pbase; }
};
And use it as:
lambda finally = [&]{a=2; std::cout << "finally executed" << std::endl; };
Looks interesting?
Complete demo : http://www.ideone.com/DYqrh
You could return a shared_ptr:
template<typename T>
std::shared_ptr<D<T>> MyFinally(T t) {
return std::shared_ptr<D<T>>(new D<T>(t));
}