Windows HANDLE RAII管理,如果返回bool而不是句柄怎么办?

时间:2017-03-07 14:05:55

标签: c++ raii

此前:

Proper way close WinAPI HANDLEs (avoiding of repeated closing)

我的问题是:如果CreateFile返回一个bool而不是HANDLE,并且输出有一个指针怎么办?发明的例子:

HANDLE handle;

if (CreateFile(&handle, "filename", ...) == true) {
 //...
}

使用提供的RAII类在C ++中使用它有一个很好的语法吗?这样我就可以将我的对象作为第一个参数而不是指针。

4 个答案:

答案 0 :(得分:0)

你想要的是一个调用CreateFile的函数,如果它返回false则抛出,并返回句柄。类似的东西:

HANDLE createFileOrThrow(const std::string& filename, ... )
{
    HANDLE handle;
    if (!CreateFile(&handle, filename.c_str(), ...) {
        throw suitable_exception_type();
    }
    return handle;
}

HandleWrapper<KernelHandleTraits> hFile(CreateFileOrThrow("filename"));

在这个特定示例中,您可以将函数设置为您立即调用的lambda,但您可能希望经常这样做。

可替换地:

HandleWrapper<KernelHandleTraits> createFileOrThrow(const std::string& filename, ... )
{
    HANDLE handle;
    if (!CreateFile(&handle, filename.c_str(), ...) {
        throw suitable_exception_type();
    }
    return HandleWrapper<KernelHandleTraits>(handle);
}

HandleWrapper<KernelHandleTraits> hFile = CreateFileOrThrow("filename");

最后一个选项确实需要HandleWrapper才能拥有移动构造函数。

答案 1 :(得分:0)

在这种情况下,我只需向RAII类添加一个覆盖operator&,例如:

template< class traits >
class HandleWrapper {
...
public:
    ...
    traits::HandleType* operator&() { return &FHandle; }
};

然后像这样使用它:

HandleWrapper<KernelHandleTraits> handle;

if (CreateFile(&handle, "filename", ...) == true) {
    ...
}

答案 2 :(得分:0)

你可以用其他人提到的operator&重载来实现这个目标,但是这个风险充满了首先使用包装器的目的。

考虑如何使用假设的HandleWrapper,其简单的重载operator&只返回指向内部句柄的指针:

HandleWrapper<KernelHandleTraits> handle;

if (CreateFile(&handle, "first", ...) == true) {
  // process the first file...
}
if (CreateFile(&handle, "second", ...) == true) {
  // process the second file...
}

这会将句柄泄漏到第一个文件,因为operator&重载允许您执行后门分配。请注意,包装器类没有专门的赋值运算符,因此在完成第一个资源后,它不必处理重新使用包装器跟踪第二个资源的复杂性。

你可以扩展包装器,以便在它已经包含资源的情况下做更聪明的事情,但是这个类变得更复杂并且你对RAII的想法越来越远。

如果我们分裂头发,引用的HandleWrapper代码应用RRID(资源释放是破坏)而不是RAII(资源分配是初始化)。

  • RRID类是一个包装器,您可以将已分配资源的所有权转移到(以确保它稍后发布)。
  • RAII类的生命周期与其包装的资源完全相同。它在资源出现时拥有资源,并在资源释放时死亡。

如果你有一个函数将资源返回为&#34;输出参数,&#34;你可以写一个适配器功能:

HANDLE CreateFileAdapter(const char * filename, ...) {
  HANDLE handle;
  return CreateFile(&handle, "filename", ...) ? handle : INVALID_HANDLE_VALUE;
}

然后,您可以通过调用适配器函数来原样使用HandleWrapper:

HandleWrapper<KernelHandleTraits> handle(CreateFileAdapter("filename", ...));

严格的RAII解决方案会将资源的分配封装在构造函数中,就像它在析构函数中封装解除分配一样。您可以在RRID包装器和适配器函数的背面构建RAII包装器:

class FileHandleWrapper : public HandleWrapper<KernelHandleTraits> {
  public:
    FileHandleWrapper(const char * file_name, ...) :
      HandleWrapper(CreateFileAdapter(file_name, ...) {}

  private:
    static HANDLE CreateFileAdapter(const char *file_name, ...) {
      HANDLE handle;
      return CreateFile(&handle, "filename", ...) ?
          handle : INVALID_HANDLE_VALUE;
    }
};

请注意,我已将适配器功能设为私有静态,以确保没有人以不安全的方式使用它。

答案 3 :(得分:-1)

Windows API很好,但你看一下Boost library for filesystem operations。它是跨平台的这种操作的抽象。