I know the topic of RAII has been blogged about plenty of times before. Still, I want to present to you my take on it 🙂 Recently I created a policy-based generic RAII template for holding various types of resources (pointers, file handles, mutexes, etc). The nice thing about my implementation is that in order to acquire and release a new type of a resource you only have to define simple

Acquire
and
Release
policy template classes and plug them as parameters to the
RAII
template. Here’s how you can use it with
std::mutex
:

template struct LockPolicy { static void Execute(T t) { t.lock(); } };
template struct UnlockPolicy { static void Execute(T t) { t.unlock(); } };
template using scope_lock = RAII;

std::mutex m;
{
    scope_lock lock(m);
}

In the code above we declare 2 policy classes:

LockPolicy
which calls
.lock();
on type
T
, and
UnlockPolicy
which calls
.unlock();
on type
T
. Next we declare
scope_lock
to be a template which will hold type
T
by reference and apply
LockPolicy::Execute(T t);
and
UnlockPolicy::Execute(T t);
in the constructor and destructor of
RAII
. This way we can use
scope_lock
with any object that has
.lock();
and
.unlock();
methods.

As an exercise in writing policy classes let’s use

RAII
template to hold and automatically
delete
or
delete[]
pointers and pointers to arrays:

template struct NoOpPolicy { static void Execute(T) {} };

template struct PointerReleasePolicy { static void Execute(T ptr) { delete ptr; } };
template struct ArrayReleasePolicy { static void Execute(T ptr) { delete[] ptr; } };

template using ptr_handle_t = RAII;
template using arr_ptr_handle_t = RAII;

{
    ptr_handle_t p1 = new int;
    arr_ptr_handle_t p2 = new int [2];
    
    *p1 = 0xDEADBEEF;
    p2[1] = 0x8BADF00D;
}

First we need a policy that does nothing; let’s call it

NoOpPolicy
. That is because nothing needs to happen to a pointer in the constructor of
RAII
; it’s already allocated. Next we declare two policies:
PointerReleasePolicy
which calls
delete
on type
T
, and
ArrayReleasePolicy
which calls
delete[]
on type
T
. Finally we define
ptr_handle_t
to be a
RAII
template which holds a pointer to
T
, applies
NoOpPolicy::Execute(T t);
in its constructor and
PointerReleasePolicy::Execute(T t);
in its destructor. We do the same for
arr_ptr_handle_t
except using
ArrayReleasePolicy
.

Complete listing:

#include 

template<
    typename T,
    template typename AcquirePolicy,
    template typename ReleasePolicy
>
class RAII
{
public:
    typedef T  val_type;
    typedef T& ref_type;
    
    RAII(val_type h) : m_handle(h) { AcquirePolicy::Execute(m_handle); }
    RAII(const RAII&) = delete;
    RAII(RAII&&) = delete;
    RAII& operator = (const RAII&) = delete;
    ~RAII() { ReleasePolicy::Execute(m_handle); }
    
    constexpr operator ref_type () { return m_handle; }
    constexpr operator ref_type () const { return m_handle; }
    
private:
    val_type m_handle;
};

template struct LockPolicy { static void Execute(T t) { t.lock(); } };
template struct UnlockPolicy { static void Execute(T t) { t.unlock(); } };
template using scope_lock = RAII;

template struct NoOpPolicy { static void Execute(T) {} };

template struct PointerReleasePolicy { static void Execute(T ptr) { delete ptr; } };
template struct ArrayReleasePolicy { static void Execute(T ptr) { delete[] ptr; } };

template using arr_ptr_handle_t = RAII;
template using ptr_handle_t = RAII;

int main(int argc, char** argv)
{
    
    std::mutex m;
    scope_lock lock(m);
 
    ptr_handle_t p1 = new int;
    arr_ptr_handle_t p2 = new int [2];
     
    *p1 = 0xDEADBEEF;
    p2[1] = 0x8BADF00D;

    return 1;
}

7 Replies to “RAII”

  1. I am trying to use the ptr_handle_t pMyClass = new MyClass, but am unable to dereference the pMyClass to get to member functions or data. pMyClass->xxx gives
    error C2819: type ‘RAII’ does not have an overloaded member ‘operator ->’

Leave a Reply to Scott SchindlerCancel reply