Click here to Skip to main content
13,900,571 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

1.2K views
Posted 16 Mar 2019
Licenced MIT

RAII

, 16 Mar 2019
Rate this:
Please Sign up or sign in to vote.
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:

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:

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

#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;
}

License

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

Share

About the Author

Martin Vorbrodt
Software Developer (Senior)
United States United States
No Biography provided

You may also be interested in...

Comments and Discussions

 
-- There are no messages in this forum --
Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web02 | 2.8.190306.1 | Last Updated 16 Mar 2019
Article Copyright 2019 by Martin Vorbrodt
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid