Оболочка RAII для пар функций и специализированная специализация

Я написал оболочку RAII для пар функций C, которые инициализируют и освобождают ресурсы, и в большинстве случаев она мне помогает.

#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");
}
</decltype(glfwclosewindow),></decltype(glfwterminate),></uninitfunctype></typename></typename></stdexcept></functional></string></gl>

Однако, скажем, когда функция возвращает void как Enter/LeaveCriticalSection, я не уверен, как это сделать и сделать это в этом классе. Должен ли я специализировать класс для случая SuccessValueType = void? Или что-то с параметром шаблона по умолчанию должно быть?

2 ответа

Хочу отметить, что

  • Вам не нужна информация о вашей функции инициализации в вашем классе-оболочке. Вам нужно знать только функцию uninitialization.

  • Вы можете создавать вспомогательные функции для создания экземпляра вашей обертки.

Я придумал следующее решение (мне понравилась идея обработки исключений @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,="" 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);
}
</uninitf></uninitf></typename></uninitf></uninitf></typename></typename>

Пример:

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");
}

Edit

RAII_wrapper_type должен иметь семантику перемещения, и мы должны тщательно реализовать его конструктор перемещения, чтобы предотвратить uninit_f от вызова несколько раз.


Я бы разделил логику return-Checking и 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,="" ret="ReturnChecker<InitFuncType," successvaluetype=""> >
Ret checkReturn(InitFuncType func, SuccessValueType success, const char *errorString)
{
 return Ret{func, success, errorString};
}
</typename></args></typename...></typename></args></typename></uninitfunctype></typename></args></decltype(fpinitfunc(args...))></typename></typename>

Я также добавил функции, позволяющие выводить тип. Вот как его использовать:

auto _ = raiiWrapper(checkReturn(glfwInit, GL_TRUE, "Failed to initialize GLFW"),
 glfwTerminate);

Поскольку объект функции, который имеет невоичное возвращаемое значение, приводит к сбою static_assert, невозможно выполнить следующее:

raiiWrapper(glfwInit, glfwTerminate); // fails compiling

Если вы действительно хотите игнорировать его, вы можете добавить объект функции ignoreReturn. Также обратите внимание, что проверка кода возврата может быть такой же сложной, как вы хотите (например, успех должен быть четным числом), так как вы можете написать свою собственную проверку кода возврата.

licensed under cc by-sa 3.0 with attribution.