5

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;
}
Robinson
  • 9,666
  • 16
  • 71
  • 115
  • possible duplicate of [Variadic template templates and perfect forwarding](http://stackoverflow.com/questions/6486432/variadic-template-templates-and-perfect-forwarding) – Bartek Banachewicz Sep 24 '15 at 12:03
  • It does touch on perfect forwarding but this is a far, far simpler practical example of it than what's described on that thread. So I vote not to close, at least until it works. – Robinson Sep 24 '15 at 12:07

2 Answers2

3

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

TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • Interesting, I didn't know `index_sequence` made it into C++14. Good answer. – Bartek Banachewicz Sep 24 '15 at 12:12
  • Yes I fixed the pointer/value issue. Unfortunately I have C++11 so no index_sequence. I found a C++11 implementation on github though, so may use that. https://gist.github.com/doicanhden/7249956, though this implementation actually gives me " fatal error C1001: An internal error has occurred in the compiler" (Visual Studio 2013). – Robinson Sep 24 '15 at 12:13
  • @Robinson Yay, compiler bugs. It seems to [work fine](http://coliru.stacked-crooked.com/a/c95f162cd89dbfd7) in Clang. I'll see if I can find a VS2013 compatible one. – TartanLlama Sep 24 '15 at 12:17
  • There are a few solutions [here](http://stackoverflow.com/questions/17424477/implementation-c14-make-integer-sequence) which might work for you. – TartanLlama Sep 24 '15 at 12:20
  • 1
    Yes, Khurshid's answer for integer_sequence worked. And the whole thing works. Thank you. – Robinson Sep 24 '15 at 12:23
1

I'm pretty sure the magic would be:

subject->Acquire(a...);

Since a is a template pack, you need to expand it for the call.

Expanding a tuple into a variadic call requires integer_sequence expansion.

Community
  • 1
  • 1
Bartek Banachewicz
  • 38,596
  • 7
  • 91
  • 135
  • Ahah! Though it says `use of class requires template argument list' (where I'm instantiating the Raii objects). Why can't it deduce the template argument list from the constructor parameters? – Robinson Sep 24 '15 at 12:05
  • @Robinson because C++. Use a helper function (`make_raii`). This is the same problem that happens with `make_tuple` and `make_pair` etc. – Bartek Banachewicz Sep 24 '15 at 12:07