module
CnxUniquePtrCnxUniquePtr(T)
is a struct template similar to C++'s std::unique_ptr
or Rust's Box
for a "smart" pointer type that maintains (when used correctly) unique ownership of, and manages an object through, a contained pointer. The contained object is disposed of ("freed") when the CnxUniquePtr(T)
is explicitly freed, when it goes out of scope when declared with the unique_scoped(T)
tagged declaration, or when reset
(cnx_unique_ptr_reset
) is called on the CnxUniquePtr(T)
.
CnxUniquePtr(T)
is allocator aware and stores its associated allocator in itself.
The contained object is "freed" by a CnxDeleter
, a function taking a T*
and a CnxAllocator
, whose task is to properly cleanup/free/etc. anything associated with the T
. This deleter can be supplied as a template parameter, UNIQUE_DELETER
when instantiating the CnxUniquePtr(T)
template. This template parameter is optional, and if not supplied the deleter used will be the default deleter, which will simply deallocate the T
with the allocator associated with the given CnxUniquePtr(T)
instance.
CnxUniquePtr(T)
can own dynamically allocated array types, and provided functionality will be slightly different, though comparable, when T
is an array type vs a singular type. To instantiate the template for an array type, the type must be typedef
ed, e.g.: typedef int int_arr[];
, and the typedef provided as the UNIQUE_T
template parameter.
Instantiation Requirements:
- a
typedef
of your type to to provide an alphanumeric name for it (so it can be used in template and macro parameters)
We recommend two routes for providing instantations for user-defined types, either:
- Provide the instantiation together with your type's public interface and implementation OR
- Provide it as a separate "template instantiation" .h/.c file pair.
We recommend option one when your type is typically used in tandem with the template instantation or if it requires it for its public interface. We recommend option two for all other circumstances, so that users don't pay the price (compile time, and potentially code size) of template instantiations if they don't use them.
Instantiation Mode Parameters
These signal to the implementation to instantiation the declarations, definitions, or both, for the template.
UNIQUE_DECL
(Optional) - Defining this to true signals to the implementation to declare the required type declarations and definitions, and any required or public function declarations when you include<Cnx/
. No functions will be defined. This is optional (but signals intent explicitly) - If required template parameters are defined andUniquePtr.h> UNIQUE_DECL
is not, then this will be inferred as true by default.UNIQUE_IMPL
- Defining this to true signals to the implementation to define the functions necessary for the template instantation when you include<Cnx/
. This is not required to be paired withUniquePtr.h> UNIQUE_DECL
(you can declare the template, in a header for example, prior to defining it in a source file), but it is eventually required to be used in exactly one translation unit in the resulting binary in order to provide definitions for the template's functions. Failing to complete this instantiation-mode exactly once in exactly one translation unit in your build will cause linking errors due to missing or multiply defined functions.
Template Parameters
These provide the type or value parameters that the template is parameterized on to the template implementation. These should be #define
d to their appropriate types/values.
UNIQUE_T
- The type to be stored and managed in the unique_ptr (e.g.u32
orCnxString
). This is required.UNIQUE_DELETER
- The deleter function to free the memory associated with the managed type and perform any necessary associated cleanup. This function must take a particular signature: For a typeY
that evaluates to either:
T
(UNIQUE_T
) ifT
is NOT an array type- The element type of the array type
T
ifT
IS an array type. e.g. for an array typeT
, and an instancet
,Y
would betypeof(t[0])
The signature must be void name(Y* object, CnxAllocator allocator);
where object
is a pointer to the managed object and allocator
is the allocator associated with the object (the one that allocated it, and will free it).
Example using instantiation method one:
// in MyStruct.h // include for `TRUE` (could just use `1`) #include <Cnx/Def.h> // include for `i32` #include <Cnx/BasicTypes.h> typedef struct { i32 one; i32 two; i32 three; } MyStruct; // instantiate `CnxUniquePtr(MyStruct)` declaration #define UNIQUE_T MyStruct #define UNIQUE_DECL TRUE #define UNIQUE_UNDEF_PARAMS TRUE #include <Cnx/UniquePtr.h> #undef UNIQUE_UNDEF_PARAMS // in MyStruct.c #include "MyStruct.h" // instantiate `CnxUniquePtr(MyStruct)` declaration #define UNIQUE_T MyStruct #define UNIQUE_IMPL TRUE #define UNIQUE_UNDEF_PARAMS TRUE #include <Cnx/UniquePtr.h> #undef UNIQUE_UNDEF_PARAMS void takes_ownership(CnxUniquePtr(MyStruct) to_take); void example(void) { // create a uniquely owned `MyStruct` unique_scoped(MyStruct) my_ptr = cnx_make_unique(MyStruct, .one = 2, .two = 4, .three = 1); // transfer ownership of the `MyStruct` object managed by `my_ptr` to `takes_ownership` // `my_ptr` now contains `nullptr` takes_ownership(ptr_move(my_ptr)); // aside: `ptr_move` from is nearly equivalent to `move`, but it ensures that the contained // pointer is set to the correct `nullptr` constant (`NULL` is not necessarily 0 on all // platforms, and `move` is implemented with zero-setting on the moved-from value). // on platforms where `NULL` is guaranteed to be `0`, you can safely use `move` instead of // `ptr_move` }
Example using instantiation method two:
// in MyStruct.h // include for `i32` #include <Cnx/BasicTypes.h> typedef struct { i32 one; i32 two; i32 three; } MyStruct; // in CnxUniquePtrMyStruct.h // include for `TRUE` (could just use `1`) #include <Cnx/Def.h> #include "MyStruct.h" // instantiate `CnxUniquePtr(MyStruct)` declaration #define UNIQUE_T MyStruct #define UNIQUE_DECL TRUE #define UNIQUE_UNDEF_PARAMS TRUE #include <Cnx/UniquePtr.h> #undef UNIQUE_UNDEF_PARAMS // in CnxUniquePtrMyStruct.c #include "CnxUniquePtrMyStruct.h" // instantiate `CnxUniquePtr(MyStruct)` declaration #define UNIQUE_T MyStruct #define UNIQUE_IMPL TRUE #define UNIQUE_UNDEF_PARAMS TRUE #include <Cnx/UniquePtr.h> #undef UNIQUE_UNDEF_PARAMS // in MyStruct.c void takes_ownership(CnxUniquePtr(MyStruct) to_take); void example(void) { // create a uniquely owned `MyStruct` unique_scoped(MyStruct) my_ptr = cnx_make_unique(MyStruct, .one = 2, .two = 4, .three = 1); // transfer ownership of the `MyStruct` object managed by `my_ptr` to `takes_ownership` // `my_ptr` now contains `nullptr` takes_ownership(ptr_move(my_ptr)); // aside: `ptr_move` from is nearly equivalent to `move`, but it ensures that the contained // pointer is set to the correct `nullptr` constant (`NULL` is not necessarily 0 on all // platforms, and `move` is implemented with zero-setting on the moved-from value). // on platforms where `NULL` is guaranteed to be `0`, you can safely use `move` instead of // `ptr_move` }
Like other Cnx templates, CnxUniquePtr(T)
provides its type-agnostic usage through a vtable pointer contained in the struct, and provides macros which wrap usage of the vtable, making access simpler. If you prefer to not use this method of access, you can call the typed functions directly by in-fixing the contained type in the associated function name. IE: for a CnxUniquePtr(i32)
, i32_ptr
, the equivalent function call for cnx_unique_ptr_release
would be cnx_unique_ptr_i32_release(&i32_ptr)
.
Defines
- #define CnxUniquePtr(T)
- macro alias for a
CnxUniquePtr(T)
maintaining ownership of an allocatedT
- #define cnx_unique_ptr_new(T)
- Creates a new uniquely, owned zero-initialized
T
- #define cnx_unique_ptr_new_with_allocator(T, allocator)
- Creates a new uniquely owned, zero-initialized
T
, allocated with the given allocator. - #define cnx_unique_ptr_new_with_capacity(T, capacity)
- Creates a new uniquely owned, zero-initialized, dynamically allocated array.
- #define cnx_unique_ptr_new_with_capacity_and_allocator(T, capacity, allocator)
- Creates a new uniquely owned, zero-initialized, dynamically allocated array, allocated with the given allocator.
- #define cnx_unique_ptr_from(T, ptr)
- Creates a new
CnxUniquePtr(T)
managing the given pointer. - #define cnx_unique_ptr_from_with_allocator(T, ptr, allocator)
- Creates a new
CnxUniquePtr(T)
managing the given pointer, associated with the given allocator. - #define cnx_unique_ptr_free(T, self)
- Frees the given
CnxUniquePtr(T)
, calling the associated deleter on the managed object. - #define cnx_unique_ptr_release(self)
- Releases the pointer managed by
self
from ownership. - #define cnx_unique_ptr_reset(self, new_ptr)
- Frees the object currently managed by
self
, and replaces it with the one pointed to bynew_ptr
- #define cnx_unique_ptr_swap(self, other_ptr)
- Swaps the managed objects of
self
and theCnxUniquePtr(T)
pointed to byother_ptr
- #define cnx_unique_ptr_get_deleter(self)
- Returns the
CnxDeleter(T)
function pointer associated with theCnxUniquePtr(T)
instantiationself
is an instance of. - #define cnx_unique_ptr_as_bool(self)
- Returns whether the given
CnxUniquePtr(T)
currently manages an object. - #define cnx_unique_ptr_get(self)
- Returns a pointer to the object managed by
self
- #define cnx_unique_ptr_get_const(self)
- Returns a pointer-to-const to the object managed by
self
- #define cnx_unique_ptr_get_mut(self)
- Returns a pointer-to-non-const to the object managed by
self
- #define cnx_unique_ptr_at(self, index)
- Returns a reference to the element at
index
in the array managed byself
- #define cnx_unique_ptr_at_const(self, index)
- Returns a reference-to-const to the element at
index
in the array managed byself
- #define cnx_unique_ptr_at_mut(self, index)
- Returns a reference-to-non-const to the element at
index
in the array managed byself
- #define cnx_make_unique(T, ...)
- Creates a
CnxUniquePtr(T)
managing aT
initialized with the given list of (potentially designated) initializers. - #define cnx_make_unique_with_allocator(T, allocator, ...)
- Creates a
CnxUniquePtr(T)
managing aT
initialized with the given list of (potentially designated) initializers, allocated with the given allocator. - #define cnx_make_unique_array(T, capacity)
- Creates a
CnxUniquePtr(T)
managing the array typeT
with the given initial capacity. - #define cnx_make_unique_array_with_allocator(T, capacity, allocator)
- Creates a
CnxUniquePtr(T)
managing the array typeT
with the given initial capacity, allocated with the given allocator. - #define UniquePtr(T)
- Declare a
CnxUniquePtr(T)
with this declaration attribute/type to ensure it (and its managed object) is automatically freed when theCnxUniquePtr(T)
goes out of scope.
Define documentation
#define CnxUniquePtr(T)
macro alias for a CnxUniquePtr(T)
maintaining ownership of an allocated T
CnxUniquePtr(T)
is a template for a "smart" pointer type that maintains (when used correctly) unique ownership of and manages an object through a contained pointer. The contained object is disposed of ("freed") when the CnxUniquePtr(T)
is explicitly freed, when it goes out of scope when declared with the UniquePtr(T)
tagged declaration, or when reset
(Cnx_unique_ptr_reset
) is called on the CnxUniquePtr(T)
.
CnxUniquePtr(T)
is allocator aware and stores its associated allocator in itself.
The contained object is "freed" by a CnxDeleter
, a function taking a T*
and a CnxAllocator
, whose task is to properly cleanup/free/etc. anything associated with the T
. This deleter can be supplied as a template parameter, UNIQUE_DELETER
when instantiating the CnxUniquePtr(T)
template. This template parameter is optional, and if not supplied the deleter used will be the default deleter, which will simply deallocate the T
with the allocator associated with the given CnxUniquePtr(T)
instance.
CnxUniquePtr(T)
can own dynamically allocated array types, and provided functionality will be slightly different, though comparable, when T
is an array type or a singular type. To instantiate the template for an array type, the type must be typedef
ed, e.g.: typedef int int_arr[];
, and the typedef provided as the UNIQUE_T
template parameter.
Example:
// include for `TRUE` #include <Cnx/Def.h> // include for `i32` #include <Cnx/BasicTypes.h> // just for example's sake typedef struct { i32 one; i32 two; i32 three; } MyStruct; // instantiate `CnxUniquePtr(MyStruct)` #define UNIQUE_T MyStruct #define UNIQUE_DECL TRUE #define UNIQUE_IMPL TRUE #define UNIQUE_UNDEF_PARAMS TRUE #include <Cnx/UniquePtr.h> #undef UNIQUE_UNDEF_PARAMS void takes_ownership(CnxUniquePtr(MyStruct) to_take); void example(void) { // create a uniquely owned `MyStruct` UniquePtr(MyStruct) my_ptr = cnx_make_unique(MyStruct, .one = 2, .two = 4, .three = 1); // transfer ownership of the `MyStruct` object managed by `my_ptr` to `takes_ownership` // `my_ptr` now contains `nullptr` takes_ownership(ptr_move(my_ptr)); }
Like other Cnx templates, CnxUniquePtr(T)
provides its type-agnostic usage through a vtable pointer contained in the struct, and provides macros which wrap usage of the vtable, making access simpler. If you prefer to not use this method of access, you can call the typed functions directly by in-fixing the contained type in the associated function name. IE: for a CnxUniquePtr(i32)
, i32_ptr
, the equivalent function call for cnx_unique_ptr_release
would be cnx_unique_ptr_i32_release(&i32_ptr)
.
#define cnx_unique_ptr_new(T)
Creates a new uniquely, owned zero-initialized T
Parameters | |
---|---|
T | - The type to manage in the created CnxUniquePtr(T) |
Returns | a CnxUniquePtr(T) managing a zero-initialized T |
#define cnx_unique_ptr_new_with_allocator(T, allocator)
Creates a new uniquely owned, zero-initialized T
, allocated with the given allocator.
Parameters | |
---|---|
T | - The type to manage in the created CnxUniquePtr(T) |
allocator | - The allocator to allocator and eventually free the managed T with |
Returns | a CnxUniquePtr(T) managing a zero-initialized T |
#define cnx_unique_ptr_new_with_capacity(T, capacity)
Creates a new uniquely owned, zero-initialized, dynamically allocated array.
Parameters | |
---|---|
T | - The array type to manage in the created CnxUniquePtr(T) . Must be a typedef to an array type (e.g.: typedef i32 i32_array[]; ). |
capacity | - The capacity of the array, in number of elements |
Returns | a CnxUniquePtr(T) managing a zero-initialized, dynamically allocated array. |
#define cnx_unique_ptr_new_with_capacity_and_allocator(T, capacity, allocator)
Creates a new uniquely owned, zero-initialized, dynamically allocated array, allocated with the given allocator.
Parameters | |
---|---|
T | - The array type to manage in the created CnxUniquePtr(T) . Must be a typedef to an array type (e.g.: typedef i32 i32_array[]; ). |
capacity | - The capacity of the array, in number of elements |
allocator | - The allocator to allocator and eventually free the managed array with |
Returns | a CnxUniquePtr(T) managing a zero-initialized, dynamically allocated array. |
#define cnx_unique_ptr_from(T, ptr)
Creates a new CnxUniquePtr(T)
managing the given pointer.
Parameters | |
---|---|
T | - The type to manage in the created CnxUniquePtr(T) |
ptr | - The ptr to manage with the CnxUniquePtr(T) . |
Returns | a CnxUniquePtr(T) managing the given pointer |
T
may be either an array type or a single object.
Useful when working with a legacy API that returns raw allocated object(s), but you still want use a CnxUniquePtr(T)
to maintain ownership. If you have control over when allocation occurs, prefer cnx_unique_ptr_new
, cnx_make_unique
, or derivatives.
#define cnx_unique_ptr_from_with_allocator(T, ptr, allocator)
Creates a new CnxUniquePtr(T)
managing the given pointer, associated with the given allocator.
Parameters | |
---|---|
T | - The type to manage in the created CnxUniquePtr(T) |
ptr | - The ptr to manage with the CnxUniquePtr(T) . |
allocator | - The allocator to allocator and eventually free the managed T with |
Returns | a CnxUniquePtr(T) managing the given pointer |
T
may be either an array type or a single object.
Useful when working with a legacy API that returns raw allocated object(s), but you still want use a CnxUniquePtr(T)
to maintain ownership. If you have control over when allocation occurs, prefer cnx_unique_ptr_new_with_allocator
or cnx_make_unique_with_allocator
#define cnx_unique_ptr_free(T, self)
Frees the given CnxUniquePtr(T)
, calling the associated deleter on the managed object.
Parameters | |
---|---|
T | - The type managed in the given CnxUniquePtr(T) |
self | - The CnxUniquePtr(T) to free |
This should generally only need to be called explicitly in particularly rare circumstances. In typical usage, CnxUniquePtr(T)
s should be declared with the UniquePtr(T)
tag, so cleanup is performed automatically when the associated CnxUniquePtr(T)
goes out of scope
#define cnx_unique_ptr_release(self)
Releases the pointer managed by self
from ownership.
Parameters | |
---|---|
self | - The CnxUniquePtr(T) to release ownership of its managed object |
Returns | the pointer to the object previously managed by self |
Useful when you need to transfer ownership of the managed object from yourself to a another API that uses a different method of ownership management
#define cnx_unique_ptr_reset(self, new_ptr)
Frees the object currently managed by self
, and replaces it with the one pointed to by new_ptr
Parameters | |
---|---|
self | - The CnxUniquePtr(T) to have its managed object replaced |
new_ptr | - The pointer to the object to transfer ownership of to self |
#define cnx_unique_ptr_swap(self, other_ptr)
Swaps the managed objects of self
and the CnxUniquePtr(T)
pointed to by other_ptr
Parameters | |
---|---|
self | - The CnxUniquePtr(T) to have its managed object swapped |
other_ptr | - Pointer to the CnxUniquePtr(T) to swap managed objects with |
self
and the CnxUniquePtr(T)
pointed to by other_ptr
must manage the same type
#define cnx_unique_ptr_get_deleter(self)
Returns the CnxDeleter(T)
function pointer associated with the CnxUniquePtr(T)
instantiation self
is an instance of.
Parameters | |
---|---|
self | - The CnxUniquePtr(T) to get the associated deleter of |
Returns | the CnxDeleter(T) function pointer associated with self |
#define cnx_unique_ptr_as_bool(self)
Returns whether the given CnxUniquePtr(T)
currently manages an object.
Parameters | |
---|---|
self | - The CnxUniquePtr(T) to check if currently manages an object |
Returns | true if self currently manages an object, otherwise false |
#define cnx_unique_ptr_get(self)
Returns a pointer to the object managed by self
Parameters | |
---|---|
self | - The CnxUniquePtr(T) to get the managed object of |
Returns | const correct pointer to the managed object |
This is const correct:
- If
self
is const, this will return a pointer-to-const of the managed object. - Otherwise, this will return a "normal" pointer to the managed object. If you want to explicitly get a pointer-to-const or pointer-to-not-const, use
cnx_unique_ptr_get_const
orcnx_unique_ptr_get_mut
, respectively.
#define cnx_unique_ptr_get_const(self)
Returns a pointer-to-const to the object managed by self
Parameters | |
---|---|
self | - The CnxUniquePtr(T) to get the managed object of |
Returns | pointer-to-const to the managed object |
#define cnx_unique_ptr_get_mut(self)
Returns a pointer-to-non-const to the object managed by self
Parameters | |
---|---|
self | - The CnxUniquePtr(T) to get the managed object of |
Returns | pointer-to-non-const to the managed object |
#define cnx_unique_ptr_at(self, index)
Returns a reference to the element at index
in the array managed by self
Parameters | |
---|---|
self | - The CnxUniquePtr(T) to get the element from |
index | - The index of the element in the array managed by self |
Returns | const correct reference to the array element |
This is const correct:
- If
self
is const, this will return a reference-to-const of the array element. - Otherwise, this will return a "normal" reference to the array element. If you want to explicitly get a reference-to-const or reference-to-not-const, use
cnx_unique_ptr_at_const
orcnx_unique_ptr_at_mut
, respectively.
#define cnx_unique_ptr_at_const(self, index)
Returns a reference-to-const to the element at index
in the array managed by self
Parameters | |
---|---|
self | - The CnxUniquePtr(T) to get the element from |
index | - The index of the element in the array managed by self |
Returns | reference-to-const to the array element |
#define cnx_unique_ptr_at_mut(self, index)
Returns a reference-to-non-const to the element at index
in the array managed by self
Parameters | |
---|---|
self | - The CnxUniquePtr(T) to get the element from |
index | - The index of the element in the array managed by self |
Returns | reference-to-non-const to the array element |
#define cnx_make_unique(T, ...)
Creates a CnxUniquePtr(T)
managing a T
initialized with the given list of (potentially designated) initializers.
Parameters | |
---|---|
T | - The type the CnxUniquePtr(T) will manage |
... | - The list of initializers to initialize the T with |
Returns | a CnxUniquePtr(T) managing a newly allocated T |
#define cnx_make_unique_with_allocator(T, allocator, ...)
Creates a CnxUniquePtr(T)
managing a T
initialized with the given list of (potentially designated) initializers, allocated with the given allocator.
Parameters | |
---|---|
T | - The type the CnxUniquePtr(T) will manage |
allocator | - The CnxAllocator to allocate and eventually free the T with |
... | - The list of initializers to initialize the T with |
Returns | a CnxUniquePtr(T) managing a newly allocated T |
#define cnx_make_unique_array(T, capacity)
Creates a CnxUniquePtr(T)
managing the array type T
with the given initial capacity.
Parameters | |
---|---|
T | - The array type the CnxUniquePtr(T) will manage. Must be a typedef to an array type (e.g.: typedef i32 i32_array[]; ) |
capacity | - The capacity of the array in number of elements |
Returns | a CnxUniquePtr(T) managing a newly allocated dynamic array of capacity capacity |
#define cnx_make_unique_array_with_allocator(T, capacity, allocator)
Creates a CnxUniquePtr(T)
managing the array type T
with the given initial capacity, allocated with the given allocator.
Parameters | |
---|---|
T | - The array type the CnxUniquePtr(T) will manage. Must be a typedef to an array type (e.g.: typedef i32 i32_array[]; ) |
capacity | - The capacity of the array in number of elements |
allocator | - The CnxAllocator to allocate and eventually free the array with |
Returns | a CnxUniquePtr(T) managing a newly allocated dynamic array of capacity capacity |
#define UniquePtr(T)
Declare a CnxUniquePtr(T)
with this declaration attribute/type to ensure it (and its managed object) is automatically freed when the CnxUniquePtr(T)
goes out of scope.
Parameters | |
---|---|
T | - The type managed by the CnxUniquePtr(T) |