Any RAII template in boost or C++0x
Asked Answered
E

6

15

Is there any template available in boost for RAII. There are classes like scoped_ptr, shared_ptr which basically work on pointer. Can those classes be used for any other resources other than pointers. Is there any template which works with a general resources.

Take for example some resource which is acquired in the beginning of a scope and has to be somehow released at the end of scope. Both acquire and release take some steps. We could write a template which takes two(or maybe one object) functors which do this task. I havent thought it through how this can be achieved, i was just wondering are there any existing methods to do it

Edit: How about one in C++0x with support for lambda functions

Eightfold answered 23/1, 2010 at 1:3 Comment(3)
It's lambda, not lambada :) Fixed it for you. ;)Eagan
Shouldn't it be called C++1x now?Hairy
@LiraNuna: Not really, because it's a placeholder name. Its only purpose is to be convenient and ensure that everyone knows what is meant. C++1x is ambiguous because 1) we're not used to the name, and 2) more than one revision of the standard may occur during this decade. C++0x is the one everyone's used to, and there's nothing else it can mean.Eagan
R
13

shared_ptr provides the possibility to specify a custom deleter. When the pointer needs to be destroyed, the deleter will be invoked and can do whatever cleanup actions are necessary. This way more complicated resources than simple pointers can be managed with this smart pointer class.

Rote answered 23/1, 2010 at 1:14 Comment(1)
Custom deleters are great, but what if the type you want managed isn't a pointer? This happens a lot when wrapping C libraries that are used like example_t data; example_new(&data); /* manipuate the data ... */ example_free(&data), and if these objects are stored in some persistent container, then you have to store the managed pointer as well, which would be impractical & wasteful.Resonant
S
8

The most generic approach is the ScopeGuard one (basic idea in this ddj article, implemented e.g. with convenience macros in Boost.ScopeExit), and lets you execute functions or clean up resources at scope exit.

But to be honest, i don't see why you'd want that. While i understand that its a bit annoying to write a class every time for a one-step-aquire and one-step-release pattern, you are talking about multi-step-aquire and -release.
If its taken multiple steps, it, in my opinion, belongs in an appropiately named utility class so that the details are hidden and the code in place (thus reducing error probability).
If you weigh it against the gains, those few additional lines are not really something to worry about.

Slave answered 23/1, 2010 at 1:8 Comment(6)
life would be so much easier if Stroustrop would allow finally into c++ (he flatly refuses to allow it, he feels you should have a class every time)Schmo
To be honest, i rarely feel the need for what the OP mentions because every repeated resource usage pattern should be in a utility class. Added bonus - you don't have to remember the resource handling details.Slave
@pm100: No, you've got it upside down. Finally would mean I'd have to write cleanup code every time I use the class. RAII, relying on the destructor, means I can write the cleanup code once per class. How is finally a good idea again?Eagan
@jalf: Amen! Finally is an abomination!Equivocal
Finally is a good idea when you want to write cleanup code that only occurs in one place, and is inconsistent between different users of the same resource. i.e, never. No, I mean: e.g, when you want to add some quick tracing to something, or write a transaction (which is almost always unique and used in only one place). Obviously you can write a class to represent your transaction, but it can get a bit enterprise-y if you have to do that, hence various scope guards, which are fine (better, even) unless you're used to finally and uncomfortable with learning coding styles.Midinette
if I acquire something as a one off - then I want to use finally to release it. Otherwise I have to write a one off class. I know that for common resource classes I must write wrapper classes (and my code is full of them). I object to being forced to do in every caseSchmo
C
8

A more generic and more efficient (no call through function pointer) version is as follows:

#include <boost/type_traits.hpp>

template<typename FuncType, FuncType * Func>
class RAIIFunc
{
public:
   typedef typename boost::function_traits<FuncType>::arg1_type arg_type;
   RAIIFunc(arg_type p) : p_(p) {}
   ~RAIIFunc() { Func(p_); }
   arg_type & getValue() { return p_; }
   arg_type const & getValue() const { return p_; }
private:
   arg_type p_;
};

Example use:

