这是一个尝试:
#include <utility>
#include <type_traits>
#include <cstddef>
包装函数的更友好的方式。我将签名样板移至 this template
,而不是弄乱下面的实际 RAII 类。这也允许在下面的 RAII 类中使用完整的函数对象以及函数:
template< typename FuncSig, FuncSig func >
struct Functor {
template<typename... Args>
auto operator()(Args&&... args) const
-> decltype( func(std::forward<Args>(args)...) )
{ return ( func(std::forward<Args>(args)...) ); }
};
除了基本功能之外,需要的一项操作是将句柄“空”,允许存在无效句柄,并允许移动句柄。 Zeroer
是我的“null”句柄的默认函数对象:
struct Zeroer {
template<typename T>
void operator()( T& t ) const {
t = 0;
}
};
RAII_handle
她自己。您将创建和销毁签名打包到其中,并将构造转发到基础数据。 .close()
让您RAII_handle
提前关闭,这是实践中的常见要求。operator*
您通过or访问底层数据operator->
,虽然这使它看起来像指针,RAII_handle
但不遵守指针语义。它是一种只能移动的类型。
template< typename T, typename Creator, typename Destroyer, typename Nuller=Zeroer >
struct RAII_handle {
RAII_handle( std::nullptr_t ):
data()
{
Nuller()(data);
}
RAII_handle( RAII_handle const& ) = delete;
RAII_handle( RAII_handle && o ):data(std::move(o.data)) {
Nuller()(o.data);
}
RAII_handle& operator=( RAII_handle const& ) = delete;
RAII_handle& operator=( RAII_handle && o ) {
data = std::move(o.data);
Nuller()(o.data);
return *this;
}
template<typename... Args>
RAII_handle( Args&&... args ):
data( Creator()(std::forward<Args>(args)...) )
{}
auto close()->decltype( Destroyer()(std::declval<T&>()) ) {
auto retval = Destroyer()(data);
Nuller()(data);
return retval;
}
~RAII_handle() {
close();
}
T& get() { return data; }
T const& get() const { return data; }
T& operator*() { return get(); }
T const& operator*() const { return get(); }
T* operator->() { return &get(); }
T const* operator->() const { return &get(); }
private:
T data;
};
现在,一些测试代码。我的文件句柄将是unsigned char
,并且打开/关闭将简单地测试事情是否正常。
#include <iostream>
typedef unsigned char HANDLE;
HANDLE CreateFile( char const* name ) {
std::cout << name << "\n";
return 7;
}
bool CloseFile( HANDLE h ) {
if (h) {
--h;
std::cout << (int)h << "\n";
return true;
} else {
std::cout << "already closed\n";
return true;
}
}
一旦你有了你的打开/关闭函数或函数对象,下面是你如何创建类型的FileHandle
:
typedef RAII_handle< HANDLE, Functor< HANDLE(*)( char const* ), CreateFile >, Functor< bool(*)(HANDLE), CloseFile > > FileHandle;
您可以通过简单地创建一个转发到固定函数名称而不是固定函数指针的函数对象来支持整个重载集。基本上采取Functor
上面,删除template
签名和指针,并用func
你的函数名的实际使用替换使用。
突然间,您的函数对象代表不调用一个函数,而是调用整个重载集。
更高级的工作甚至可以支持多个函数,允许一个函数对象支持调用其中一个CreateFile
或CreateFileEx
取决于传入的参数。
这是我们的简单测试代码:
int main() {
FileHandle bob("hello.txt");
HANDLE value = *bob; // get the HANDLE out of the FileHandle
bob.close(); // optional, to close early
}
要求:你CloseFile
必须接受Nuller()(std::declval<T&>())
并且不做坏事。默认值Nuller()(...)
只是将零分配给 your T
,这适用于许多句柄类型。
它支持移动语义,允许您从函数中返回这些,但我没有包含Copier
参数(我希望任何可以复制的 RAII 对象都需要该参数)。
代码略有不同的实时示例。