是否有适当的“手柄所有权”?

时间:2013-02-14 15:22:02

标签: c++ c++11 handle raii ownership-semantics

除了指针之外,

Handles具有正确的语义。所以对我来说这样的例子(摘自Rule of Zero):

class module {
public:
    explicit module(std::wstring const& name)
    : handle { ::LoadLibrary(name.c_str()), &::FreeLibrary } {}

    // other module related functions go here

private:
    using module_handle = std::unique_ptr<void, decltype(&::FreeLibrary)>;

    module_handle handle;
};

使用unique_ptr作为句柄的'所有权'是一个糟糕的例子。首先,它利用句柄类型的内部知识,并使用它来使unique_ptr基本类型为“不透明”句柄类型构建。

句柄可以是任何类型,它们可以是指针,它们可以是索引或谁知道。最重要的是,你手头的东西(例如大多数C API)是句柄及其资源释放功能。

在句柄语义中是否正常工作是否存在正确的'包中所有权'?我的意思是,已经公开供一个人使用?

对我来说,unique_ptr等。人。不起作用,我必须对句柄类型 做出不必要的假设,当我想要的只是通过opaque句柄类型获得'所有权在一个包'并释放它功能,单独。

对于在句柄类型内部进行对等来构造此信息是没有意义的。它是一个手柄,它应该没关系。

我在这里引用another question's answer中其他SO用户的感受:

  

创建一个特定的“智能指针”类,不会花费很长时间。不要滥用   图书馆类。句柄语义与a语言完全不同   C ++指针;一方面,取消引用一个HANDLE是没有意义的。

     

使用自定义智能句柄类的另一个原因 - NULL不会   总是意味着一个空手柄。有时它是INVALID_HANDLE_VALUE,   这是不一样的。

声明:

这个问题在此基础上重新制定并建立:

4 个答案:

答案 0 :(得分:2)

类型unique_ptr不如短语“handle”,是的。但为什么不应该呢?只是您的“句柄”示例之一(例如,整数索引),与unique_ptr一样通用。您无法将一种特定类型的句柄与“所有句柄”进行比较。

如果你想要一个单独的,具体的C ++类型(或类型模板),它是一个句柄而没有实际定义任何特定的处理语义,那么...我无法帮助你。我不认为任何人都可以做到。

答案 1 :(得分:0)

答案 2 :(得分:0)

这是一个“类型伪装”的玩具示例我很开心,这也与讨论有关。我会把它留在这里作为参考(我不对它造成的任何伤害负责!)。

#ifndef _DISGUISE_HPP_
#define _DISGUISE_HPP_

#include <cassert>
#include <utility>
#include <iostream>

namespace disguise
{
    template<typename T>
    struct default_release { void operator()(const T &) const { std::cout << "I'M DUMB" << std::endl; } };

    template<typename T>
    struct default_release<T *> { void operator()(T *p) const { std::cout << "I'M SMART" << std::endl; delete p; } };

    template<typename R>
    struct default_release<R ()> { void operator()(R (*f)()) const { std::cout << "I'M A SCOPE-EXIT FUNCTION" << std::endl; f(); } };

    template<typename R>
    struct default_release<R (*)()> { void operator()(R (*f)()) const { std::cout << "I'M A SCOPE-EXIT FUNCTION POINTER" << std::endl; f(); } };

    template<typename F, F> struct releaser;

    //template<typename R, typename P, R F(P)> struct releaser_impl_f { void operator()(P v) const { F(v); } };
    template<typename R, typename P, R (*F)(P)> struct releaser_impl_p { void operator()(P v) const { F(v); } };

    //template<typename R, typename P, R F(P)> struct releaser<R (P), F> : releaser_impl_f<R, P, F> {};
    template<typename R, typename P, R (*F)(P)> struct releaser<R (*)(P), F> : releaser_impl_p<R, P, F> {};

    #define RELEASER(f) disguise::releaser<decltype(f), (f)>

    template<typename T, typename F>
    class unique_impl
    {
        T v;
        F f;
        bool empty = true;

    public:

        unique_impl() {}
        unique_impl(const unique_impl &) = delete;
        unique_impl &operator=(const unique_impl &) = delete;

        unique_impl &operator=(unique_impl &&other)
        {
            assert(!other.empty);
            if(!empty) f(v);
            v = std::move(other.v);
            f = std::move(other.f);
            empty = false;
            other.empty = true;
            return *this;
        }

