module
CnxSharedPtrCnxSharedPtr(T)
is a struct template similar to C++'s std::shared_ptr
or Rust's Box
for a "smart" pointer type that maintains (when used correctly) shared ownership of, and manages an object through, a contained pointer. The shared object is disposed of ("freed") when all CnxSharedPtr(T)
s sharing its ownership have been explicitly freed, when all owners have gone out of scope when declared with the shared_scoped(T)
tagged declaration, when reset
(cnx_shared_ptr_reset
) has been called on all owners, or some combination thereof.
CnxSharedPtr(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, SHARED_DELETER
when instantiating the CnxSharedPtr(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 CnxSharedPtr(T)
instance.
CnxSharedPtr(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 SHARED_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.
SHARED_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 andSharedPtr.h> SHARED_DECL
is not, then this will be inferred as true by default.SHARED_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 withSharedPtr.h> SHARED_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.
SHARED_T
- The type to be stored and managed in the shared_ptr (e.g.u32
orCnxString
). This is required.SHARED_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
(SHARED_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 `CnxSharedPtr(MyStruct)` declaration #define SHARED_T MyStruct #define SHARED_DECL TRUE #define SHARED_UNDEF_PARAMS TRUE #include <Cnx/SharedPtr.h> #undef SHARED_UNDEF_PARAMS // in MyStruct.c #include "MyStruct.h" // instantiate `CnxSharedPtr(MyStruct)` declaration #define SHARED_T MyStruct #define SHARED_IMPL TRUE #define SHARED_UNDEF_PARAMS TRUE #include <Cnx/SharedPtr.h> #undef SHARED_UNDEF_PARAMS void takes_ownership(CnxSharedPtr(MyStruct) to_take); void example(void) { // create a sharedly owned `MyStruct` shared_scoped(MyStruct) my_ptr = cnx_make_shared(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 CnxSharedPtrMyStruct.h // include for `TRUE` (could just use `1`) #include <Cnx/Def.h> #include "MyStruct.h" // instantiate `CnxSharedPtr(MyStruct)` declaration #define SHARED_T MyStruct #define SHARED_DECL TRUE #define SHARED_UNDEF_PARAMS TRUE #include <Cnx/SharedPtr.h> #undef SHARED_UNDEF_PARAMS // in CnxSharedPtrMyStruct.c #include "CnxSharedPtrMyStruct.h" // instantiate `CnxSharedPtr(MyStruct)` declaration #define SHARED_T MyStruct #define SHARED_IMPL TRUE #define SHARED_UNDEF_PARAMS TRUE #include <Cnx/SharedPtr.h> #undef SHARED_UNDEF_PARAMS // in MyStruct.c void takes_ownership(CnxSharedPtr(MyStruct) to_take); void example(void) { // create a sharedly owned `MyStruct` shared_scoped(MyStruct) my_ptr = cnx_make_shared(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, CnxSharedPtr(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 CnxSharedPtr(i32)
, i32_ptr
, the equivalent function call for cnx_shared_ptr_release
would be cnx_shared_ptr_i32_release(&i32_ptr)
.
Defines
- #define CnxSharedPtr(T)
- macro alias for a
CnxSharedPtr(T)
maintaining ownership of an allocatedT
- #define cnx_shared_ptr_new(T)
- Creates a new sharedly, owned zero-initialized
T
- #define cnx_shared_ptr_new_with_allocator(T, allocator)
- Creates a new sharedly owned, zero-initialized
T
, allocated with the given allocator. - #define cnx_shared_ptr_new_with_capacity(T, capacity)
- Creates a new sharedly owned, zero-initialized, dynamically allocated array.
- #define cnx_shared_ptr_new_with_capacity_and_allocator(T, capacity, allocator)
- Creates a new sharedly owned, zero-initialized, dynamically allocated array, allocated with the given allocator.
- #define cnx_shared_ptr_from(T, ptr)
- Creates a new
CnxSharedPtr(T)
managing the given pointer. - #define cnx_shared_ptr_from_with_allocator(T, ptr, allocator)
- Creates a new
CnxSharedPtr(T)
managing the given pointer, associated with the given allocator. - #define cnx_shared_ptr_free(T, self)
- Frees the given
CnxSharedPtr(T)
, calling the associated deleter on the managed object. - #define cnx_shared_ptr_release(self)
- Releases the pointer managed by
self
from ownership. - #define cnx_shared_ptr_reset(self, new_ptr)
- Frees the object currently managed by
self
, and replaces it with the one pointed to bynew_ptr
- #define cnx_shared_ptr_clone(self)
- Clones the given
CnxSharedPtr(T)
- #define cnx_shared_ptr_swap(self, other_ptr)
- Swaps the managed objects of
self
and theCnxSharedPtr(T)
pointed to byother_ptr
- #define cnx_shared_ptr_get_deleter(self)
- Returns the
CnxDeleter(T)
function pointer associated with theCnxSharedPtr(T)
instantiationself
is an instance of. - #define cnx_shared_ptr_as_bool(self)
- Returns whether the given
CnxSharedPtr(T)
currently manages an object. - #define cnx_shared_ptr_get(self)
- Returns a pointer to the object managed by
self
- #define cnx_shared_ptr_get_const(self)
- Returns a pointer-to-const to the object managed by
self
- #define cnx_shared_ptr_get_mut(self)
- Returns a pointer-to-non-const to the object managed by
self
- #define cnx_shared_ptr_at(self, index)
- Returns a reference to the element at
index
in the array managed byself
- #define cnx_shared_ptr_at_const(self, index)
- Returns a reference-to-const to the element at
index
in the array managed byself
- #define cnx_shared_ptr_at_mut(self, index)
- Returns a reference-to-non-const to the element at
index
in the array managed byself
- #define cnx_make_shared(T, ...)
- Creates a
CnxSharedPtr(T)
managing aT
initialized with the given list of (potentially designated) initializers. - #define cnx_make_shared_with_allocator(T, allocator, ...)
- Creates a
CnxSharedPtr(T)
managing aT
initialized with the given list of (potentially designated) initializers, allocated with the given allocator. - #define cnx_make_shared_array(T, capacity)
- Creates a
CnxSharedPtr(T)
managing the array typeT
with the given initial capacity. - #define cnx_make_shared_array_with_allocator(T, capacity, allocator)
- Creates a
CnxSharedPtr(T)
managing the array typeT
with the given initial capacity, allocated with the given allocator. - #define SharedPtr(T)
- Declare a
CnxSharedPtr(T)
with this declaration attribute/type to ensure it (and its managed object) is automatically freed when theCnxSharedPtr(T)
goes out of scope.
Define documentation
#define CnxSharedPtr(T)
macro alias for a CnxSharedPtr(T)
maintaining ownership of an allocated T
CnxSharedPtr(T)
is a template for a "smart" pointer type that maintains (when used correctly) shared ownership of and manages an object through a contained pointer. The contained object is disposed of ("freed") when the CnxSharedPtr(T)
is explicitly freed, when it goes out of scope when declared with the SharedPtr(T)
tagged declaration, or when reset
(Cnx_shared_ptr_reset
) is called on the CnxSharedPtr(T)
.
CnxSharedPtr(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, SHARED_DELETER
when instantiating the CnxSharedPtr(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 CnxSharedPtr(T)
instance.
CnxSharedPtr(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 SHARED_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 `CnxSharedPtr(MyStruct)` #define SHARED_T MyStruct #define SHARED_DECL TRUE #define SHARED_IMPL TRUE #define SHARED_UNDEF_PARAMS TRUE #include <Cnx/SharedPtr.h> #undef SHARED_UNDEF_PARAMS void takes_ownership(CnxSharedPtr(MyStruct) to_take); void example(void) { // create a sharedly owned `MyStruct` SharedPtr(MyStruct) my_ptr = cnx_make_shared(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, CnxSharedPtr(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 CnxSharedPtr(i32)
, i32_ptr
, the equivalent function call for cnx_shared_ptr_release
would be cnx_shared_ptr_i32_release(&i32_ptr)
.
#define cnx_shared_ptr_new(T)
Creates a new sharedly, owned zero-initialized T
Parameters | |
---|---|
T | - The type to manage in the created CnxSharedPtr(T) |
Returns | a CnxSharedPtr(T) managing a zero-initialized T |
#define cnx_shared_ptr_new_with_allocator(T, allocator)
Creates a new sharedly owned, zero-initialized T
, allocated with the given allocator.
Parameters | |
---|---|
T | - The type to manage in the created CnxSharedPtr(T) |
allocator | - The allocator to allocator and eventually free the managed T with |
Returns | a CnxSharedPtr(T) managing a zero-initialized T |
#define cnx_shared_ptr_new_with_capacity(T, capacity)
Creates a new sharedly owned, zero-initialized, dynamically allocated array.
Parameters | |
---|---|
T | - The array type to manage in the created CnxSharedPtr(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 CnxSharedPtr(T) managing a zero-initialized, dynamically allocated array. |
#define cnx_shared_ptr_new_with_capacity_and_allocator(T, capacity, allocator)
Creates a new sharedly owned, zero-initialized, dynamically allocated array, allocated with the given allocator.
Parameters | |
---|---|
T | - The array type to manage in the created CnxSharedPtr(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 CnxSharedPtr(T) managing a zero-initialized, dynamically allocated array. |
#define cnx_shared_ptr_from(T, ptr)
Creates a new CnxSharedPtr(T)
managing the given pointer.
Parameters | |
---|---|
T | - The type to manage in the created CnxSharedPtr(T) |
ptr | - The ptr to manage with the CnxSharedPtr(T) . |
Returns | a CnxSharedPtr(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 CnxSharedPtr(T)
to maintain ownership. If you have control over when allocation occurs, prefer cnx_shared_ptr_new
, cnx_make_shared
, or derivatives.
#define cnx_shared_ptr_from_with_allocator(T, ptr, allocator)
Creates a new CnxSharedPtr(T)
managing the given pointer, associated with the given allocator.
Parameters | |
---|---|
T | - The type to manage in the created CnxSharedPtr(T) |
ptr | - The ptr to manage with the CnxSharedPtr(T) . |
allocator | - The allocator to allocator and eventually free the managed T with |
Returns | a CnxSharedPtr(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 CnxSharedPtr(T)
to maintain ownership. If you have control over when allocation occurs, prefer cnx_shared_ptr_new_with_allocator
or cnx_make_shared_with_allocator
#define cnx_shared_ptr_free(T, self)
Frees the given CnxSharedPtr(T)
, calling the associated deleter on the managed object.
Parameters | |
---|---|
T | - The type managed in the given CnxSharedPtr(T) |
self | - The CnxSharedPtr(T) to free |
This should generally only need to be called explicitly in particularly rare circumstances. In typical usage, CnxSharedPtr(T)
s should be declared with the SharedPtr(T)
tag, so cleanup is performed automatically when the associated CnxSharedPtr(T)
goes out of scope
#define cnx_shared_ptr_release(self)
Releases the pointer managed by self
from ownership.
Parameters | |
---|---|
self | - The CnxSharedPtr(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_shared_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 CnxSharedPtr(T) to have its managed object replaced |
new_ptr | - The pointer to the object to transfer ownership of to self |
#define cnx_shared_ptr_clone(self)
Clones the given CnxSharedPtr(T)
Parameters | |
---|---|
self | - The CnxSharedPtr(T) to clone |
Returns | a clone of self |
#define cnx_shared_ptr_swap(self, other_ptr)
Swaps the managed objects of self
and the CnxSharedPtr(T)
pointed to by other_ptr
Parameters | |
---|---|
self | - The CnxSharedPtr(T) to have its managed object swapped |
other_ptr | - Pointer to the CnxSharedPtr(T) to swap managed objects with |
self
and the CnxSharedPtr(T)
pointed to by other_ptr
must manage the same type
#define cnx_shared_ptr_get_deleter(self)
Returns the CnxDeleter(T)
function pointer associated with the CnxSharedPtr(T)
instantiation self
is an instance of.
Parameters | |
---|---|
self | - The CnxSharedPtr(T) to get the associated deleter of |
Returns | the CnxDeleter(T) function pointer associated with self |
#define cnx_shared_ptr_as_bool(self)
Returns whether the given CnxSharedPtr(T)
currently manages an object.
Parameters | |
---|---|
self | - The CnxSharedPtr(T) to check if currently manages an object |
Returns | true if self currently manages an object, otherwise false |
#define cnx_shared_ptr_get(self)
Returns a pointer to the object managed by self
Parameters | |
---|---|
self | - The CnxSharedPtr(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_shared_ptr_get_const
orcnx_shared_ptr_get_mut
, respectively.
#define cnx_shared_ptr_get_const(self)
Returns a pointer-to-const to the object managed by self
Parameters | |
---|---|
self | - The CnxSharedPtr(T) to get the managed object of |
Returns | pointer-to-const to the managed object |
#define cnx_shared_ptr_get_mut(self)
Returns a pointer-to-non-const to the object managed by self
Parameters | |
---|---|
self | - The CnxSharedPtr(T) to get the managed object of |
Returns | pointer-to-non-const to the managed object |
#define cnx_shared_ptr_at(self, index)
Returns a reference to the element at index
in the array managed by self
Parameters | |
---|---|
self | - The CnxSharedPtr(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_shared_ptr_at_const
orcnx_shared_ptr_at_mut
, respectively.
#define cnx_shared_ptr_at_const(self, index)
Returns a reference-to-const to the element at index
in the array managed by self
Parameters | |
---|---|
self | - The CnxSharedPtr(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_shared_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 CnxSharedPtr(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_shared(T, ...)
Creates a CnxSharedPtr(T)
managing a T
initialized with the given list of (potentially designated) initializers.
Parameters | |
---|---|
T | - The type the CnxSharedPtr(T) will manage |
... | - The list of initializers to initialize the T with |
Returns | a CnxSharedPtr(T) managing a newly allocated T |
#define cnx_make_shared_with_allocator(T, allocator, ...)
Creates a CnxSharedPtr(T)
managing a T
initialized with the given list of (potentially designated) initializers, allocated with the given allocator.
Parameters | |
---|---|
T | - The type the CnxSharedPtr(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 CnxSharedPtr(T) managing a newly allocated T |
#define cnx_make_shared_array(T, capacity)
Creates a CnxSharedPtr(T)
managing the array type T
with the given initial capacity.
Parameters | |
---|---|
T | - The array type the CnxSharedPtr(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 CnxSharedPtr(T) managing a newly allocated dynamic array of capacity capacity |
#define cnx_make_shared_array_with_allocator(T, capacity, allocator)
Creates a CnxSharedPtr(T)
managing the array type T
with the given initial capacity, allocated with the given allocator.
Parameters | |
---|---|
T | - The array type the CnxSharedPtr(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 CnxSharedPtr(T) managing a newly allocated dynamic array of capacity capacity |
#define SharedPtr(T)
Declare a CnxSharedPtr(T)
with this declaration attribute/type to ensure it (and its managed object) is automatically freed when the CnxSharedPtr(T)
goes out of scope.
Parameters | |
---|---|
T | - The type managed by the CnxSharedPtr(T) |