Search code examples
c++lambdac++11try-catchfinally

Simulating finally block in C++0x


Inspired from the other topic, I wrote this code which simulates a finally block:

#include <cassert>
#include <iostream>

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; }
};

class A{
    int a;
public:
    void start(){
        int a=1;        
        lambda finally = [&]{a=2; std::cout<<"finally executed";}; 
        try{
            assert(a==1);
            //do stuff
        }
        catch(int){
            //do stuff
        }
    }
};
int main() {
    A a;
    a.start();
}

Output (ideone):

finally executed

@Johannes seems to think that its not entirely correct, and commented that:

It can crash if the compiler doesn't elide the temporary in the copy initialization, because then it deletes twice with the same pointer value

I would like to know how exactly. Help me understanding the problem :-)


EDIT:

Problem fixed as:

class lambda{
    base *pbase;
public:
    template<typename TLambda>
    lambda(TLambda l): pbase(new exec<TLambda>(l)){}
    ~lambda() { delete pbase; }

    lambda(const lambda&)= delete;            //disable copy ctor
    lambda& operator=(const lambda&)= delete; //disable copy assignment
};

And then use it as:

//direct initialization, no copy-initialization
lambda finally([&]{a=2;  std::cout << "finally executed" << std::endl; }); 

Complete code : http://www.ideone.com/hsX0X


Solution

  • In this initialization:

    lambda finally = [&]{a=2; std::cout<<"finally executed";};
    

    The implicitly defined copy constructor for lambda may be used. This will just copy the raw pointer pbase which will then be deleted more than once.

    E.g.

    $ g++ -std=c++0x -Wall -Wextra -pedantic -fno-elide-constructors lambdafun.cc 
    $ ./a.out 
    a.out: lambdafun.cc:29: void A::start(): Assertion `a==1' failed.
    finally executedAborted (core dumped)
    

    Actually, your assert firing masks the double delete problem, but this demonstrates the crash I was highlighting.

    $ g++ -std=c++0x -Wall -Wextra -pedantic -fno-elide-constructors -DNDEBUG lambdafun.cc 
    $ ./a.out 
    Segmentation fault (core dumped)