6 ms·
This seems unnecessary and possibly even harmful. In C++ the destructor handles this as part of the lifespan of the object. Making users of the object encapsul
by albinofrenchy 9y ago
This seems unnecessary and possibly even harmful.
In C++ the destructor handles this as part of the lifespan of the object. Making users of the object encapsulating the resource handle resource cleanup is an antipattern, which is why the standard library does this for you.
- Occivink 9y agoThere's plenty of one-time cases where you don't want to declare an entire class but still enjoy scope-based functions. However, the approach in the post is not very C++-esque, I like this one much more: https://github.com/mawww/kakoune/blob/master/src/utils.hh#L53 https://github.com/mawww/kakoune/blob/master/src/utils.hh#L5...
- Too 9y agostd::unique_ptr has a widely unknown second template argument called deleter. It is designed exactly for this purpose. You just do roughly like this(written from a phone): typedef fdwrapper = unique_ptr<int, close>; fdwrapper fdhandle(open(filename));
- albinofrenchy 9y agoThe only time I can think of where this is useful is when you are trying to be super defensive with possible exceptions being thrown. Otherwise it seems a lot like you can just make the call at the end of scope yourself for the same line count and _much_ more readable code. I guess if you have multiple return paths it's more tenable to use scoped dtors for this but it all really seems questionable to me -- I can't think of a single use-case where it'd be preferable to use something like this rather than a dedicated class.
- loeg 9y agoIt allows you to keep the cleanup next to the allocation/initialization, which is a nice organization. It's easy to ensure you haven't forgotten to clean something up in any exit path. (Obviously, classic RAII has pretty much the same benefit. But it requires a whole class per type, whereas this approach has less boilerplate.)
- beached_whale 9y agoYou can use it for locks where std::lock_guard isn’t what you need, put the mutex.unlock in the scope_guard
- kentonv 9y ago> The only time I can think of where this is useful is when you are trying to be super defensive with possible exceptions being thrown. If your application uses exceptions at all, then the time to be super-defensive about exceptions is all the time. If you don't form the habit, then you'll forget in the cases where it matters. Plus, most programmers are pretty bad at identifying where exceptions might be thrown, because it tends to reduce to the question: "Where will my code have bugs?" Obviously, if you knew, you'd have fixed them... > _much_ more readable code. I'd argue that it's more readable to put state build-up and tear-down next to each other, so that future readers can see and modify them together. If you put them far apart, it's easier for them to skew.
- quietbritishjim 9y agoIt's rare that there is no chance of exceptions being thrown anywhere in a function, especially in the futurer as the code changes.. But even if you're really sure, it's so idiomatic to do cleanup in a destructor in C++ that it looks wrong to leave some cleanup code at the end of a function. Plus, it means you can think about cleanup at the same time you're thinking about creation, then forget about it later; one less thing to think about during the lifetime of the function. I think life is simpler if you just adopt a blanket rule: all cleanup is done in destructors/scoped guards. Occasionally code might be slightly longer than it needs to be, but your life is simpler overall. Plus you can forget the old C rule of only one return from a function that you mentioned. (If you use exceptions anywhere then you already stopped using that rule anyway.)
- alexgartrell 9y agoScopeGuards as imagined here (Facebook's folly library has one) are near-essential for writing reasonable systems code in C++. int fd = socket(...); if (fd < 0) return false; SCOPE_EXIT { ::close(fd); }; if (ioctl(fd, ...) != 0) return false; if (ioctl(fd, ...) != 0) return false; ... return true; You could totally just write the FdWrapper class, but then you're still a user handling resource cleanup, but with more lines.
- ot 9y ago> You could totally just write the FdWrapper class Which, BTW, is exactly what folly::File does :)
- bodyfour 9y ago> You could totally just write the FdWrapper class Yes, and that RAII class would be reusable all of the places you need to close a file descriptor. It's even a handy place to hang more methods that work on the descriptor... for instance if you wanted to abstract those ioctls. That's why I personally never use ScopeGuards in C++. You are correct that writing a single-use RAII class requires a few lines of extra code. However I have found that after I write such a class I often find myself using it over and over.
- butterisgood 9y agoSometimes you need to wrap C APIs. Sure you could put their guts in a class or struct, but you can also use a unique_ptr for some of the same lifetime stuff. Also, defer in Go always uses the function exit, not the scope. Not sure you can do that in C++. Everything seems scoped. Maybe don't do this :-)
- albinofrenchy 9y agoYou usually shouldn't use unique_ptr for C api stuff unless you really only need to free it. Typically though there are custom delete/free API calls you need to make, and to use them with unique_ptr you have to pass that function pointer in as the second argument to the unique_ptr constructor. At the very least you should have a C++ function that gives you the unique_ptr back that does the right thing; but at that point you almost might as well just make a class and give it member functions to map the C calls.
- evincarofautumn 9y agoIf you wanted to do things on function exit, you could have an “ENABLE_DEFER” macro that declares a scope for guards; the “DEFER” macro would register the deferred lambda with that scope, not define a new object, and the destructor of the scope would run all the registered guards. Of course, in my C++ code I would just write a class/struct to wrap any RAII-like logic, not use a “defer”. A marginal amount of extra work that pays off in consistency and readability.