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 (...)
).