具有可变模板的 acquire/release 的 RAII 模式
RAII pattern for acquire/release with variadic templates
我正在尝试用一个模板 class 替换我所有的 "Acquire/Release" RAII classes(我目前每种资源都有一个)。 acquire的一般形式是,有的类型是Acquire(),有的是Acquire(p1),有的是Acquire(p1, p2)等,Release也是一样。但是,如果资源是使用参数获取的,那么它需要使用相同的参数释放。
我想我可以使用可变参数模板来做到这一点,将参数存储在一个元组中。我当然不喜欢语法。有人可以帮忙吗?
#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;
}
我很确定魔术会是:
subject->Acquire(a...);
由于a
是模板包,调用时需要展开
将元组扩展为可变参数调用需要 integer_sequence
expansion。
除了 pointer/value 差异等一些小问题外,您的主要问题是您不能在没有模板参数的情况下引用 Raii
并且您不会扩展参数参数 pack/tuple.
这是一个工作版本,您可以通过一些额外的完美转发等来改进它。
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);
{
}
}
}
}
我正在尝试用一个模板 class 替换我所有的 "Acquire/Release" RAII classes(我目前每种资源都有一个)。 acquire的一般形式是,有的类型是Acquire(),有的是Acquire(p1),有的是Acquire(p1, p2)等,Release也是一样。但是,如果资源是使用参数获取的,那么它需要使用相同的参数释放。
我想我可以使用可变参数模板来做到这一点,将参数存储在一个元组中。我当然不喜欢语法。有人可以帮忙吗?
#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;
}
我很确定魔术会是:
subject->Acquire(a...);
由于a
是模板包,调用时需要展开
将元组扩展为可变参数调用需要 integer_sequence
expansion。
除了 pointer/value 差异等一些小问题外,您的主要问题是您不能在没有模板参数的情况下引用 Raii
并且您不会扩展参数参数 pack/tuple.
这是一个工作版本,您可以通过一些额外的完美转发等来改进它。
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);
{
}
}
}
}