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, 这是不一样的。
声明:
这个问题在此基础上重新制定并建立:
答案 0 :(得分:2)
类型unique_ptr
不如短语“handle”,是的。但为什么不应该呢?只是您的“句柄”示例之一(例如,整数索引),与unique_ptr
一样通用。您无法将一种特定类型的句柄与“所有句柄”进行比较。
如果你想要一个单独的,具体的C ++类型(或类型模板),它是一个句柄而没有实际定义任何特定的处理语义,那么...我无法帮助你。我不认为任何人都可以做到。
答案 1 :(得分:0)
很高兴我在##c++
IRC频道中了解了Generic Scope Guard and RAII Wrapper for the Standard Library。
提案的当前状态:
答案 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
,我会有更多的乐趣
答案 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