std::unique_ptr<Handle, HandleDeleter>
); class ScopedResource { public: ScopedResource() = default; explicit ScopedResource(Resource resource) : resource_{ resource } {} ScopedResource(const ScopedResource&) = delete; ScopedResource& operator=(const ScopedResource&) = delete; ~ScopedResource() { DestroyResource(resource_); } operator const Resource&() const { return resource_; } private: Resource resource_{}; };
#include <memory> // From low-level API. using Handle = void*; Handle CreateHandle() { Handle h{ nullptr }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // error: expected argument of type void** ScopedHandle h{ CreateHandle() }; }
ScopedHandle
constructor expect an argument with type void**
? Recall that smart pointers were designed primarily for managing memory (that is, pointers): std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
std::unique_ptr int*
. std::unique_ptr Handle*
, void**
. ? -, std::remove_pointer
:
using ScopedHandle = std::unique_ptr<std::remove_pointer_t<Handle>, HandleDeleter>;
-, : pointer, :
struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>;
, , : Handle
. Handle
; Handle
– , .
, :
#include <memory> using Handle = int; Handle CreateHandle() { Handle h{ -1 }; /*...*/ return h; } void CloseHandle(Handle h) { /* ... */ } struct HandleDeleter { using pointer = Handle; void operator()(Handle h) { CloseHandle(h); } }; using ScopedHandle = std::unique_ptr<Handle, HandleDeleter>; int main() { // Error: type mismatch: "int" and "std::nullptr_t". ScopedHandle h{ CreateHandle() }; }
std::unique_ptr
, , .
– NullablePointer Handle
. , NullablePointer , , nullptr
. Handle
, int
, . , std::unique_ptr
, POSIX OpenGL.
, . Handle
, NullablePointer, , , – .
, , «» . Bitmap
:
// Graphics API. bool CreateBitmap(Bitmap* bmp) { /*...*/ return true; } bool DestroyBitmap(Bitmap bmp) { /* ... */ return true; } bool DrawBitmap(DeviceContext ctx, Bitmap bmp) { /* ... */ return true; } ... // User code. DeviceContext ctx{}; Bitmap bmp{}; CreateBitmap(&bmp); DrawBitmap(ctx, bmp);
Bitmap
std::unique_ptr:
struct BitmapDeleter { using pointer = Bitmap; void operator()(Bitmap bmp) { DestroyBitmap(bmp); } }; using ScopedBitmap = std::unique_ptr<Bitmap, BitmapDeleter>; ... DeviceContext ctx{}; Bitmap tmp; CreateBitmap(&tmp); ScopedBitmap bmp{ tmp }; DrawBitmap(ctx, bmp.get());
, ScopedBitmap
. , ScopedBitmap
, Bitmap.
, – RAII-.
, . .
#include <cassert> #include <memory> // std::addressof template<typename ResourceTag, typename ResourceType> class Resource { public: Resource() noexcept = default; explicit Resource(ResourceType resource) noexcept : resource_{ resource } {} Resource(const Resource&) = delete; Resource& operator=(const Resource&) = delete; Resource(Resource&& other) noexcept : resource_{ other.resource_ } { other.resource_ = {}; } Resource& operator=(Resource&& other) noexcept { assert(this != std::addressof(other)); Cleanup(); resource_ = other.resource_; other.resource_ = {}; return *this; } ~Resource() { Cleanup(); } operator const ResourceType&() const noexcept { return resource_; } ResourceType* operator&() noexcept { Cleanup(); return &resource_; } private: // Intentionally undefined - must be explicitly specialized. void Cleanup() noexcept; ResourceType resource_{}; };
.
, , ( std::unique_ptr
). , ( std::shared_ptr
). , ResourceType
(, void*
int
), , noexcept
. operator&
– . , , - CreateHandle(Handle* handle)
. -.
. , Cleanup
, , . . , leanup
, . :
// Here "FileId" is some OS-specific file descriptor type // which must be closed with CloseFile function. using File = Resource<struct FileIdTag, FileId>; template<> void File::Cleanup() noexcept { if (resource_) CloseFile(resource_); }
FileId
:
{ File file{ CreateFile(file_path) }; ... } // "file" will be destroyed here
Cleanup
Resource
« ». , Cleanup
FileId
« » .
— ResourceTag?
- , ResourceTag
? .
-, . , void*
. - :
using ScopedBitmap = Resource<Bitmap>; using ScopedTexture = Resource<Texture>; void DrawBitmap(DeviceContext& ctx, ScopedBitmap& bmp) { /* ... */ } int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; // Passing texture to function expecting bitmap. // Compiles OK. DrawBitmap(ctx, t); }
, :
using ScopedBitmap = Resource<struct BitmapTag, Bitmap>; using ScopedTexture = Resource<struct TextureTag, Texture>; int main() { DeviceContext ctx; ScopedBitmap bmp; ScopedTexture t; DrawBitmap(ctx, t); // error: type mismatch }
: Cleanup
, C++ . , , Bitmap
DestroyBitmap
, Texture
– DestroyTexture
. , ScopedBitmap
ScopedTexture
(, Bitmap
, Texture
void*
), .
, :
using File = Resource<struct FileIdTag, FileId>;
, struct FileIdTag
. , , :
struct FileIdTag{}; using File = Resource<FileIdTag, FileId>;
, . , . , . , . , C++ , :
struct FileIdTag; using File = Resource<FileIdTag, FileId>;
, , FileIdTag
, :
using File = Resource<struct FileIdTag, FileId>;
…
Cleanup, / . . , :
, ( ) , ; . static_assert
:
void Cleanup() noexcept { static_assert(false, "This function must be explicitly specialized."); }
, , : Cleanup
. : static_assert
, , , .
, : . , - , false:
static constexpr bool False() noexcept { return false; } void Cleanup() noexcept { static_assert(False(), "This function must be explicitly specialized."); }
RAII-, , , . - , , ‑ ? , , :
class Bitmap { public: Bitmap(int width, int height); ~Bitmap(); int Width() const; int Height() const; Colour PixelColour(int x, int y) const; void PixelColour(int x, int y, Colour colour); DC DeviceContext() const; /* Other methods... */ private: int width_{}; int height_{}; // Raw resources. BITMAP bitmap_{}; DC device_context_{}; };
, , Bitmap:
Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = CreateBitmap(width, height); if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = CreateCompatibleDc(); if (!device_context_) // bitmap_ will be leaked here! throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
, : ( Windows GDI , , , , ). : device_context_
, bitmap_
!
:
using ScopedBitmap = Resource<struct BitmapTag, BITMAP>; using ScopedDc = Resource<struct DcTag, DC>; ... Bitmap::Bitmap(int width, int height) : width_{ width }, height_{ height } { // Create bitmap. bitmap_ = ScopedBitmap{ CreateBitmap(width, height) }; if (!bitmap_) throw std::runtime_error{ "Failed to create bitmap." }; // Create device context. device_context_ = ScopedDc{ CreateCompatibleDc() }; if (!device_context_) // Safe: bitmap_ will be destroyed in case of // exception. throw std::runtime_error{ "Failed to create bitmap DC." }; // Select bitmap into device context. // ... }
: . RAII , . , ( ).
Windows API. Windows API, RAII ( ; Windows API ).
// Windows handle. using Handle = Resource<struct HandleTag, HANDLE>; template<> void Handle::Cleanup() noexcept { if (resource_ && resource_ != INVALID_HANDLE_VALUE) CloseHandle(resource_); } // WinInet handle. using InetHandle = Resource<struct InetHandleTag, HINTERNET>; template<> void InetHandle::Cleanup() noexcept { if (resource_) InternetCloseHandle(resource_); } // WinHttp handle. using HttpHandle = Resource<struct HttpHandleTag, HINTERNET>; template<> void HttpHandle::Cleanup() noexcept { if (resource_) WinHttpCloseHandle(resource_); } // Pointer to SID. using Psid = Resource<struct PsidTag, PSID>; template<> void Psid::Cleanup() noexcept { if (resource_) FreeSid(resource_); } // Network Management API string buffer. using NetApiString = Resource<struct NetApiStringTag, wchar_t*>; template<> void NetApiString::Cleanup() noexcept { if (resource_ && NetApiBufferFree(resource_) != NERR_Success) { // Log diagnostic message in case of error. } } // Certificate store handle. using CertStore = Resource<struct CertStoreTag, HCERTSTORE>; template<> void CertStore::Cleanup() noexcept { if (resource_) CertCloseStore(resource_, CERT_CLOSE_STORE_FORCE_FLAG); }
, :
, ( , Resource
); , , (inline): , – , .
unique_resource N3949
, , N3949 . N3949 unique_resource_t
, , ( , std::unique_ptr
):
template<typename Resource, typename Deleter> class unique_resource_t { /* … */ }; // Factory. template<typename Resource, typename Deleter> unique_resource_t<Resource, Deleter> unique_resource(Resource&& r, Deleter d) noexcept { /* … */ } ... // Usage (predefined deleter). struct ResourceDeleter { void operator()(Resource resource) const noexcept { if (resource) DestroyResource(resource); } }; using ScopedResource = unique_resource_t<Resource, ResourceDeleter>; ScopedResource r{ CreateResource(), ResourceDeleter{} }; // Alternative usage (in-place deleter definition). auto r2 = unique_resource( CreateResource(), [](Resource r){ if (r) DestroyResource(r); });
, unique_resource_t
, Resource
– . , , ( , RAII-). , . , , .
, , , , , Windows API CertCloseStore, .
, , Resource unique_resource_t . , , (. ., operator()
). unique_resource_t
, , , ( ).
, C++, .
RAII-, , , , . :
– ; , ; .
, , . , , . , , , . , .
.
: , Positive Technologies
126 Overload ( 2015).
Source: https://habr.com/ru/post/255487/
All Articles