Document Number: N3677
Date: 2013-04-26
Project: Programming Language C++, Library Working Group
Reply-to: Andrew L. Sandoval <sandoval at netwaysglobal dot com>
unique_ptr
, but is designed for use with any type of resource
that requires clean-up prior to scope exit, as well as any block of code (via
lambda or function invocation) that needs to executed prior to scope exit.
Quality C++ code requires the use of RAII to manage all types of resources in order to prevent leakage as well as to limit resource lifetime. Resource leakage can occur from the obvious, though often overlooked, failure to clean-up a resource at its intended end-of-life, as well as the less obvious and arguably more common failure to ensure proper handling of resources during exception unwinding.
Smart pointers such as unique_ptr
and shared_ptr
provide an excellent means of managing the lifetime of pointer types. Many other RAII classes have been written to manage the lifetime and limit the scope of locks, such as Boost's boost::lock_guard
. All of these and the many other types of RAII objects help to
substantially improve C++ code quality.
This proposal seeks to add a set of generic, light-weight RAII wrappers intended to manage the scope and lifetime of any other type of resource.
It also proposes an RAII wrapper intended to wrap a function, lambda, or functor
which needs to execute prior to scope exit.
There are many types of resources that may not justify encapsulation in a broader object -- when a simple RAII wrapper around the resource will suffice or will provide a more obvious implementation.
This is especially true when object re-use is not expected. In addition, RAII
wrappers will simplify the design of objects that do encapsulate resources,
allowing the generation of, or the declaration of the resource, to also declare its method of clean-up.
This results in moving resource clean-up outside of an explicit destructor,
which also assures proper order of destruction when initialization order is
correct. This is common practice with pointers managed by unique_ptr
and shared_ptr
, and even with buffers allocated and managed by vector
.
This proposal requests the addition of the following RAII wrapper types:
scoped_function
make_scoped_function(func);
func
is a function, functor, or lambda that takes no parameters. A lambda however may capture
and use any scope variables.scoped_resource_unchecked
make_scoped_resource_unchecked(resource, deleter-func);
, or with the
more generic make_scoped_resource(resource, deleter-func);
deleter-func
is a function, functor, or lambda that takes a single parameter of the same type as resource.scoped_resource
make_scoped_resource(resource, deleter-func, no-delete-value);
make_scoped_resource<true>(resource, deleter-func, no-delete-value);
the scoped_resource
constructor will throw failed_resource_initialization
if the resource is equal to the no-delete value.deleter-func
is a function, functor, or lambda that takes a single parameter of the same type as resource.no-delete-value
is a value of type resource that is used to prevent calling deleter-func when the resource is not valid, as well as to check if the resource is bool valid() const;
unique_resource
failed_resource_initialization
if the value passed to the constructor matches the no-delete value.
When false, a validity check is not performed on object construction, but the
deleter function will not be executed in the destructor if the validity check
fails.
static_cast<T>(nullptr)
.if(resource != nodelete) { deleter-func(resource); }
at a common exit point for the
scope.bool valid() const;
operator=
at a later time.scoped_function
scoped_function
is ideal for tying a block of code -- such as a lambda to a particular scope, ensuring that the block is invoked prior to scope exit.
Two examples should suffice:
scoped_function
for mandatory logging on function exit:
bool SomeFunction(const input_t &input, modifier_t &modifier, status_t &status)
{
// Always log the status on scope-exit whether it changes or not...
auto &&log_status_on_exit = std::make_scoped_function([&status, &modifier]() ->void
{
LogStatus(status, "SomeFunction", modifier);
});
if(!AlterationOne(input, modifier, status))
{
return false;
}
.
.
.
return AlterationFour(modifier, status);
}
scoped_function
for loop advancement:
LegacyDisassembler dis(pInstructionPointer, length);
while(dis.RemainingBytes())
{
// Ensure that no matter where else the loop continues, dis gets advanced...
auto &&advance = std::make_scoped_function([&dis]() ->void
{
dis.Advance(dis.InstructionLength());
});
if(dis.BadInstruction())
{
std::cerr << "Error on " << dis << std::endl;
continue; // Skip the bad byte(s)
}
// Handle special cases
.
.
.
// Print the Instruction...
std::cerr << dis.Address << ": " << dis << std::endl;
}
scoped_resource_unchecked
scoped_resource_unchecked
is ideal for managing a resource that either can't fail to be initialized, or whose deleter function handles exceptional cases (such as an invalid, uninitialized or previously deleted resource).
It provides no validity checking, and unless release()
'd the deleter-function
will be invoked with the resource on scope exit.
scoped_resource_unchecked
example:
CRITICAL_SECTION cs;
InitializeCriticalSection(&cs);
// InitializeCriticalSection() can't fail and returns void, but we need the delete to occur prior to scope exit...
// Otherwise it remains on the process's linked-list of active CRITICAL_SECTION's.
auto &&pCS = std::make_scoped_resource_unchecked(&cs, DeleteCriticalSection); // DeleteCriticalSection can't fail and return void
scoped_resource
scoped_resource
is designed to handle most resources. It keeps the user congnizant of how the resource will be destroyed when it exits scope, and of the condition that must be met.
Though it produces larger object code than unique_resource
, it benefits from having a generator function template that deduces the template arguments.
scoped_resource
for with a C API:
// Open a file, ensure it is closed when we leave scope, if the file descriptor is valid...
auto&& iFileDesc = std::make_scoped_resource(_open(pszPath, O_WRONLY), _close, -1); // if _open returns -1, don't call _close...
if(iFileDesc.valid())
{
_write(iFileDesc, &data, sizeof(data));
}
This could also be done using exception handling
(where exception handling is allowed and expected)...
try
{
// Open a file, ensure it is closed when we leave scope, if the file descriptor is valid...
auto&& iFileDesc = std::make_scoped_resource(_open(pszPath, O_WRONLY), _close, -1); // if _open returns -1, throw...
_write(iFileDesc, &data, sizeof(data));
}
catch (const std::failed_resource_initialization &e)
{
std::cout << "Exception: " << e.what() << std::endl;
return false;
}
return true;
scoped_resource
for with a Windows API:
// Create or open an event handle, wait on it, but after being signalled close the handle...
// Don't CloseHandle() if we don't have permissions to open it, etc.
// Another exception-free example, that with different calls could be used in kernel-mode
const HANDLE INVALID_EVENT_HANDLE = static_cast<HANDLE>(nullptr);
auto hEvent = std::make_scoped_resource(CreateEvent(nullptr, TRUE, FALSE, nullptr), CloseHandle, INVALID_EVENT_HANDLE);
if(!hEvent.valid())
{
return false; // CloseHandle will not be called
}
DWORD dwWait = WaitForSingleObject(hEvent, 1000);
.
.
.
// CloseHandle() will be called as soon as hEvent leaves scope
NOTE: Microsoft defines INVALID_HANDLE_VALUE
as -1
for the CreateFile
API, but returns NULL
for most other APIs. Compliant compilers can handle the case where INVALID_HANDLE_VALUE
is passed to scoped_ptr
as a no-delete value.
Unfortunately this is not true of some c++11 compliant compilers (such as g++) when INVALID_HANDLE_VALUE
is passed as a non-type template parameter:
auto&& hFile = std::make_scoped_resource(CreateFileW(pwzPath, FILE_GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr),
CloseHandle, INVALID_HANDLE_VALUE); // Compiles fine
std::unique_resource<UNIQUE_DELETER(CloseHandle), INVALID_HANDLE_VALUE> // Compiler error on gcc-4.7, compiles on Visual Studio 2010
hFile(CreateFileW(pwzPath, FILE_GENERIC_WRITE, FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
unique_resource
The primary motivation for using unique_resource
over scoped_resource
is the size of the generated object code. It's use is restricted to resources that have a constexpr deleter function which takes a single argument of the same type as the resource.
Also, the deleter function and the no-delete value must be constant expressions.
There is no generator like
for unique_resource
. It's construction requires
4 template parameters, but unlike
, the static
(constexpr) no-delete value has a default value of static_cast<T>(nullptr)
, where T
is the type of the first argument passed to the deleter function.
In the reference implementation unique_resource
uses a parent implementation class called unique_resource_impl
. This allows the type of the resource to be deduced using template metaprogramming. unique_resource_impl
requires
5 template parameters. The TFuncInfo1
template resolves the parameter type of the deleter function for unique_resource
which inherits from unique_resource_impl
.
To simplify use of unique_resource
, a convenience macro is used in the examples below, and in the reference implementation. It is defined as:
#define UNIQUE_DELETER(deleter_fn) decltype(&deleter_fn), deleter_fn, false
#define UNIQUE_DELETER_THROW(deleter_fn) decltype(&deleter_fn), deleter_fn, true
Another consideration when using unique_resource
over make_scoped_resource()
is the benefit it provides for member variable resources. For example, both of the following classes will accomplish the same end-goal, of calling CloseHandle
on m_hEvent
when an instance of the class is destroyed, but the unique_resource
example is slightly easier to read, especially with the help of UNIQUE_DELETER
.
class TestClassMember
{
private:
// auto &&m_hEvent; // error C3531: 'm_hEvent': a symbol whose type contains 'auto' must have an initializer
std::scoped_resource<decltype(&CloseHandle), HANDLE> m_hEvent; // ok, but a bit bulky... And a macro can't deduce the resource type...
public:
// TestClassMember() : m_hEvent(std::make_scoped_resource(CreateEvent(nullptr, TRUE, FALSE, nullptr), [](HANDLE hEvent) { if(hEvent) CloseHandle(hEvent); }))
// { // ^^: error C2440: 'initializing' : cannot convert from 'std::scoped_resource_unchecked<T,R>' to 'int'
// }
TestClassMember() : m_hEvent(std::make_scoped_resource(CreateEvent(nullptr, TRUE, FALSE, nullptr), CloseHandle, static_cast
unique_resource
compared to a scoped_resource
generated with make_scoped_resource()
test_unique_resource()
is only 32 bytes, compared to the 57 byte test_scoped_resource()
, which does the same thing.
void test_unique_resource(...) //... added to prevent inlining
{
// Create an event which will be closed on scope-exit, if NOT NULL. It can be passed to SetEvent/WFSO as hEvent
std::unique_resource<UNIQUE_DELETER(CloseHandle)> hEvent(CreateEvent(nullptr, TRUE, FALSE, nullptr));
}
// Object Code: (32 bytes, no prologue, no epilogue, 1 byte for ret)
// scope_exit!test_unique_resource [scope_exit\scope_exit.cpp @ 243]:
// 243 003d1310 6a00 push 0
// 245 003d1312 6a00 push 0
// 245 003d1314 6a01 push 1
// 245 003d1316 6a00 push 0
// 245 003d1318 ff1510303d00 call dword ptr [scope_exit!_imp__CreateEventW (003d3010)]
// 246 003d131e 85c0 test eax,eax
// 246 003d1320 7407 je scope_exit!test_unique_resource+0x19 (003d1329)
// 246 003d1322 50 push eax
// 246 003d1323 ff1514303d00 call dword ptr [scope_exit!_imp__CloseHandle (003d3014)]
// 246 003d1329 c3 ret
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
const HANDLE INVALID_EVENT_HANDLE = static_cast<HANDLE>(nullptr);
void test_scoped_resource(...) //... added to prevent inlining
{
// Create an event which will be closed on scope-exit, if NOT NULL. It can be passed to SetEvent/WFSO as hEvent
auto hEvent = std::make_scoped_resource(CreateEvent(nullptr, TRUE, FALSE, nullptr), CloseHandle, INVALID_EVENT_HANDLE);
}
// Object Code: (57 bytes, 3 prologue bytes and 3 epilogue bytes, to allow for scoped_resource (12 bytes, aligned) on the stack, + 1 for ret)
// scope_exit!test_scoped_resource [scope_exit.cpp @ 250]:
// 250 003d1330 55 push ebp
// 250 003d1331 8bec mov ebp,esp
// 250 003d1333 83ec0c sub esp,0Ch
// 252 003d1336 6a00 push 0
// 252 003d1338 6a00 push 0
// 252 003d133a 6a00 push 0
// 252 003d133c 6a01 push 1
// 252 003d133e 6a00 push 0
// 252 003d1340 ff1510303d00 call dword ptr [scope_exit!_imp__CreateEventW (003d3010)]
// 252 003d1346 8b0d14303d00 mov ecx,dword ptr [scope_exit!_imp__CloseHandle (003d3014)]
// 252 003d134c 8bd0 mov edx,eax
// 252 003d134e 8d45f4 lea eax,[ebp-0Ch]
// 252 003d1351 e83a060000 call scope_exit!std::make_scoped_resource<int (__stdcall*)(void *),void *> (003d1990)
// 253 003d1356 8b45f8 mov eax,dword ptr [ebp-8]
// 253 003d1359 83c404 add esp,4
// 253 003d135c 3b45fc cmp eax,dword ptr [ebp-4]
// 253 003d135f 7404 je scope_exit!test_scoped_resource+0x35 (003d1365)
// 253 003d1361 50 push eax
// 253 003d1362 ff55f4 call dword ptr [ebp-0Ch]
// 253 003d1365 8be5 mov esp,ebp
// 253 003d1367 5d pop ebp
// 253 003d1368 c3 ret
<scoped_resource>
header filescope_exit.cpp
file with testsscoped_resource
. Otherwise the impact and scope should be much the same as the impact and scope of unique_ptr
, only
with the intended use focused on non-pointer resources, and pointer based
resources for which one of these wrappers may be a better fit than unique_ptr
.
<scope_resource>
, but it does not require changes to any standard classes or functions. It does not require any changes in the core language, and it has been implemented in standard C++ conforming to c++11. The proposal does not depend on any other library extensions. The reference implementation utilizes the standard <utility>
header, for std::move
, as well as <type_traits>
for std::remove_pointer
, and std::add_reference
.
Using an RAII resource wrapper should be as easy as using a raw data
type. Generally the cast operator accomplishes this goal. The get()
method
would be a reasonable alternative to the cast operator if it were to meet with insurmountable opposition.
Other operators are also provided to simplify usage in a natural manner for the wrapped resource.
Simple construction and generation should encourage use of the wrappers over raw resource types. This is as true when resources are used within functions and methods as it is when the wrappers are used in objects designed to encapsulate and provided additional related functionality around the resource in a class.
The "generator" function templatesmake_scoped_function
, andmake_scoped_resource
help ensure simplicity.
Though macros are generally frowned upon, theUNIQUE_DELETER
macro used to simplify the passing of template parameters tounique_resource
, can also claim to improve simplicity. Unlike other macros, it does not hide functionality -- instead it draws attention to the the template arguments that matter most. The end result is a very easy to use and understandunique_resource
.
Code written with these RAII wrappers becomes simple to maintain. Lifetime of resources and their method of clean-up is evident from the time of declaration or generation. The order of operations in clean-up is expected and simple. And object lifetime is managed by scope rather than the hopefully correct ordering of clean-up at scope-exit or in traditional destructors.
It should be obvious from a glance what each RAII resource wrapper does. Constructors and generator function templates should clearly show the binding of the resource's clean-up to the resource.
Similarly a glance at a scoped_function
should make it obvious what will happen when the object reaches scope exit.
Each wrapper is designed to encourage consideration of a resource's lifetime when it is created. The cost of using these wrappers should not out weigh the benefits.
Code bloat should be avoided, and options should exist to minimize generated object code size. These wrappers should be usable in resource constrained environments.
These RAII resource wrappers should be able to be used without introducing any new risk of throwing exceptions due to allocation failures, etc. They should be usable for example in kernel code where C++ exceptions often can't be handled.
Both types of RAII wrappers that allow for a vailidity check (scoped_resource
, andunique_resource
) default to a nothrow implementation. Each also offer a simple means by whichfailed_resource_initialization
may be thrown on construction when a validity check fails. This provides the broadest opportunity for use, and adaptation to the users needs.
These RAII resource wrappers should allow for the use of lambdas with and without capturing. Because lambda's may be used to capture by reference, these RAII wrappers can utilize or update references just prior to scope-exit, etc. Lambda's also provide a means of adding logic to deleter "functions", and of returning status from a deleter function.
B. Prior implementations
RAIIFunction
andRAIIWrapper
have been in shipping commercial products. They were initially posted to std-proposals to begin the discussion leading to this proposal.RAIIWrapper
is very similar tounique_resource
.RAIIFunction
was original designed to inherit fromstd::function<void ()>
, and was later changed to use the same in composition. This class was posted to the Boost Developer mailing list, where encouragement and feedback where given on possible inclusion in Boost. It was noted however thatstd::function
can throw on an allocation failure. This was mentioned when posting to the std-proposals list, and the current solution -- of using template types (and generator function templates) instead ofstd::function<void ()>
was recommended by one of the list participants (DeadMG).scoped_function
,scoped_resource_unchecked
, andscoped_resource
along with theirmake_scoped_function
andmake_scoped_resource()
generators are the result of reworkingRAIIFunction
based on feedback from the group in general. The result is a much more usable set of classes, where lambda's can be used to clean-up resources with or without capturing, etc.
During discussion on the Boost Developer mail list, attention was drawn to the Boost Scope.Exit library. This library uses macros to provide the ability to execute clean-up code on scope exit. For the purposes of this proposal, macros have been avoided, other than the very simpleUNIQUE_DELETER
macro that is used only for template parameters. Macros often hide details, including from debuggers, that can be crucial to understand and troubleshooting problems. For that reason, and because of the general distaste for macros in C++, Boost Scope.Exit was not considered for this proposal. That should not be in anyway interpreted as disrespect to the author of Boost Scope.Exit, as it was designed to work with earlier C++ compilers that lack C++11 support. It should also be noted that the Boost Scope.Exit documentation also includes a suggested alternative for a class compositingstd::function<void (void)>
1.
During discussions on the std-proposal list, many examples were given, and some suggestions thatstd::unique_ptr
is sufficient and additional RAII classes are not needed. This proposal rejects those suggestions. Thoughstd::unique_ptr
is vital to the Standard Library it is designed to work with pointers, not resources of other types. Converting resources, whether they areint
's of a file descriptors, or other non-pointer types isn't straightforward -- and requires that the deleter be able to dereference the pointer. That would rule out use with many functions, unless the deleter function was first wrapped in a lambda or another function or functor for that purpose. Additionally,std::unique_ptr
does not provide a cast operator to allow natural use of the wrapped resource in API calls, etc.
An article2 written by Andrei Alexandrescu and Petru Marginean, published by Dr. Dobbs Magazine in December 2000 introduces a class calledScopeGuard
, which is very similar toscope_resource_uc
as presented in the reference implementation associated with this proposal.
C. Risks
The cast operator provided by each of the classes does pose a minor risk. For example, if a confused user where to try to to mix themake_scoped_resource()
call with construction of aunique_resource
, the cast operator will allow initialization ofunique_resource
, but the return of themake_scoped_resource()
will result in a temporary object, which after transfering the wrapped resource to theunique_resource
will then be "deleted" by the deleter function passed tomake_scoped_resource
. This would lead to the deleter being invoked more than once, and the resource being used after deletion. This is a minor risk. The template parameters tounique_resource
should make it obvious that it does not match up tomake_scoped_resource()
.
Subclause | Header(s) |
20.14 Scoped resources | <scoped_resource> |
scoped_function
1 | A scoped function is an object s that manages a function object f such that f will be invoked when s is destroyed (e.g., when leaving block scope (6.7)). |
2 | The function object managed by scoped_function<T> takes no parameters, so that s may invoke f as f(); . |
3 | f may be a function pointer, functor, callable function as returned from std::bind , or a lambda
taking no parameters. |
namespace std
{
template<typename T> class scoped_function
{
public:
// 20.14.1.1 constructors
explicit scoped_function(T&& f) noexcept;
explicit scoped_function(const T& f) noexcept;
// 20.14.1.2 destructor
~scoped_function();
// 20.14.1.3 modifiers
scoped_function& release() noexcept();
scoped_function& reset(T&& f);
scoped_function& reset(const T& f);
scoped_function& invoke(bool bRelease = true);
void operator()();
// 20.14.1.4 assignment
void operator=(T&& f) noexcept;
void operator=(const T& f) noexcept;
};
// 20.14.1.5 generator
// Make a scoped_function
// Takes 1 parameter - the function to be run on scope exit...
template<typename T> scoped_function<T> make_scoped_function(T f) noexcept;
{
return scoped_function<T>(std::move(f));
}
}
scoped_function
constructors take one argument, the type of which must be a callable function taking no parameters. It is intended that std::make_scoped_function( f )
be used to generate a scoped_function
, as in the following examples:
//
// Using a lambda:
auto logOnScopeExit = std::make_scoped_function([&syslog, &someStateVariable]() ->void
{
syslog << "Exiting with state: " << someStateVariable;
});
//
// Using a function:
// e.g. void SetFinalStatus();
auto setFinalStatus = std::make_scoped_function(SetFinalStatus);
//
// Using bind
auto closeFile = std::make_scoped_function( std::bind(close, fd) ); // std::bind may throw
scoped_function
destructor will invoke the function object passed to the constructor, as long as the scoped_function
has not been "released" via a call to release()
, or invoke(true)
.
release()
instructs scoped_function
that the function passed to the constructor (or reset / assignment operators) is not to be called when the destructor is invoked.reset()
accepts a new function, function object, or lambda to be called when the scoped_function
destructor is invoked. The argument is the same as what is accepted by the constructors. Reseting the function also resets the state of a previously "released" scoped_function
invoke(true)
performs an early (pre-destructor) invocation of the function passed to the constructor, reset method, or assignment operator. The default true parameter sets the scoped_function
to the "released" state so that the function will not be invoked again when the destructor is called.invoke(false)
performs an early (pre-destructor) invocation of the function passed to the constructor, reset method, or assignment operator. The false parameter leaves the scoped_function
in the "unreleased" state so that the function will be invoked again when the destructor is called.operator()()
performs an early (pre-destructor) invocation of the function passed to the constructor, the same as if invoke(true)
had been called.reset()
to replace the function that will be invoked when the destructor is called. The object is set to the "unreleased" state so that even if release()
, or invoke(true)
had been previously called, the new function will be invoked when the destructor is called.
make_scoped_function
make_scoped_function(func)
is used to generate a scoped_function
object.
scoped_resource_unchecked
1 | A scoped_resource_unchecked is an object s that manages a generic resource r such that a clean-up function, function object, or lambda, f(r) will be invoked when s is destroyed (e.g., when leaving block scope (6.7)). |
2 | The function object f managed by scoped_resource_unchecked<TDel,
TRes> takes a single parameter of the type of the resource r so that s may invoke f(r) as f(r); , when s leaves the current scope. |
3 | f may be a function pointer, functor, a callable function as returned from std::bind , or a lambda, taking a single argument of the type of the resource. |
4 | r may be accessed through cast operators as if it were of the type of the resource r. |
5 | Unlike scoped_resource, r is not compared to a "no-delete value" prior to the invocation of f(r) . |
6 | A scoped_resource_unchecked may be generated (is intended to be generated) using make_scoped_resource(r, f) |
namespace std
{
template<typename TDel, typename TRes> class scoped_resource_unchecked
{
public:
// 20.14.2.1 constructors
explicit scoped_resource_unchecked(TDel&& arg, TRes&& resource) noexcept;
explicit scoped_resource_unchecked(const TDel& arg, const TRes& resource) noexcept;
// 20.14.2.2 destructor
~scoped_resource_unchecked();
// 20.14.2.3 modifiers
TRes release() throw();
scoped_resource_unchecked& reset(TRes&& resource);
scoped_resource_unchecked& reset(const TRes& resource);
scoped_resource_unchecked& invoke(bool bRelease = true);
void operator()();
// 20.14.2.4 access
TRes get() const throw();
operator TRes() const throw();
TRes operator->() const;
TRes* operator&();
TDel& get_deleter();
const TDel& get_deleter() const;
};
// 20.14.2.5 Specific Generator
// Make a scoped_resource_unchecked (unchecked)
// Takes 2 arguments: The resource and the deleter
template<typename TDel, typename TRes>
scoped_resource_unchecked<TDel, TRes> make_scoped_resource_unchecked(TRes r, TDel d) throw() // Specific request
{
return scoped_resource_unchecked<TDel, TRes>(std::move(d), std::move(r));
}
// 20.14.2.6 Generic Generator
template<typename TDel, typename TRes>
scoped_resource_unchecked<TDel, TRes> make_scoped_resource(TRes r, TDel d) throw() // Generic request
{
return scoped_resource_unchecked<TDel, TRes>(std::move(d), std::move(r));
}
}
scoped_resource_unchecked
constructors take two arguments. The type of the first must be a callable function taking a single parameter of the type of the second template argument. It is intended that std::make_scoped_resource(resource, deleter)
be used to generate a scoped_resource_unchecked
, as in the following examples:
//
// Most common usage - close "handle" types on scope-exit, using a function...
extern PACCESS_TOKEN PsReferencePrimaryToken(IN PEPROCESS pProcess); // Assumption: Always returns a valid value that can be passed to ObDerferenceToken
extern void ObDereferenceToken(PACCESS_TOKEN pToken); // Deleter: Handles any value (including nullptr) from PsReferencePrimaryToken()
// Manual Construction (not intended):
std::scoped_resource_unchecked<decltype(&ObDereferenceToken), PACCESS_TOKEN> pToken(PsReferencePrimaryToken(PsGetCurrentProcess(), ObDereferenceToken));
// Construction via Generic Generator (preferred method) (see 20.14.2.6):
auto pToken = std::make_scoped_resource(PsReferencePrimaryToken(PsGetCurrentProcess(), ObDereferenceToken));
//
// Using a lambda:
extern PACCESS_TOKEN PsReferencePrimaryToken(IN PEPROCESS pProcess); // Assumption: Always returns a valid value that can be passed to ObDerferenceToken
extern void ObDereferenceObject(PVOID pObject); // Generic deleter
auto pToken = std::make_scoped_resource(PsReferencePrimaryToken(PsGetCurrentProcess()), [](PACCESS_TOKEN pToken) ->void
{
if(pToken == nullptr)
{
syslog << "Warning, closing nullptr primary token on scope-exit!!!";
}
ObDereferenceObject(pToken);
});
//
// Using bind
extern void * VirtualAllocGuaranteed(PVOID pPreferredBaseAddr, size_t szSize, unsigned int flags, unsigned int pageProtection); // System API that always returns usable pointer (even if nullptr / page zero)
extern void VirtualFree(void *pAddress, size_t size, unsigned int freeFlags); // Generic System API for releasing, freeing, etc. virtual memory
auto pVirtMem = std::make_scoped_resource(VirtualAllocGuaranteed(nullptr, 4096, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE),
std::bind(VirtualFree, _1, 0, MEM_RELEASE));
scoped_resource_unchecked
destructor will invoke the "deleter" function object passed to the constructor, passing it the
tracked resource, as long as the scoped_resource_unchecked
has not been "released" via a call to release()
, or invoke(true)
. By this means the resource clean-up is carried out by the "deleter" function on scope-exit or unwind.
release()
prevents the scoped_resource_unchecked
from
invoking the "deleter" function in the destructor. It returns a copy of the resource so that it may if needed, be assigned to another tracking object.reset(resource)
causes the scoped_resource_unchecked
to invoke the "deleter" function for any currently tracked resource that has not been "released", and assigns a new resource to the scoped_resource_unchecked
to be tracked. The scoped_resource_unchecked
will be set to an "un-released" state when a new resource is assigned.invoke(true)
performs an early (pre-destructor) invocation of the "deleter" function. The
default true parameter sets the scoped_resource_unchecked
to the "released" state so that the deleter function will not be invoked again when the destructor is called, unless the object is subsequently "reset."invoke(false)
performs an early (pre-destructor) invocation of the "deleter" function. The false parameter leaves the scoped_resource_unchecked
in the "un-released" state so that the deleter function will be invoked again when the destructor is called.operator()()
performs an early (pre-destructor) invocation of the deleter function, the same as if invoke(true)
had been called.TRes get() const throw()
returns the resource being managed by the scoped_resource_unchecked
object.operator TRes() const throw()
returns the resource being managed by the scoped_resource_unchecked
object.TRes operator->() const
returns the resource being managed by the scoped_resource_unchecked
object.TRes* operator&()
returns a pointer to the resource being managed by the scoped_resource_unchecked
object, where the scoped_resouce_unchecked
has a private member of type TRes
such as TRes m_resource;
which is returned from this accessor with return &m_resource;
TDel& get_deleter()
returns a reference to the deleter function.
This allows the caller to modify the deleter function if needed on a non-const
object.const TDel& get_deleter() const
returns a reference to the deleter
function.make_scoped_resource_unchecked
make_scoped_resource_unchecked(resource, deleterfunc)
is used to generate a scoped_resource_unchecked
object.
See the examples above in 20.14.2.1.
make_scoped_resource
make_scoped_resource(resource, deleterfunc)
is used to generate a scoped_resource_unchecked
object in a manner that is compatible with the generation of a scoped_resource
object. The difference between the generator for a scoped_resource_unchecked
object and the generator for a scoped_resource
is that the scoped_resource
requires a 3rd parameter that is used for validity checking prior to invoking the deleter function. Also, scoped_resource
can be controlled through template parameters to throw failed_resource_initialization();
when it is constructed with a resource that matches the "no-delete" value.
std::failed_resource_initialization exception
scoped_resource
or unique_resource
object. The following is an example implementation.
namespace std
{
////////////////////////////////////////////////////////////////////////////
// failed_resource_initialization
// An exception thrown when a no-delete value is passed to a scoped_resource
// or unique_resource constructor, assignment, or reset IF the template
// arguments allow for throwing. Throwing is optional so that these classes
// may be used in environment where exception can not be handled (e.g.
// kernel drivers, etc.) In those cases the resource should be validated
// using .valid() prior to use.
////////////////////////////////////////////////////////////////////////////
class failed_resource_initialization : public exception
{
public:
explicit failed_resource_initialization(const char * = nullptr) throw()
{
}
virtual const char * what() const throw()
{
return "resource initialization failed";
}
};
}
scoped_resource
1 | A scoped_resource is an object s that manages a generic resource r such that a clean-up function, function object, or lambda, f(r) will be invoked when s is destroyed (e.g., when leaving block scope (6.7)), as long as the resource passes a validity check. |
2 | The function object f managed by scoped_resource<TDel,
TRes, const bool throw_on_init_failure = false> takes a single parameter of the type of the resource r so that s may invoke f(r) as f(r); , when s leaves the current scope
or is otherwise destructed. |
3 | f may be a function pointer, functor, a callable function as returned from std::bind , or a lambda, taking a single argument of the type of the resource. |
4 | r may be accessed through cast operators as if it were of the type of the resource r. |
5 | Unlike scoped_resource_unchecked, r is compared to a "no-delete value" prior to the invocation of f(r) . |
6 | A scoped_resource may be generated (is intended to be generated) using make_scoped_resource(r, f, nd) |
7 | The 3rd template parameter, when true, allows the constructors to throw if the resource matches the "invalid" or "no-delete" value. |
namespace std
{
template<typename TDel, typename TRes, const bool throw_on_init_failure> class scoped_resource
{
public:
// 20.14.4.1 constructors
explicit scoped_resource(TDel&& arg, TRes&& resource, TRes no_delete_value);
explicit scoped_resource(const TDel& arg, const TRes& resource, TRes no_delete_value);
// 20.14.4.2 destructor
~scoped_resource();
// 20.14.4.3 modifiers
TRes release() throw();
scoped_resource& reset(TRes&& resource);
scoped_resource& reset(const TRes& resource);
scoped_resource& invoke(bool bRelease = true);
void operator()();
// 20.14.4.4 access
TRes get() const throw();
operator TRes() const throw();
TRes operator->() const;
TRes* operator&();
TDel& get_deleter();
const TDel& get_deleter() const;
// 20.14.4.5 validity checking
bool valid() const throw();
};
// 20.14.4.6 Default Generator Function
// Takes 3 parameters: The Resource, the Deleter, and a "invalid" or "no-delete" value
template<typename TDel, typename TRes>
scoped_resource<TDel, TRes, false> make_scoped_resource(TRes resource, TDel deleter, TRes nodelete)
{
return scoped_resource<TDel, TRes, false>(std::move(deleter), std::move(resource), std::move(nodelete));
}
// 20.14.4.7 Generator Function for throw_on_init
template<bool t_throw_on_init_failure, typename T, typename R>
scoped_resource<T, R, t_throw_on_init_failure> make_scoped_resource(R r, T t, R nd)
{
return scoped_resource<T, R, t_throw_on_init_failure>(std::move(t), std::move(r), std::move(nd));
}
}
scoped_resource
constructors take three arguments:
scoped_resource.
std::make_scoped_resource(resource, deleter, nodelete)
be used to generate a scoped_resource
, as in the following examples:
//
// Most common usage - close "handle" types on scope-exit, using a function...
extern HANDLE CreateEventW(PSECURITY_ATTRIBUTES pEventAttributes, BOOL bManualReset, BOOL bInitialState, PCWSTR pName);
extern void CloseHandle(HANDLE h);
extern int open(const char *pszFilename, int oflags, ...);
extern void close(int iFileDesc);
// Construction via Generic Generator with nullptr check, no-throw option
auto hEvent = std::make_scoped_resource(CreateEvent(nullptr, FALSE, FALSE, nullptr), CloseHandle, nullptr);
if(hEvent.valid()) // After Initialization check if (hEvent == nullptr)
//
// Construction via Generic Generator with nullptr check, throw on allocation failure...
try
{
auto hEvent = std::make_scoped_resource<true>(CreateEvent(nullptr, FALSE, FALSE, nullptr), CloseHandle, nullptr);
}
catch (const std::failed_resource_initialization &e)
{
// Deal with failure to CreateEvent, IOW hEvent == nullptr
}
//
// Construction via Generic Generator with -1 check, no-throw option
auto iFileDesc = std::make_scoped_resource(open("/tmp/log.txt", O_WRONLY|O_APPEND), close, -1);
if(iFileDesc.valid()) // Check validity
{
write(iFileDesc, ...);
}
//
// Construction via Generic Generator with -1 check, throw on allocation failure
try
{
auto iFileDesc = std::make_scoped_resource<true>(open("/tmp/log.txt", O_WRONLY|O_APPEND), close, -1);
write(iFileDesc, ...); // Can't get here if allocation failed
}
catch (const std::failed_resource_initialization &e)
{
// Deal with failure to open file, IOW iFileDesc == -1
}
Note: Manual constuction is also possible with scoped_resource
in much the same way as with scoped_resource_unchecked
. Also, leaving the decision on whether to throw, or require testing for allocation failures up to the user makes it possible to use scoped_resource
in places, such as kernel-mode code, where exceptions can not be tolerated.
scoped_resource
destructor will invoke the "deleter" function object passed to the constructor, passing it the tracked resource, as long as the scoped_resource
has not been "released" via a call to release()
, or invoke(true)
, and if the resource does not match the no-delete value. By this means the resource clean-up is carried out by the "deleter" function on scope-exit or unwind.
release()
prevents the scoped_resource
from invoking the "deleter" function in the destructor. It returns a copy of the resource so that if needed it may be assigned to a new tracking object, such as a scoped_resource
within a different scope.reset(resource)
causes the scoped_resource
to invoke the "deleter" function for any currently tracked and valid resource that has not been "released", and assigns a new resource to the scoped_resource
to be tracked. The scoped_resource
will be set to the "un-released" state when a new resource is assigned. The deleter function does not change. If the scoped_resource
was constructed with the throw-option, the new resource will be compared to the no-delete value passed to the constructor and std::failed_resource_initialization
exception will be thrown if it matches.invoke(true)
performs an early (pre-destructor) invocation of the "deleter" function for a valid resource. The default true parameter sets the scoped_resource
to the "released" state so that the deleter function will not be invoked again when the destructor is called, unless the object is subsequently "reset."invoke(false)
performs an early (pre-destructor) invocation of the "deleter" function for a valid resource. The false parameter leaves the scoped_resource
in the "un-released" state so that the deleter function will be invoked again when the destructor is called.operator()()
performs an early (pre-destructor) invocation of the deleter function, the same as if invoke(true)
had been called.TRes get() const throw()
returns the resource being managed by the scoped_resource
object.operator TRes() const throw()
returns the resource being managed by the scoped_resource
object.TRes operator->() const
returns the resource being managed by the scoped_resource
object.TRes* operator&()
returns a pointer to the resource being managed by the scoped_resource
object, where the scoped_resouce
has a private member of type TRes
such as TRes m_resource;
which is returned from this accessor with return &m_resource;
TDel& get_deleter()
returns a reference to the deleter function. This allows the caller to modify the deleter function if needed on a non-const object.const TDel& get_deleter() const
returns a reference to the deleter function.valid()
member may be used to compare the tracked resource to the no-delete value used when constructing the scoped_resource
object. It returns true if the resource is NOT equal to the no-delete value.
make_scoped_resource - Default Generator Function
make_scoped_resource_unchecked(resource, deleterfunc, no_delete_value)
is used to generate a scoped_resource
object. See the examples above in 20.14.3.1.
The absence of the true bool template parameter indicates that an exception should NOT be thrown if the tracked resource is assigned a value equal to the no_delete_value, or in other words, if(resource == no_delete_value)
. In this case the user is responsible for checking the validity of the resource via the valid()
member function. This allows scoped_resource
to be used in enviroments (such as kernel-mode code) where exceptions can not be tolerated.
make_scoped_resource<true> - Generator Function which allows throws on allocation failure
make_scoped_resource_unchecked<bool>(resource, deleterfunc, no_delete_value)
is used to generate a scoped_resource
object. See the examples above in 20.14.3.1.
The bool template parameter indicates whether or not a std::failed_resource_initialization
exception should be thrown if the tracked resource is assigned a value equal to the no_delete_value, or in other words:if(resource == no_delete_value)
.
unique_resource
1 | A unique_resource is an object s that manages a generic resource r such that a static clean-up function, f(r) will be invoked when s is destroyed (e.g., when leaving block scope (6.7)), as long as the resource passes a validity check. |
2 | The clean-up function f managed by unique_resource takes a single parameter of the type of the resource r so that s may invoke f(r) as f(r); , when s leaves the current scope
or is otherwise destructed. |
3 | f must be a function that can be passed and invoked as a template argument, known at compile time. The no-delete value must also be a static value that may be passed and evaluated as a template argument. This allows the compiler to generate the least possible amount of code for each unique_resource . |
4 | r may be accessed through cast operators as if it were of the type of the resource r. |
5 | Like scoped_resource, r is compared to a "no-delete value" prior to the invocation of f(r) . |
6 | There is no generator for unique_resource . It is designed for use in places where a generator is not convenient, and where the smallest generated code possible is needed. |
7 | The 3rd template parameter, when true, allows the constructors to throw if the resource matches the "invalid" or "no-delete" value. |
8 | Convenience macros are provided to simplify the declaration of template parameters. |
9 | As shown below, the reference implementation uses an implementation-defined class and a set of implementation defined helper classes to aid in deducing template parameters. While these classes are not part of the specification, the interfaces they expose must be carried forward to the actual unique_resource implementation which in this case inherits from the base class. |
namespace std
{
// 20.14.5.1 unique_resource_impl
template<typename TResource, typename TDeleter, TDeleter t_deleter, bool throw_on_init_failure, TResource t_nodelete>
class unique_resource_impl
{
private:
unique_resource_impl(const unique_resource_impl &);
void operator=(const unique_resource_impl &);
public:
unique_resource_impl() : m_resource(t_nodelete) // Allows for operator=(TResource) later...
{
}
explicit unique_resource_impl(TResource&& resource) : m_resource(std::move(resource))
{
if(throw_on_init_failure && t_nodelete == m_resource)
{
throw failed_resource_initialization();
}
}
explicit unique_resource_impl(const TResource& resource) : m_resource(resource)
{
if(throw_on_init_failure && t_nodelete == m_resource)
{
throw failed_resource_initialization();
}
}
~unique_resource_impl()
{
invoke(true);
}
TResource release() throw()
{
TResource tempcopy = m_resource;
m_resource = t_nodelete;
return tempcopy;
}
unique_resource_impl& reset(TResource&& resource)
{
invoke(false); // false because we are going to change it manually here...
m_resource = std::move(resource);
if(throw_on_init_failure && t_nodelete == m_resource)
{
throw failed_resource_initialization();
}
return *this;
}
unique_resource_impl& reset(TResource const& resource)
{
invoke(false); // false because we are going to change it manually here...
m_resource = resource;
if(throw_on_init_failure && t_nodelete == m_resource)
{
throw failed_resource_initialization();
}
return *this;
}
void operator()()
{
invoke(true);
}
TResource get() const throw()
{
return m_resource;
}
void operator=(TResource &&res)
{
reset(res);
}
void operator=(TResource const &res)
{
reset(res);
}
//
// Cast operator - for access to resource
// WARNING: This can cause make_scoped_resource*(R, T) to return a temporary that initializes something else! Be careful with usage!
// Nevertheless, it provides transparency for API calls, etc.
operator TResource() const throw()
{
return m_resource;
}
//
// operator-> for accessing members on pointer types
TResource operator->() const
{
return m_resource;
}
//
// operator& for getting the address of the resource - not const!
TResource* operator&()
{
return &m_resource;
}
const TDeleter get_deleter() const
{
return t_deleter;
}
//
// invoke...(e.g. early invocation), allows for repeated
// invocation, if bRelease = false. Use with caution!
unique_resource_impl& invoke(bool bRelease = true)
{
if(valid())
{
t_deleter(m_resource);
if(bRelease)
{
m_resource = t_nodelete;
}
}
return *this;
}
//
// Check for a valid / usable resource
bool __inline valid() const throw()
{
return m_resource != t_nodelete;
}
};
// 20.14.5.2 implementation specific helper classes used to deduce template parameters
//
// Extract First Parameter Type
//
template <typename T>
struct TFuncInfo1;
#if defined(_WIN32) && !defined(_WIN64) // Support Microsoft calling conventions
template <typename TReturn, typename TParam>
struct TFuncInfo1<TReturn (__stdcall *)(TParam)>
{
typedef TReturn tReturnType;
typedef TParam tParameter1;
};
template <typename TReturn, typename TParam>
struct TFuncInfo1<TReturn (__cdecl *)(TParam)>
{
typedef TReturn tReturnType;
typedef TParam tParameter1;
};
template <typename TReturn, typename TParam>
struct TFuncInfo1<TReturn (__fastcall *)(TParam)>
{
typedef TReturn tReturnType;
typedef TParam tParameter1;
};
#else // All other compilers
template <typename TReturn, typename TParam>
struct TFuncInfo1<TReturn (*)(TParam)>
{
typedef TReturn tReturnType;
typedef TParam tParameter1;
};
#endif // _WIN32 && !_WIN64
// 20.14.5.3 unique_resource (reference implementation)
template<typename TDeleter,
TDeleter t_deleter,
bool throw_on_init_failure = false, // Set to true if you want throw(failed_resource_initialization) when m_resource == t_nodelete_value
typename TFuncInfo1<TDeleter>::tParameter1 t_nodelete_value = static_cast<typename TFuncInfo1<TDeleter>::tParameter1>(nullptr)>
struct unique_resource : public
unique_resource_impl<typename TFuncInfo1<TDeleter>::tParameter1, TDeleter, t_deleter, throw_on_init_failure, t_nodelete_value>
{
typedef typename TFuncInfo1<TDeleter>::tParameter1 TResource;
unique_resource() :
unique_resource_impl<typename TFuncInfo1<TDeleter>::tParameter1, TDeleter, t_deleter, throw_on_init_failure, t_nodelete_value>()
{
}
unique_resource(TResource &&resource) :
unique_resource_impl<typename TFuncInfo1<TDeleter>::tParameter1, TDeleter, t_deleter, throw_on_init_failure, t_nodelete_value>(std::move(resource))
{
}
unique_resource(TResource const &resource) :
unique_resource_impl<typename TFuncInfo1<TDeleter>::tParameter1, TDeleter, t_deleter, throw_on_init_failure, t_nodelete_value>(resource)
{
}
void operator=(TResource &&resource)
{
this->reset(resource);
}
void operator=(TResource const &resource)
{
this->reset(resource);
}
};
//
// 20.14.5.4 unique_resource (specification)
template<typename TDeleter,
TDeleter t_deleter,
bool throw_on_init_failure = false, // Set to true if you want throw(failed_resource_initialization) when m_resource == t_nodelete_value
typename TFuncInfo1<TDeleter>::tParameter1 t_nodelete_value = static_cast<typename TFuncInfo1<TDeleter>::tParameter1>(nullptr)>
class unique_resource
{
typedef typename TFuncInfo1<TDeleter>::tParameter1 TResource;
// 20.14.5.4.1 - unique_resource constructors
unique_resource();
unique_resource(TResource &&resource);
unique_resource(TResource const &resource);
// 20.14.5.4.2 - unique_resource destructor
~unique_resource();
// 20.14.5.4.3 - unique_resource modifiers
TResource release() throw();
unique_resource_impl& reset(TResource&& resource);
unique_resource_impl& reset(TResource const& resource);
unique_resource_impl& invoke(bool bRelease = true);
void operator()();
void operator=(TResource &&resource);
void operator=(TResource const &resource);
// 20.14.5.4.4 - unique_resource accessors
TResource get() const throw();
operator TResource() const throw();
TResource operator->() const;
TResource* operator&();
const TDeleter get_deleter() const;
// 20.14.5.4.5 - unique_resource validity checking
bool __inline valid() const throw();
};
//
// 20.14.5.5 Helper Macros
// The following macros simplify template parameters for unique_resource
#define UNIQUE_DELETER(deleter_fn) decltype(&deleter_fn), deleter_fn, false
#define UNIQUE_DELETER_THROW(deleter_fn) decltype(&deleter_fn), deleter_fn, true
#define UNIQUE_DELETER_NOTHROW(deleter_fn) decltype(&deleter_fn), deleter_fn, false
}
unique_resource_impl
unique_resource
with its implementation specific classes the accomodate template deduction. All code shown in the reference implementation is provided as an example only and not part of an official specification. Each actual implemenation will need to supply it's own methods of providing adequate template deduction methods to support construction of a unique_resource
as shown in section 20.14.5.4.1.
unique_resource
. An implementation that can satisify the construction specification shown in section 20.14.5.4.1 below may or may not need these helper classes (specifically) TFuncInfo
.
unique_resource
unique_resource
and how it utilizes template metaprogramming to deduce template arguments for the construction of unique_resource
and it's implementation specific base class unique_resource_impl
(20.14.5.1) to satisify the construction specifications laid out in section 20.14.5.4.1.
unique_resource
unique_resource
constructionunique_resource
may be constructed using one (1) or zero (0) arguments. This is possible because the template parameters supplied provide a deleter function used to clean-up the resource on unique_resource
destruction. A zero parameter constructor creates an object that has no resource to track until a subsequent assignment or reset.
Template Parameters:
std::failed_resource_initialization
if the resource is assigned (or constructed with) a value matching the no-delete or "invalid" value. The no-delete value must be of the type of the resource which is deduced from the argument required by the deleter function.nullptr
after having been cast (static_cast
to the type of the resource.) The no-delete value must be a static value known at compile time that may be passed as a template parameter and used in a comparison withing the invoke() method or the destructor.unique_resource
is meant to be used in places where:
make_scoped_resource()
is unsuitable or awkward, such as is the case for class member variables that can not be declared as auto
.unique_resource
objects being constructed:
//
// Most common usage - close "handle" types on scope-exit or class destruction...
extern HANDLE OpenFileW(const wchar_t *pwzFilename);
extern HANDLE CreateEventW(PSECURITY_ATTRIBUTES pEventAttributes, BOOL bManualReset, BOOL bInitialState, PCWSTR pName);
extern void SetEvent(HANDLE hEvent);
extern void CloseHandle(HANDLE h);
extern int open(const char *pszFilename, int oflags, ...);
extern void close(int iFileDesc);
extern ssize_t pwrite(int iFileDes, const void *pBuf, size_t szBytes, off_t offset);
class MyDataFile
{
private:
const std::unique_resource<UNIQUE_DELETER(CloseHandle)> m_hWriteSignaledEvent; // NOTE: Using helper macro
std::unique_resource<decltype(&CloseHandle), CloseHandle, INVALID_HANDLE_VALUE> m_hFile; // NOTE: Not using helper macro, also with non-default no-delete value
std::unique_resource<UNIQUE_DELETER_NOTHROW(SetEvent)> hSignalThread; // NOTE: In this case we only want a .valid() event handle if it is created/assigned later - using default constructor
std::vector<unsigned char> m_vData;
public:
MyDataFile(const wchar_t *pwzFileName) :
m_hWriteSignaledEvent(CreateEvent(nullptr, FALSE, FALSE, nullptr)), // Note, very simple construction
m_hFile(OpenFileW(pwzFileName)) // Notice that we let m_hSignalThread use the default constructor, it starts with .valid() == false
{
}
.
.
.
void SetEventToSignalOnClassDestruction(HANDLE hEvent)
{
m_hSignalThread = hEvent; // Contrived example, but showing assignment operator after default construction...
// When this class is destroyed, the event handle will be passed to SetEvent. Also happens to an existing event handle prior to assignment of a new value.
}
DWORD WriteOnSignal()
{
DWORD dwWait = WaitForSingleObject(m_hWriteSignaledEvent, INFINITE);
if(false == m_hFile.valid()) // Use validation method
{
return;
}
DWORD dwWritten = 0;
if(WAIT_OBJECT_O == dwWait)
{
WriteFile(m_hFile, &m_vData, m_vData.size(), &dwWritten, nullptr);
}
return dwWritten;
}
};
//
// Example using throw option...
void DumpVector(const char *pszFilename, std::vector<unsigned char> &vBuffer, off_t where)
{
try
{
std::unique_resource<UNIQUE_DELETER_THROW(close, -1)> iFileDesc(open(pszFilename, O_RDWR|O_APPEND));
if(pwrite(iFileDesc, &vBuffer[0], vBuffer.size(), where) != vBuffer.size())
{
throw write_failed; // example, not defined here...
}
}
catch (const std::failed_resource_initialization &e)
{
// Deal with error (file open failed) condition!
}
}
unique_resource
destructor will invoke the "deleter" function object supplied as a template parameter, passing it the tracked resource, as long as the unique_resource
has not been "released" via a call to release()
, or invoke(true)
, and does not match the no-delete value. By this means the resource clean-up is carried out by the "deleter" function on scope-exit or unwind.
release()
prevents the unique_resource
from invoking the "deleter" function in the destructor, by assigning the no-delete value to the internally tracked resource. Returns a copy of the resource so that if needed it may be assigned to another tracking object, such as a unique_resource
in another instance of a class (e.g. in a copy constructor).reset(resource)
causes the unique_resource
to invoke the "deleter" function for any currently tracked and valid resource that has not been "released", and assigns a new resource to the unique_resource
to be tracked. The unique_resource
will be set to the "un-released" state when a new resource is assigned. The deleter function does not change. If the unique_resource
was constructed with the throw-option, the new resource will be compared to the no-delete value passed to the constructor and std::failed_resource_initialization
exception will be thrown if it matches.invoke(true)
performs an early (pre-destructor) invocation of the "deleter" function for a valid resource. The default true parameter sets the unique_resource
to the "released" state so that the deleter function will not be invoked again when the destructor is called, unless the object is subsequently "reset."invoke(false)
performs an early (pre-destructor) invocation of the "deleter" function for a valid resource. The false parameter leaves the unique_resource
in the "un-released" state so that the deleter function will be invoked again when the destructor is called.operator()()
performs an early (pre-destructor) invocation of the deleter function for a valid resource, the same as if invoke(true)
had been called.operator=(TResource &&res)
performs an early (pre-destructor) invocation of the deleter function for a valid, assigned resource, and assigns a tracks a new resource the same as if reset(res)
had been called.operator=(TResource const &res)
performs an early (pre-destructor) invocation of the deleter function for a valid, assigned resource, and assigns a tracks a new resource the same as if reset(res)
had been called.TResource get() const throw()
returns the resource being managed by the unique_resource
object.operator TResource() const throw()
returns the resource being managed by the unique_resource
object.TResource operator->() const
returns the resource being managed by the unique_resource
object.TResource* operator&()
returns a pointer to the resource being managed by the unique_resource
object, where the unique_resource
has a private member of type TResource
such as TResource m_resource;
which is returned from this accessor with return &m_resource;
const TDeleter& get_deleter() const
returns a reference to the deleter function. The deleter function can not be modified.valid()
member may be used to compare the tracked resource to the no-delete value specified in the template parameters. It returns true if the resource is NOT equal to the no-delete value.
unique_resource
:
UNIQUE_DELETER(delter_fn)
- supplies the type of the deleter function, and the deleter function parameters, supplying false for the throw option.UNIQUE_DELETER_THROW(delter_fn)
- supplies the type of the deleter function, and the deleter function parameters, supplying true for the throw option.UNIQUE_DELETER_NOTHROW(delter_fn)
- supplies the type of the deleter function, and the deleter function parameters, supplying false for the throw option.