RAIIFunc<int (int), ::close> f = ::open("...");
Chancelor answered 12/3, 2011 at 23:20 Comment(1)
I put this solution through an actual compile and like it. I did have to make some minor changes but nothing horrible.Enscroll
E
5

I have to admit I don't really see the point. Writing a RAII wrapper from scratch is ridiculously simple already. There's just not much work to be saved by using some kind of predefined wrapper:

struct scoped_foo : private boost::noncopyable {
  scoped_foo() : f(...) {}
  ~scoped_foo() {...}

  foo& get_foo() { return f; }

private:
  foo f;
};

Now, the ...'s are essentially the bits that'd have to be filled out manually if you used some kind of general RAII template: creation and destruction of our foo resource. And without them there's really not much left. A few lines of boilerplate code, but it's so little it just doesn't seem worth it to extract it into a reusable template, at least not at the moment. With the addition of lambdas in C++0x, we could write the functors for creation and destruction so concisely that it might be worth it to write those and plug them into a reusable template. But until then, it seems like it'd be more trouble than worth. If you were to define two functors to plug into a RAII template, you'd have already written most of this boilerplate code twice.

Eagan answered 23/1, 2010 at 1:48 Comment(2)
lambdas, yes thats what i was thinking about . sorry for not mentioning this in my question ( it was in my mind though)Eightfold
As far as I know, C++0x doesn't define such a template. Perhaps because it's so simple to do there's not much point. Perhaps they're waiting for Boost or other third parties to define one, and let it prove itself in the real world before standardizing it. At the moment, we don't yet know if 1) it's worth the effort and 2) if there are any non-obvious gotchas that the template should be able to handle.Eagan
O
5

I was thinking about something similar:

template <typename T>
class RAII {
    private:
        T (*constructor)();
        void (*destructor)(T);
    public:
        T value;
        RAII(T (*constructor)(), void (*destructor)(T)) : 
                    constructor(constructor), 
                    destructor(destructor) {
            value = constructor();
        }
        ~RAII() {
            destructor(value);
        }
};

and to be used like this (using OpenGL's GLUquadric as an example):

RAII<GLUquadric*> quad = RAII<GLUquadric*>(gluNewQuadric, gluDeleteQuadric);
gluSphere(quad.value, 3, 20, 20)
Outsell answered 10/9, 2010 at 8:33 Comment(0)
D
4

Here's yet another C++11 RAII helper: https://github.com/ArtemGr/libglim/blob/master/raii.hpp

It runs a C++ functor at destruction:

auto unmap = raiiFun ([&]() {munmap (fd, size);});
Dorinda answered 29/6, 2013 at 12:23 Comment(7)
A very dangerous implementation: It requires that copy/move elision occurs. If it doesn't occur, the destructor of the RAIIFun object runs more than once!Marinmarina
@Marinmarina It uses Perfect Forwarding and most certainly does not require an elision to work. If you're seeing some corner case were it runs the functor twice, please provide MCVE.Dorinda
It is difficult to provide a MCVE as copy elision is not a guarantee, but an option for the compiler. The problem occurs when the function raiiFun returns an object of type RAIIFun. If the copy/move is not elided, the destructor is called before the return completes. There is another copy/move that may or may not be elided when the local variable is initialized from the function result: if the temporary is not elided, its destructor is called. In C++17 we get guaranteed copy elision and the problem disappears.Marinmarina
@Marinmarina IIRC, there is no copy in return RAIIFun<Fun> (fun), because RAIIFun<Fun> (fun) is already an rvalue. Copy elision handles a different use case, for example auto f = RAIIFun<Fun> (fun); return f;. When you return an rvalue there is no need for copy elision to kick in. Neither there is a copy in operator =, because we use the = default; constructors.Dorinda
Though it's a fact that with -std=c++11 -O0 -fno-elide-constructors gcc calls the functor thrice, so it's indeed possible to shoot oneself in the foot with this.Dorinda
Right. You are at the mercy of the compiler.Marinmarina
Since C++17, copy ellision is guaranteed.Esparto

© 2022 - 2024 — McMap. All rights reserved.