Search code examples
c++variadic-templatesraii

RAII pattern for acquire/release with variadic templates


I'm attempting to replace all of my "Acquire/Release" RAII classes (I have one for each kind of resource at the moment) with a single templated class. The general form of acquire is that some types are Acquire(), some are Acquire(p1), some are Acquire(p1, p2), etc. The same is true of Release. But if a resource is Acquired with parameters then it needs to be released with those same parameters.

I think I can do this with variadic templates, storing the arguments in a tuple. I've fallen down on the syntax of course. Can anyone assist?

#include <tuple>

template<class T, typename... Args>
class Raii
{
public:

    Raii(T * s, Args&& ... a) : subect(s), arguments(a)
    {
        subject->Acquire(arguments);
    }

    ~Raii()
    {
        subject->Release(arguments);
    }

private:

    T subject;
    std::tuple<Args...> arguments;
};

class Framebuffer
{
public:

    void Acquire() {}
    void Release() {}
};

class Sampler
{
public:

    void Acquire(int channel) {}
    void Release(int channel) {}
};

class Buffer
{
public:

    void Acquire(int target, int location) {}
    void Release(int target, int location) {}
};

int main(void)
{
    Framebuffer f;
    Sampler s;
    Buffer b;

    auto b1 = Raii(&f);
    {
        auto b2 = Raii(&s, 10);
        {
            auto b3 = Raii(&b, 10, 20);
            {

            }
        }
    }
    return 0;
}

Solution

  • Apart from a few minor things like pointer/value discrepancies, your main issues are that you can't refer to Raii without template arguments and you don't expand the argument parameter pack/tuple.

    Here is a working version which you could improve with some extra sprinklings of perfect forwarding etc.

    template<class T, typename... Args>
    class Raii
    {
    public:
    
        Raii(T & s, Args&& ... a) : subject(s), arguments(a...)
        {
            //expand a
            subject.Acquire(a...);
        }
    
        //helper to expand tuple
        //requires std::index_sequence and friends from C++14
        //if you are limited to C++11, you can find implementations online
        template <std::size_t... Idx>
        void release(std::index_sequence<Idx...>) 
        {
            subject.Release(std::get<Idx>(arguments)...);
        }
    
        ~Raii()
        {
            //yay, index trick
            release(std::index_sequence_for<Args...>{});
        }
    
    private:
    
        T &subject;
        std::tuple<Args...> arguments;
    };
    
    //helper function so that we can deduce the Raii template args
    template <class T, typename... Args>
    Raii<T,Args...> make_raii (T & t, Args&&... args) {
        return {t, std::forward<Args>(args)...};   
    }
    
    // Framebuffer etc.
    
    int main()
    {
        Framebuffer f;
        Sampler s;
        Buffer b;
    
        //use make_raii instead of Raii
        auto b1 = make_raii(f);
        {
            auto b2 = make_raii(s, 10);
            {
                auto b3 = make_raii(b, 10, 20);
                {
    
                }
            }
        }
    }
    

    Live Demo