Click here to Skip to main content
15,912,977 members
Articles / Programming Languages / C++
Tip/Trick

RAII

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
16 Mar 2019MIT1 min read 2.3K  
RAII

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:

C++
template<typename T> struct LockPolicy { static void Execute(T t) { t.lock(); } };
template<typename T> struct UnlockPolicy { static void Execute(T t) { t.unlock(); } };
template<typename T> using scope_lock = RAII<T&, LockPolicy, UnlockPolicy>;

std::mutex m;
{
    scope_lock<std::mutex> 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:

C++
template<typename T> struct NoOpPolicy { static void Execute(T) {} };

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

template<typename T> using ptr_handle_t = RAII<T*, NoOpPolicy, PointerReleasePolicy>;
template<typename T> using arr_ptr_handle_t = RAII<T*, NoOpPolicy, ArrayReleasePolicy>;

{
    ptr_handle_t<int> p1 = new int;
    arr_ptr_handle_t<int> 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

C++
#include <mutex>

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

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

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

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

template<typename T> using arr_ptr_handle_t = RAII<T*, NoOpPolicy, ArrayReleasePolicy>;
template<typename T> using ptr_handle_t = RAII<T*, NoOpPolicy, PointerReleasePolicy>;

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

    return 1;
}
This article was originally posted at https://vorbrodt.blog/2019/02/02/raii

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --