Ryan L
Ryan L

Reputation: 43

Call a member function automatically in the end of a scope

Is there a way to call a class member function automatically in the end of a scope? Apparently if the class is instantiated within the same scope, clean() can be used in the destructor of it. The question is, can we achieve that if it is instantiated before the scope? In detail:

class foo
{
public:
    void do_something() {}
    void clean() {}
};

int main()
{
    foo c;
    {
        c.do_something();
        // do something
        // call c.clean() automatically
    }
}

Upvotes: 1

Views: 646

Answers (5)

JHBonarius
JHBonarius

Reputation: 11271

std::unique_ptr actually has something like that, in the form of a second parameter you can pass to the constructor, a deleter. The deleter cleans up when the std::unique_ptr object goes out of scope. Normally (i.e. if you don't specify it) it would be std::default_delete that calls delete on the dynamically allocated memory it owns. But you can make it do other things, like this:

#include <cstdio>
#include <memory>

class foo
{
public:
    void do_something() { printf("do_something\n"); }
    void clean() { printf( "clean\n"); }
};

int main()
{
    foo c;
    {
        std::unique_ptr<foo, void(*)(foo*)> scoped_cleaner(&c, [](foo* c) { c->clean(); });
        c.do_something();
    }
}

godbolt

Upvotes: 4

eerorika
eerorika

Reputation: 238351

Only thing that is called automatically at the end of the scope is the destructors of the objects whose lifetime end in that scope. As such, you could have another object local to the inner scope whose destructor calls the function.

A generic solution exists in Boost already:

foo c;
{
    BOOST_SCOPE_EXIT(&c) {
        c.clean();
    };
    c.do_something();
    // do something
}

One caveat with all these answers including mine is that foo::clean() must not throw.

Upvotes: 2

NathanOliver
NathanOliver

Reputation: 180630

Write a do_guard. You can leverage RAII to create a wrapper that will call do on the object you give the wrapper in it's constructor, and then the destructor will call clear. That would look like

struct do_guard
{
    foo& ref;
    do_guard(foo& ref) : ref(ref) { ref.do_something(); }
    ~do_guard() { ref.clean(); }
};

int main()
{
    foo c;
    {
        do_guard guard(c);
        // do something
    } // guard.~do_guard() will call c.clean() automatically
}

Upvotes: 1

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122468

You can make do_something return something that gets its destructor called at the end of the scope:

class foo;
struct cleaner {
    foo& f;
    cleaner(foo& f) : f{f} {}
    ~cleaner();
};


class foo
{
public:
    cleaner do_something() { return *this; }
    void clean() {}
};

cleaner::~cleaner() { f.clean(); }

int main()
{
    foo c;
    {
        auto x = c.do_something();
        // do something           
    }  // x calls c.clean() automatically
}

Upvotes: 0

john
john

Reputation: 87959

Something like this. May not be exactly what you are looking for as it requires you do declare another variable at the start of the scope where you want the clean function to be called.

class CleanFoo
{
public:
    CleanFoo(Foo& r) : ref(r) {}
    ~CleanFoo() { ref.clean(); }
    CleanFoo(const CleanFoo&) = delete;
    CleanFoo& operator=(const CleanFoo&) = delete;
private:
    Foo& ref;
};

int main()
{
    foo c;
    {
        CleanFoo cc(c);
        ...
    } // c.clean() will be called here
    ...
}

Upvotes: 5

Related Questions