📜 ⬆️ ⬇️

Resource Management with Explicit Template Specializations



RAII is one of the most important and useful idioms in C ++. RAII frees the programmer from manual resource management, without it it is extremely difficult to write code that is safe from the point of view of exceptions . Perhaps the most popular use of RAII is managing dynamically allocated memory using smart pointers , but it can also be successfully applied to other resources, especially in the world of low-level libraries. Examples include Windows API descriptors, POSIX file descriptors, OpenGL primitives, and the like.

Options for implementing RAII


If we decide to write a RAII wrapper for some resource, we have several possibilities:
')

The first option is to write a specialized wrapper class - at first it may seem quite reasonable and really is a good starting point. The simplest RAII wrapper might look something like this:

 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_{}; }; 

However, as our code base increases in size, the number of resources that need to be monitored increases. Sooner or later we will notice that most of the wrapper classes are slightly different from each other: as a rule, the only difference is the resource release function. This approach provokes error-prone code reuse in the "copy / paste" style. On the other hand, we see here an excellent opportunity for generalization , which brings us to the next option - the use of smart pointers.

A smart pointer implemented as a class template is a generic resource management solution. However, he also has flaws, as we will see soon. As their name says, smart pointers were created primarily for memory management, and as a result, their use with other resources often leads to at least inconvenience. Let's look at smart pointers in more detail.

Why smart pointers are not so smart


Consider the following code:

 #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() }; } 

Why 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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 , TextureDestroyTexture . , 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