Boost logo

Boost :

Subject: Re: [boost] Proposed new RAII Library
From: Lorenzo Caminiti (lorcaminiti_at_[hidden])
Date: 2012-09-12 14:09:38


On Tue, Sep 11, 2012 at 6:40 PM, Andrew Sandoval
<sandoval_at_[hidden]> wrote:
> I'd like to propose a new Boost library for RAII wrapper classes that
> I think could be standardized to help promote the use of RAII. Though
> shared_ptr, scoped_ptr, etc. greatly enhance the use of RAII, I
> propose two additional classes.
>
> The first of these is RAIIFunction (I'm open to other names and coding
> conventions). This one is very simple, but powerful. It's a small
> wrapper (using composition) around std::tr1::function, that simply
> calls the function on scope-exit, in the normal case. Unlike
> Boost.ScopeExit, it uses no macros.

Seems similar to:
http://www.boost.org/doc/libs/1_51_0/libs/scope_exit/doc/html/scope_exit/alternatives.html#scope_exit.alternatives.c__11_lambdas

#include <functional>

struct scope_exit {
    scope_exit(std::function<void (void)> f) : f_(f) {}
    ~scope_exit(void) { f_(); }
private:
    std::function<void (void)> f_;
};

void world::add_person(person const& a_person) {
    bool commit = false;

    persons_.push_back(a_person);
    scope_exit on_exit1([&commit, this](void) { // Use C++11 lambda.
        if(!commit) persons_.pop_back(); // `persons_` via captured `this`.
    });

    // ...

    commit = true;
}

I personally wouldn't need a lib for such a scope_exit class--it's trivial.

> Anything that can be assigned to
> a std::tr1::function<AnyType> taking no parameters will work with it,
> so lambdas are perfect -- and allow for all of the associated benefits
> like capturing variables by reference, etc. It also works with
> std::tr1::bind / boost::bind, with no placeholders.
>
> //
> // RAIIFunction<> Written by Andrew L. Sandoval 2011/2012
> // Include guard here
> #include <functional>
>
> //
> // An RAII class that works with std::tr1::bind and lambdas
> // NOTE: making a const RAIIFunction prevents the use of Invoke()
> // and Cancel() and operator=(fn) which is often appropriate
> // NOTE: m_func copies the original function object, so that const and
> // non-const refs can be passed in, etc.
> template <typename TRet>
> class RAIIFunction
> {
> private:
> std::tr1::function<TRet ()> m_func;
> bool m_bInvoke;
>
> RAIIFunction();
> RAIIFunction(const RAIIFunction &);
> void operator=(const RAIIFunction &);
> public:
> RAIIFunction(const std::tr1::function<TRet ()> &fn) : m_func(fn),
> m_bInvoke(true)
> {
> }
>
> void operator=(const std::tr1::function<TRet ()> &fn)
> {
> if(m_bInvoke)
> {
> m_func();
> }
> m_bInvoke = true;
> m_func = fn;
> }
>
> ~RAIIFunction()
> {
> if(m_bInvoke)
> {
> m_func();
> }
> }
>
> void Cancel()
> {
> m_bInvoke = false;
> }
>
> TRet Invoke(bool bRelease = false)
> {
> m_bInvoke = !bRelease;
> return m_func();
> }
> };
>
> Examples:
> const RAIIFunction<void> closeDB = ([pDB]()
> {
> if(pDB)
> {
> sqlite3_close(pDB);
> }
> });
>
> //
> // while example...
> bool bContinue = true;
> RECORD record = { };
> while(bContinue)
> {
> // Ensure that no matter how the loop wraps (continue or
> fall-through) the next
> // record is grabbed or the loop terminates...
> const RAIIFunction<void> loopAdvance = ([&bContinue]()
> {
> bContinue = SomeCallThatGetsAnotherRecord(record);
> });
>
> // A bunch of record processing here ...
> }
>
> //
> // A convoluted example that shows assignment, etc.
> RAIIFunction<int> mb = std::tr1::bind(MessageBoxW,
> HWND_TOP,
> L"Present pop-up prompt again?",
> L"Test", MB_TOPMOST|MB_YESNO);
> int iMB = mb.Invoke(true);
> if(IDYES != iMB)
> {
> mb = ([]() ->int
> {
> dprintf("Printing what the user didn't wanted popped up"
> " on scope-exit...\n");
> return 0;
> });
>
> --
>
> The other one I call just RAIIWrapper, and it works with static
> deleter functions taking a single argument of the type being delete.
> Since it includes a lot more code (much of which could probably be
> replaced with type_traits code), I'll just show examples of usage
> below to gauge interest. I first used an earlier version of this in
> an environment where the standard function required a goto ErrorExit
> (like Microsoft example driver code in the WDK) to clean-up all of the
> resources. The original concern was that the c++ class would bloat
> the deliverable size. This class proved to be as tight as the goto
> equivalent. It uses one macro that I'll include below. The class
> takes 3 template parameters: 1) The type of the deleter function, 2)
> the deleter function, and 3) a no-delete value that defaults to NULL.
>
> //
> // Simplify usage with a macro:
> #define RAIIDeleter(deleterFunction) decltype(&deleterFunction), deleterFunction
>
> // Example:
> const RAIIWrapper<RAIIDeleter(CloseHandle), INVALID_HANDLE_VALUE>
> hFile = CreateFile(pwzFileName,
> GENERIC_WRITE,
> FILE_SHARE_WRITE,
> NULL,
> CREATE_ALWAYS,
> FILE_ATTRIBUTE_NORMAL,
> NULL);
>
> if(!hFile.IsValid())
> {
> // handle error...
> }
>
> // Similar but using the default no-delete value (NULL):
> const RAIIWrapper<RAIIDeleter(CloseHandle)> hEvent = CreateEvent(NULL,
> FALSE, FALSE, NULL);
>
> --
>
> I've found that using RAII consistently can dramatically improve code
> quality. It forces the coder to think about resource lifetime at
> inception rather than at exit points, etc. Much of what I've shown
> above can be done with scoped_ptr or shared_ptr, but these specialized
> classes put the focus on RAII and resource management.
>
> Please let me know your thoughts. I'd love to see this become part of
> Boost, and hopefully eventually part of the C++ standard library.

HTH,
--Lorenzo


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk