рукоятки иметь правильную семантику, кроме указателей. Так что для меня такой пример (извлеченный из Правило нуля):
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
и др. и др. не работает, я должен сделать ненужные предположения о том, что тип ручки является, когда то, что я хочу, — это просто получить «владение в пакете» только через непрозрачный тип дескриптора и его функцию освобождения.
Не имеет смысла вглядываться в тип дескриптора для создания конструкций на основе этой информации. Это ручка, это не должно иметь значения.
Я процитирую здесь чувства другого пользователя SO в другой вопрос ответ:
Создайте определенный класс «умный указатель», это не займет много времени. Не ругайте
библиотечные занятия. Семантика дескриптора сильно отличается от
Указатель на C ++; с одной стороны, разыменование РУЧКИ не имеет смысла.Еще одна причина использовать пользовательский класс smart handle — NULL этого не делает
всегда означает пустую ручку. Иногда это INVALID_HANDLE_VALUE,
что не то же самое.
Отказ от ответственности:
Этот вопрос переформулирует и строит на этом:
Тип unique_ptr
менее общий, чем фраза «ручка», да. Но почему бы не быть? Просто один из ваших «дескрипторных» примеров (скажем, тот, который является целочисленным индексом), является точно таким же общим, как unique_ptr
, Вы не можете сравнить один конкретный вид ручки со «всеми ручками когда-либо».
Если вам нужен один конкретный тип C ++ (или шаблон типа), который является дескриптором без фактического определения какой-либо конкретной семантики обработки, тогда … Я не могу вам помочь. Я не думаю, что кто-нибудь мог бы это сделать.
Это забавный пример «маскировки типа», который мне очень нравился, и это тоже как-то связано с обсуждением. Я оставлю это здесь для справки (я не несу ответственности за любой вред, причиненный им!).
#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
Я бы повеселился, если бы .(точка) может быть перегружен знак равно
Рад, что я узнал в ##c++
IRC канал о Универсальный Scope Guard и RAII Wrapper для стандартной библиотеки.
Текущее состояние предложения:
Это грубый набросок инструмента, который я спрашиваю, для случая уникального владения. Нет необходимости вглядываться в нативный / унаследованный тип дескриптора, не нарушать инкапсуляцию, не нужно отвратительной дополнительной обертки. Работает практически со всеми видами устаревших дескрипторов, на основе интеграла или указателя.
Это обеспечивает еще одну особенность, помимо управления ресурсами, которая является олицетворением необработанного дескриптора, я считаю это несколько полезным.
#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