        unique_impl(unique_impl &&other) { assert(!other.empty); *this = std::move(other); }

        unique_impl(const T &v_, const F &f_ = F()) : v(v_), f(f_), empty(false) {}
        unique_impl(T &&v_, const F &f_ = F()) : v(std::move(v_)), f(f_), empty(false) {}
        unique_impl &operator=(const T &v_) { if(!empty) f(v); v = v_; empty = false; return *this; }
        unique_impl &operator=(T &&v_) { if(!empty) f(v); v = std::move(v_); empty = false; return *this; }

        ~unique_impl() { if(!empty) f(v); }

        operator T () const { assert(!empty); return v; }
        unique_impl &operator+=(const T& v_) { assert(!empty); v += v_; return *this; }
        unique_impl &operator-=(const T& v_) { assert(!empty); v -= v_; return *this; }
        unique_impl &operator*=(const T& v_) { assert(!empty); v *= v_; return *this; }
        unique_impl &operator/=(const T& v_) { assert(!empty); v /= v_; return *this; }
        unique_impl &operator%=(const T& v_) { assert(!empty); v %= v_; return *this; }
        unique_impl &operator|=(const T& v_) { assert(!empty); v |= v_; return *this; }
        unique_impl &operator&=(const T& v_) { assert(!empty); v &= v_; return *this; }
        unique_impl &operator^=(const T& v_) { assert(!empty); v ^= v_; return *this; }
        unique_impl &operator>>=(const T& v_) { assert(!empty); v >>= v_; return *this; }
        unique_impl &operator<<=(const T& v_) { assert(!empty); v <<= v_; return *this; }
        unique_impl &operator++() { assert(!empty); ++v; return *this; }
        unique_impl &operator--() { assert(!empty); --v; return *this; }
        T operator++(int) { assert(!empty); return v++; }
        T operator--(int) { assert(!empty); return v--; }
        const T &operator->() const { assert(!empty); return v; }
        T &operator->() { assert(!empty); return v; }
        const T *operator&() const { assert(!empty); return &v; }
        T *operator&() { assert(!empty); return &v; }
    };

    template<typename T, typename F = default_release<T>>
    struct unique : unique_impl<T, F>
    {
        unique() {}
        unique(unique &&) = default;
        unique &operator=(unique &&) = default;
        unique(const unique &) = delete;
        unique &operator=(const unique &) = delete;

        unique(const T &v_, const F &f_ = F()) : unique_impl<T, F>(v_, f_) {}
        unique(T &&v_, const F &f_ = F()) : unique_impl<T, F>(std::move(v_), f_) {}
    };

    template<typename R, typename F>
    struct unique<R (), F> : unique_impl<R (*)(), F>
    {
        typedef R (*T)();

        unique() {}
        unique(unique &&) = default;
        unique &operator=(unique &&) = default;
        unique(const unique &) = delete;
        unique &operator=(const unique &) = delete;

        unique(const T &v_, const F &f_ = F()) : unique_impl<T, F>(v_, f_) {}
        unique(T &&v_, const F &f_ = F()) : unique_impl<T, F>(std::move(v_), f_) {}
    };
}

#endif // _DISGUISE_HPP_

测试:

#include <disguise.hpp>

using namespace disguise;

void disguised_address(const int *i)
{
    std::cout << *i << std::endl;
}

typedef void *handle;
handle create_handle(){ std::cout << "creating a handle" << std::endl; return static_cast<handle>(new int); };
void release_handle(handle h){ std::cout << "releasing a handle" << std::endl; delete static_cast<int *>(h); };
void manipulate_handle(handle h) { std::cout << "manipulating handle" << std::endl; }

