Class Subspace :: sus :: boxed :: Box
A heap allocated object.
A Box<T>
holds ownership of an object of type T
on the heap. When Box
is destroyed, or an rvalue-qualified method is called, the inner heap
object is freed.
Box
is similar to std::unique_ptr
with some differences,
and should be preferred when those differences will benefit you:
- Const correctness. A
const Box
treats the inner object asconst
and does not expose mutable access to it. - Never null. A
Box
always holds a value until it is moved-from. It is constructed from a value, not a pointer. Alternatively it can be constructed from the constructor arguments bywith_args
, likestd::make_unique
but built into the type. A moved-fromBox
may not be used except to be assigned to or destroyed. Using a moved-fromBox
willpanic
and terminate the program rather than operate on a null. This prevents Undefined Behaviour and memory bugs caused by dereferencing null or using null in unintended ways. - Supports up-casting (TODO: and
down-casting
) without leaving requiring a native pointer to be pulled out of theBox
. - No native arrays,
Box
can holdArray
orstd::array
but avoids API complexity for supporting pointers and native arrays (which decay to pointers) by rejecting native arrays. - Integration with concept type-erasure for
holding and constructing from type-erased objects which satisfy a given
concept in a type-safe way and without requiring inheritence. This
construction is done through
From
and thus canBox<DynC>
can be constructed in a type-deduced way from any object that satisfies the conceptC
viasus::into()
. This only requires thatDynC
is compatible withDynConcept
which is the case for all type-erasable concepts in the Subspace library. - Additional integration with Subspace library concepts like
Error
andFn
such thatBox
will satisfy those concepts itself when holding a type-erased object that satisfies those concepts.
Box implements some concepts for its inner type
The Subspace library provides a number of concepts which support
type-erasure through DynConcept
, and when
Box
is holding these as its value, it may itself implement the concept,
forwarding use of the concept through to the inner type.
The canonical example of this is
Result
<T, Box<
DynError
>>
, which allows construction via
sus::err(sus::into(e))
for any e
that satisfies
Error
. The error field, now being a Box
is still
usable as an Error
allowing generic code that
expects the Result
to hold an
Error
to function even though the actual error has
been type-erased and no longer needs the functions to be templated on it.
The following concepts, when type-erased in Box
will also be satisfied
by the Box
itself, avoiding the need to unwrap the inner type and allowing
the Box
to be used in templated code requiring that concept:
Static Methods
Constructs a Box which allocates space on the heap and moves T
into it.
Constructs a Box which allocates space on the heap and moves T
into it.
Converts U
into a Box<T>
.
The conversion allocates on the heap and moves u
into it.
Satisfies the From<U>
concept for
Box<T>
where U
is convertible to T
and is not a
subclass of T
.
Converts U
into a Box<T>
.
The conversion allocates on the heap and moves u
into it.
Satisfies the From<U>
concept for
Box<T>
where U is T
or a subclass of T
.
For a type-erased DynC
of a concept C
, Box<DynC>
can be
constructed from a type that satisfies C
.
This satisfies the From<DynC, C>
concept for
constructing Box<DynC>
from any type that satisfies the concept C
. It
allows returning a non-templated type satisfying a concept.
See DynConcept
for more on type erasure of
concept-satisfying types.
Satisfies the From<std::string>
concept for
Box
<
DynError
>
. This
conversion moves and type-erases the std::string
into a heap-alloocated
DynError
.
After calling this function, the raw pointer is owned by the resulting
Box. Specifically, the Box destructor will call the destructor of T and
free the allocated memory. For this to be safe, the memory must have been
allocated on the heap in the same way as the Box
type, using
operator new
and must not be
an array.
Constructs a Box by calling the constructor of T
with args
.
This allows construction of T
directly on the heap, which is required
for types which do not satisfy Move
. This is a common
case for virtual clases which require themselves to be heap allocated.
For type-erasure of concept objects using DynConcept
, such as DynError
,
the from
constructor method
can be used to type erase the concept object and move it to the heap.
Constructs Box<T>
with the default value for the type T
.
Box
can not satisfy Default
because it
prevents C++ from using Box
to build recursive types where T
holds
an Option<Box<T>>
as a member.
Methods
Converts Box
into a mutable reference of the inner type.
Converts Box
into a const reference of the inner type.
Returns a new box with a clone() of this box’s contents.
Copies source
's contents into the contained T
without creating a new
allocation.
An optimization to reuse the existing storage for
clone_into
.
sus::iter::IteratorAny<T>
sus::iter::ExactSizeIterator<T, typename T::Item>
Implements ExactSizeIterator
if
T
is an ExactSizeIterator
,
forwarding through to the inner T
object.
Consumes the Box
, returning the wrapped value.
This allows the caller to make use of the wrapped object as an rvalue
without leaving a moved-from T
inside the Box
.
Consumes the Box
, returning a wrapped raw pointer.
The pointer will be properly aligned and non-null.
After calling this function, the caller is responsible for the memory
previously managed by the Box
. In particular, the caller should properly
destroy T
and
delete the
memory, taking into account the alignment if any that would be passed by
Box
to operator new
. The easiest
way to do this is to convert the raw pointer back back into a Box
with
the Box::from_raw
function, allowing the
Box
destructor to perform the cleanup.
Note: this is a not a static function, unlike the matching Rust function
Box::into_raw
,
since in C++ the Box
can not expose the methods of the inner type
directly.
Examples
Converting the raw pointer back into a Box
with
Box::from_raw
for automatic cleanup:
auto x = Box<std::string>("Hello");
auto* ptr = sus::move(x).into_raw();
x = Box<std::string>::from_raw(unsafe_fn, ptr);
Manual cleanup by explicitly running the destructor and deallocating the memory:
auto x = Box<std::string>("Hello");
auto* p = sus::move(x).into_raw();
delete p;
Consumes and leaks the Box
, returning a mutable reference, T&
. Note
that the type T must outlive the returned reference.
This function is mainly useful for data that lives for the remainder of
the program's life. Dropping the returned reference will cause a memory
leak. If this is not acceptable, the reference should first be wrapped
with the Box::from_raw
method producing a
Box
. This Box
can then be dropped which will properly destroy T
and
release the allocated memory.
This method is not functionally different than into_raw
but expresses a different intent, and returns
a reference type indicating it can not ever return null.
Note: unlike with the Rust Box::leak
this is not a static method, since Box
can not expose the inner type's
methods directly in C++.
Implements DoubleEndedIterator
if
T
is a DoubleEndedIterator
,
forwarding through to the inner T
object.
Operators
T::IsDynFn
sus::fn::Fn<T, sus::fn::Return<T, Args...> (Args...)>
T::IsDynFn
sus::fn::FnMut<T, sus::fn::ReturnMut<T, Args...> (Args...)>
T::IsDynFn
sus::fn::FnOnce<T, sus::fn::ReturnOnce<T, Args...> (Args...)>
A Box
holding a type-erased function type will satisfy the fn concepts
and can be used as a function type. It will forward the call through
to the inner type.
The usual compatibility rules apply, allowing DynFn
to be treated like DynFnMut
and both of them to be treated like DynFnOnce
.
A Box<
DynFnOnce
>
must be moved from
when called, and will destroy the
underlying function object after the call completes.
Examples
A Box<DynFn>
being used as a function object:
const auto b = Box<sus::fn::DynFn<usize(std::string_view)>>::from(
&std::string_view::size);
sus_check(b("hello world") == 11u);
auto mut_b = Box<sus::fn::DynFn<usize(std::string_view)>>::from(
&std::string_view::size);
sus_check(mut_b("hello world") == 11u);
sus_check(sus::move(mut_b)("hello world") == 11u);
// The object inside `mut_b` is now destroyed.
A Box<DynFnMut>
being used as a function object. It can not be called
on a const Box
as it requies the ability to mutate, and const Box
treats its inner object as const:
auto mut_b = Box<sus::fn::DynFnMut<usize(std::string_view)>>::from(
&std::string_view::size);
sus_check(mut_b("hello world") == 11u);
sus_check(sus::move(mut_b)("hello world") == 11u);
// The object inside `mut_b` is now destroyed.
A Box<DynFnOnce>
being used as a function object. It must be an rvalue
(either a return from a call or moved with move
)
to call through it:
auto b = Box<sus::fn::DynFnOnce<usize(std::string_view)>>::from(
&std::string_view::size);
sus_check(sus::move(b)("hello world") == 11u);
auto x = [] {
return Box<sus::fn::DynFnOnce<usize(std::string_view)>>::from(
&std::string_view::size);
};
sus_check(x()("hello world") == 11u);