[英]RAII wrapper for function pairs and template specialization
我為C函數對編寫了一個RAII包裝器,用於初始化和釋放資源,在大多數情況下它都很適合我。
#include <GL/glfw.h>
#include <string>
#include <functional>
#include <stdexcept>
template <typename UninitFuncType,
typename SuccessValueType,
SuccessValueType successValue>
class RAIIWrapper
{
public:
template <typename InitFuncType, typename... Args>
RAIIWrapper(InitFuncType initializer,
UninitFuncType uninitializer,
const std::string &errorString,
const Args&... args) :
uninit_func(uninitializer)
{
if (successValue != initializer(args...))
throw std::runtime_error(errorString);
initialized = true;
}
bool isInitialized() const
{
return initalized;
}
~RAIIWrapper()
{
if (initalized)
uninit_func();
}
// non-copyable
RAIIWrapper(const RAIIWrapper &) = delete;
RAIIWrapper& operator=(const RAIIWrapper &) = delete;
private:
bool initalized = false;
std::function<UninitFuncType> uninit_func;
};
using GLFWSystem = RAIIWrapper<decltype(glfwTerminate), decltype(GL_TRUE), GL_TRUE>;
using GLFWWindow = RAIIWrapper<decltype(glfwCloseWindow), decltype(GL_TRUE), GL_TRUE>;
int main()
{
GLFWSystem glfw(glfwInit,
glfwTerminate,
"Failed to initialize GLFW");
}
但是,當一個函數像Enter/LeaveCriticalSection
一樣返回void
時,我不知道如何在這個類中執行它。 我應該專門為SuccessValueType = void
case的類嗎? 或者使用默認模板參數應該做什么?
我想注意一下
您不需要在包裝器類中有關初始化函數的信息。 您只需要了解未初始化功能。
您可以創建函數幫助程序來實例化您的包裝器。
我提出了以下解決方案(我喜歡@ipc異常處理的想法)
template <typename UninitF>
struct RAII_wrapper_type
{
RAII_wrapper_type(UninitF f)
:_f(f), _empty(false)
{}
RAII_wrapper_type(RAII_wrapper_type&& r)
:_f(r._f), _empty(false)
{
r._empty = true;
}
RAII_wrapper_type(const RAII_wrapper_type&) = delete;
void operator=(const RAII_wrapper_type&) = delete;
~RAII_wrapper_type()
{
if (!_empty) {
_f();
}
}
private:
UninitF _f;
bool _empty; // _empty gets true when _f is `moved out` from the object.
};
template <typename InitF, typename UninitF, typename RType, typename... Args>
RAII_wrapper_type<UninitF> capture(InitF init_f, UninitF uninit_f, RType succ,
const char* error, Args... args)
{
if(init_f(args...) != succ) {
throw std::runtime_error(error);
}
return RAII_wrapper_type<UninitF>(uninit_f);
}
template<typename InitF, typename UninitF, typename... Args>
RAII_wrapper_type<UninitF> capture(InitF init_f, UninitF uninit_f, Args... args)
{
init_f(args...);
return RAII_wrapper_type<UninitF>(uninit_f);
}
例:
void t_void_init(int){}
int t_int_init(){ return 1; }
void t_uninit(){}
int main()
{
auto t1 = capture(t_void_init, t_uninit, 7);
auto t2 = capture(t_int_init, t_uninit, 0, "some error");
}
編輯
RAII_wrapper_type
應該有移動語義,我們應該小心地實現它的移動構造函數,以防止uninit_f
多次調用。
我會分開返回檢查和RAII-Wrapping的邏輯
template <typename UninitFuncType>
class RAIIWrapper
{
public:
template <typename InitFuncType, typename... Args>
RAIIWrapper(InitFuncType fpInitFunc,
UninitFuncType fpUninitFunc,
Args&&... args)
: fpUninit(std::move(fpUninitFunc))
{
static_assert(std::is_void<decltype(fpInitFunc(args...))>::value, "ignored return value");
fpInitFunc(std::forward<Args>(args)...);
}
bool isInitialized() const { return true; } // false is impossible in your implementation
~RAIIWrapper() { fpUninit(); } // won't be called if constructor throws
private:
UninitFuncType fpUninit; // avoid overhead of std::function not needed
};
template <typename InitFuncType, typename UninitFuncType, typename... Args>
RAIIWrapper<UninitFuncType>
raiiWrapper(InitFuncType fpInitFunc,
UninitFuncType fpUninitFunc,
Args&&... args)
{
return RAIIWrapper<typename std::decay<UninitFuncType>::type>
(std::move(fpInitFunc), std::move(fpUninitFunc), std::forward<Args>(args)...);
}
template <typename InitFuncType, typename SuccessValueType>
struct ReturnChecker {
InitFuncType func;
SuccessValueType success;
const char *errorString;
ReturnChecker(InitFuncType func,
SuccessValueType success,
const char *errorString)
: func(std::move(func)), success(std::move(success)), errorString(errorString) {}
template <typename... Args>
void operator()(Args&&... args)
{
if (func(std::forward<Args>(args)...) != success)
throw std::runtime_error(errorString);
}
};
template <typename InitFuncType, typename SuccessValueType,
typename Ret = ReturnChecker<InitFuncType, SuccessValueType> >
Ret checkReturn(InitFuncType func, SuccessValueType success, const char *errorString)
{
return Ret{func, success, errorString};
}
我還添加了允許類型扣除的功能。 以下是如何使用它:
auto _ = raiiWrapper(checkReturn(glfwInit, GL_TRUE, "Failed to initialize GLFW"),
glfwTerminate);
由於具有非void返回值的函數對象會導致static_assert失敗,因此以下情況是不可能的:
raiiWrapper(glfwInit, glfwTerminate); // fails compiling
如果您真的想忽略它,可以添加ignoreReturn
函數對象。 另請注意,返回代碼檢查可以像您想要的那樣復雜(例如成功必須是偶數),因為您可以編寫自己的返回代碼檢查器。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.