int main()
{
    unique<void()> f = { []{ std::cout << "Hi" << std::endl; } };
    f = { []{ std::cout << "Bye" << std::endl; } };

    {
        int i;

        i = 10;
        std::cout << i << std::endl;
        i++;
        std::cout << i << std::endl;
        if(i > 0)
            std::cout << "i > 0" << std::endl;
        int j = i;
        j += i;
        std::cout << "j == " << j << std::endl;
        disguised_address(&i);
    }

    {
        unique<int> i;

        i = 10;
        std::cout << i << std::endl;
        i++;
        std::cout << i << std::endl;
        if(i > 0)
            std::cout << "i > 0" << std::endl;
        int j = i;
        j += i;
        std::cout << "j == " << j << std::endl;
        disguised_address(&i);
    }

    struct X{ int x = 10; void hello() const { std::cout << "hello" << std::endl; } };

    unique<X *> out;

    {
        X *x = new X;
        unique<X *> p = x;
        std::cout << p->x << " == " << x->x << std::endl;
        std::cout << (*p).x << " == " << (*x).x << std::endl;
        p->hello();
        (*p).hello();
        out = std::move(p);
    }

    std::cout << "Any smart destruction?" << std::endl;

    {
        unique<X *> p = std::move(out);
    }

    std::cout << "How about now?" << std::endl;

    {
        using unique_handle = unique<handle, RELEASER(&release_handle)>;

        unique_handle h = create_handle();
        manipulate_handle(h);
    }

    {
        unique<handle, decltype(&release_handle)> h = { create_handle(), release_handle };
        manipulate_handle(h);
    }
}

输出:

I'M A SCOPE-EXIT FUNCTION
Hi
10
11
i > 0
j == 22
11
10
11
i > 0
j == 22
11
I'M DUMB
10 == 10
10 == 10
hello
hello
Any smart destruction?
I'M SMART
How about now?
creating a handle
manipulating handle
releasing a handle
creating a handle
manipulating handle
releasing a handle
I'M A SCOPE-EXIT FUNCTION
Bye

如果.(dot) could be overloaded =)

,我会有更多的乐趣

答案 3 :(得分:-2)

对于独特所有权的情况,这是我要求的工具的草图。没有必要在native / legacy句柄类型中进行对等,没有封装违规,也不需要难看的额外包装。适用于几乎所有类型的传统句柄,基于整数或指针。

除了资源管理之外,它提供了一个额外的功能,即冒充原始句柄,我觉得它有点用处。

#include <utility>
#include <cassert>

template<typename T>
struct default_release
{
    void operator ()(const T &) {  }
};

template<typename Handle, typename Release = default_release<Handle>>
class unique_handle
{
    Handle handle;
    Release release;
    bool empty = true;

public:
    unique_handle(const Handle &handle_, const Release &release_ = Release()):
        handle(handle_),
        release(release_),
        empty(false) {}

    unique_handle &operator=(const unique_handle &) = delete;

    unique_handle(const unique_handle &) = delete;

    unique_handle &operator=(unique_handle &&other)
    {
        assert(!other.empty);
        if(!empty)
            release(handle);
        handle = std::move(other.handle);
        release = std::move(other.release);
        empty = false;
        other.empty = true;
        return *this;
    }

    unique_handle(unique_handle &&other)
    {
        *this = std::move(other);
    }

    operator Handle () const
    {
        assert(!empty);
        return handle;
    }

    ~unique_handle()
    {
        if(!empty)
            release(handle);
    }
};

这是一些示例用法:

// Types that model some C API
typedef unsigned GLuint;
typedef int GLsizei;

GLuint glGenLists(GLsizei range);
void glCallList(GLuint list);
void glDeleteLists(GLuint list, GLsizei range);

typedef void* HMODULE;
typedef const char *LPCSTR;
typedef int BOOL;
typedef int (*FARPROC) ();

HMODULE LoadLibraryA(LPCSTR lpFileName);
BOOL FreeLibraryA(HMODULE hModule);

FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName);

// The client code
unique_handle<HMODULE, decltype(&FreeLibraryA)> grab_foobar_lib()
{
    return {LoadLibraryA("foo/bar"), &FreeLibraryA};
}

int main()
{
    // using a short lambda here for the case of a
    // non-trivial deleter with more than one argument 
    unique_handle<GLuint, void (*)(GLuint)> gl_list_handle(glGenLists(1), [](GLuint list){ if(list != 0) glDeleteLists(list, 1); });

    glCallList(gl_list_handle); // using "impersonation" of the raw handle in the C API

    typedef void (*func)();
    auto the_lib = grab_foobar_lib(); // potential move semantics
    if(the_lib != NULL)
        func f = (func) GetProcAddress(the_lib, "f"); // more impersonation
}

修改

令人惊讶的是人们如何在这种情况下遵循类似的情况。例如,这包含相同的句柄模拟功能:

https://github.com/adobe/chromium/blob/master/base/win/scoped_handle.h