0

Here is fantasy code demonstrating the problem I'm trying to solve (see the comment // PROBLEM -); basically, how to make sure to detach a bunch of resource handles after doing something potentially throwing.

#include <initializer_list>

// from a C-style API
extern int create_thingie(); // returns non-zero resource handle
extern int create_widget(); // returns non-zero resource handle
extern void attach_thingie(int widget_handle, int thingie_handle);
extern void detach_thingie(int widget_handle, int thingie_handle);
extern void delete_thingie(int handle); // deletes a thingie unless still attached
extern void delete_widget(int handle);

// RAII wrapper for thingies
class thingie {
    int resource_handle;
public:
    thingie() : resource_handle{create_thingie()} {}
    thingie(const thingie&)=delete;
    thingie& operator=(const thingie&)=delete;
    thingie(thingie&& moved_from)
        : resource_handle{moved_from.resource_handle}
    {
        moved_from.resource_handle = 0;
    }
    thingie& operator=(thingie&&)=delete;
    ~thingie() { delete_thingie(this->resource_handle); }
    inline int get_handle() const
    {
        return this->resource_handle;
    }
};

// RAII wrapper for widgets
// here identical to thingie, but not in my use-case
class widget {
    int resource_handle;
public:
    widget() : resource_handle{create_widget()} {}
    widget(const widget&)=delete;
    widget& operator=(const widget&)=delete;
    widget(widget&& moved_from)
        : resource_handle{moved_from.resource_handle}
    {
        moved_from.resource_handle = 0;
    }
    widget& operator=(widget&&)=delete;
    ~widget() { delete_widget(this->resource_handle); }
    inline int get_handle() const
    {
        return this->resource_handle;
    }
};

class foo {
    widget underlying_widget;
public:
    foo(std::initializer_list<thingie> thingies);
    inline int get_underlying_handle() const
    {
        return this->underlying_widget.get_handle();
    }
};

foo::foo(std::initializer_list<thingie> thingies)
{
    for (thingie const& t : thingies)
        attach_thingie(this->get_underlying_handle(), t.get_handle());
    // do some potentially throwing things
    // PROBLEM - thingies might not get detached, causing resource leak
    for (thingie const& t : thingies)
        detach_thingie(this->get_underlying_handle(), t.get_handle());
}

int main()
{
    foo f1{thingie{}, thingie{}};
    return 0;
}

My first thought was to create a local class in foo::foo to represent an attachment (or array of attachments) which makes calls to attach_thingie in its constructor and detach_thingie in its destructor, but I'm not sure how to do so in a zero-overhead way compared to just running the detaching for-loop in all routes out of foo::foo (i.e. using try/catch (...)).

holomenicus
  • 151
  • 5
  • 1
    Smells like premature optimization. Just use a catch block. Calling destructors during stack unwinding should not be faster than the catch block. If in doubt, measure. – Quimby Aug 31 '20 at 21:43
  • @Quimby I see what you mean, maybe more like premature factoring, just because I'm overthinking avoiding writing 2 identical detaching for loops. Unless, is there a way to use a catch block without 2 identical loops? – holomenicus Aug 31 '20 at 22:09
  • The loops are not identical though, right? If you really want, you refactor the loop into a method and use function pointers to call the right functions, or a simple boolean parameter for attach/detach. Thus keeping the logic inside one function. – Quimby Sep 01 '20 at 05:14

0 Answers0