5 ms·
This also works well with a C++ `defer` helper. #define CONCAT_LITERAL(x, y) x ## y #define CONCAT(x, y) CONCAT_LITERAL(x, y) template <typename F
by vortico 9y ago
This also works well with a C++ `defer` helper.
#define CONCAT_LITERAL(x, y) x ## y
#define CONCAT(x, y) CONCAT_LITERAL(x, y)
template <typename F>
struct DeferWrapper {
F f;
DeferWrapper(F f) : f(f) {}
~DeferWrapper() { f(); }
};
template <typename F>
DeferWrapper<F> deferWrapper(F f) {
return DeferWrapper<F>(f);
}
#define defer(code) auto CONCAT(_defer_, __COUNTER__) = deferWrapper([&]() code)
Example of usage:
{
Foo *foo;
if (initializeFoo()) {
warn("Foo could not be initialized");
return;
}
defer({
destroyFoo(foo);
});
Bar *bar;
if (initializeBar()) {
warn("Bar could not be initialized");
return;
}
defer({
destroyBar(bar);
});
}
This way, all initialization and destruction happens in one block of the code on the same level of indentation as everything else. Useful for socket code, file handling, encoding/decoding states, etc.
- tzahola 9y agoYou're turning RAII upside down... Learn to use proper RAII and you won't need this defer hack.
- vortico 9y agoTell that to the APIs I work with, like POSIX sockets/Winsock, etc. You're suggesting that I wrap them all with RAII? That's ridiculous when you have hundreds of C types which are all used just once or twice. Using defer makes elegant/simple C-like code because it uses the C APIs directly and is nearly the same number of lines of code as a C programmer would write without using defer. I fail to see how "learning proper RAII": struct FooWrapper { Foo *foo; FooWrapper(...) { foo = initializeFoo(...); if (!foo) throw FooException(...); } ~FooWrapper() { destroyFoo(foo); } } try { FooWrapper fooWrapper(...); } catch (FooException &e) { ... } is easier than Foo *foo; if (initializeFoo(foo, ...)) { ... } defer({ destroyFoo(foo); }) If this isn't what you meant, could you demonstrate?
- lkjalskdjflkasd 9y agoI can tell by your example you don't know idiomatic C++, keep it mind it takes years to get to that point. Consider using noexcept or just use C.
- yorwba 9y agoYou could use a std::unique_ptr with a custom deleter: http://en.cppreference.com/w/cpp/memory/unique_ptr http://en.cppreference.com/w/cpp/memory/unique_ptr
- vortico 9y agoThis is a reasonable suggestion since the code isn't too bad. std::unique_ptr<Foo, std::function<void(Foo*)>> p(new Foo, [](Foo* p) { if (p) destroyFoo(p); }); // initialize Foo and set to NULL if failed It increases complexity a bit because you no longer have a simple pointer, and you can't allocate on the stack anymore (my example should have declared `Foo foo;` with `destroyFoo(&foo)`, sorry for typo.)
- contravariant 9y agoIf you didn't want a pointer you could have just used something like: template<typename TFoo, typename TDestr> struct FooWrapper { TFoo foo; const TDestr destroyFoo; template<typename TInit> FooWrapper(Tinit initializeFoo, TDestr destroyFoo) : destroyFoo(destroyFoo) { foo = initializeFoo(); if (!foo) throw FooException(...); } ~FooWrapper() { destroyFoo(&foo); } } Although really that should have been part of the "Foo" class itself, but if you need to deal with external code I suppose that might not be possible.
- vortico 9y agoOh yes, I forgot to make it clear that `Foo` is a C struct from a C API. There are tens of thousands of such libraries. An issue with your wrapper is that it is not generic enough and thus must be written for each Foo-like object. That could likely be fixed with more template arguments, but of course there are multiple ways to initialize C objects like - `fooInitialize(Foo foo); // expects that foo is pre-allocated` - `fooInitialize(Foo foo); // allocates and sets the Foo pointer. Annoying, but some libraries do this.` - `Foo fooInitialize(...); // taking certain arguments` A single `defer` wrapper allows you implement custom destruction behavior for each instance, which is useful if you want to set flags or log errors in the destruction process.
- heavenlyblue 9y agoSo with this, you literally had just thrown away the whole concept of RAII...
- vortico 9y agoSee my response to the sister comment
- lkjalskdjflkasd 9y agowhen you fail to learn C++
- jwilk 9y agoSimilar approach was discussed a few months ago: https://news.ycombinator.com/item?id=15523286 https://news.ycombinator.com/item?id=15523286