Date: Sat, 14 Dec 2024 22:31:20 +0200
This is my first time posting here, so I appreciate any guidance on the
style of my post.
C++ has been progressively incorporating ideas from Rust to improve memory
safety. Easy examples are std::expected which parallel Rust's Result,
std::optional which parallels Rust's Option.
I propose an adaptation of Rust's Box type: a pointer to a heap object that
always contains a valid value and allows the templated object to be an
incomplete type.
Conceptually, it shares similarities with std::unique_ptr: both are
intended to be memory-safe pointers to the heap with the template
parameter allowed to be an incomplete type. Both don't ever move the
contained value. This container is also made with an explicit purpose to
house C structures which might break pointers when moved or copied.
Where they differ, would be move mechanics and initialization.
First of all, I have to acknowledge that this type will most definitely be
a lot more restrictive than a std::unique_ptr.
This container enforces two key invariants: (1) the internal pointer is
never nullptr and always points to an initialized value, and (2) the
contained value is immovable.
To upkeep the invariant that the contained heap value is valid, if there is
ever an invalid state,
an exception would be thrown immediately.
Here's some of box's properties I've figured out so far (T being the
template type):
- As a more constrained unique_ptr, it would be implicitly movable to a
unique_ptr.
- It will have a default constructor only if T is default initializable.
- Analogous to make_unique, Box would have a make_box that has the same
purpose.
- A factory function that returns std::expected that attempts to constrain
a unique_ptr to a Box.
- The destructor calls delete on the internal pointer or uses the deleter
function if created similarly to unique_ptr
- Copy construction is disabled.
- Move constructor swaps the internal pointers to not have to perform any
extra initializations. Although this could be omitted from the standard and
let implementations decide how to perform the move while keeping or
discarding the invariant of the moved object. Either way should work well
enough. I haven't yet thought about how up-casting and down-casting would
function, but it shouldn't be too far from the way unique_ptr does that.
- The swap function exchanges the internal pointers, maintaining all
invariants.
- To facilitate interoperation with C libraries, support for std::inout_ptr can
be provided. std::inout_ptr would delete the object and set the pointer to
nullptr in case
This does risk breaking an invariant, but it would throw soon enough, and
anyone who uses this would immediately assume this is the place where an
invalid state is raised.
- A method for checking a box's status would most likely be useful. It
would simply check if the pointer is a nullptr and assume everything is
fine otherwise. Note that std::out_ptr would not be implementable for the
box as it should always be initialized and thus no valid use case for
std::out_ptr should exist.
- To replace/modify the value of an existing Box, you can either modify it
via dereference operators * and -> or use an emplace() method which would
accept variadic arguments with perfect forwarding to a constructor and
allocate a new memory block in the heap. The previous block would be
returned by the method as a new Box. If the returned value is unused, it
would be deallocated as if a complete overwrite had taken place.
I really hope this isn't too long of a post and I'd love to get some
feedback about this.
I've attached a somewhat simple implementation of how such a class would
look. It does not consider custom deleters and casting.
style of my post.
C++ has been progressively incorporating ideas from Rust to improve memory
safety. Easy examples are std::expected which parallel Rust's Result,
std::optional which parallels Rust's Option.
I propose an adaptation of Rust's Box type: a pointer to a heap object that
always contains a valid value and allows the templated object to be an
incomplete type.
Conceptually, it shares similarities with std::unique_ptr: both are
intended to be memory-safe pointers to the heap with the template
parameter allowed to be an incomplete type. Both don't ever move the
contained value. This container is also made with an explicit purpose to
house C structures which might break pointers when moved or copied.
Where they differ, would be move mechanics and initialization.
First of all, I have to acknowledge that this type will most definitely be
a lot more restrictive than a std::unique_ptr.
This container enforces two key invariants: (1) the internal pointer is
never nullptr and always points to an initialized value, and (2) the
contained value is immovable.
To upkeep the invariant that the contained heap value is valid, if there is
ever an invalid state,
an exception would be thrown immediately.
Here's some of box's properties I've figured out so far (T being the
template type):
- As a more constrained unique_ptr, it would be implicitly movable to a
unique_ptr.
- It will have a default constructor only if T is default initializable.
- Analogous to make_unique, Box would have a make_box that has the same
purpose.
- A factory function that returns std::expected that attempts to constrain
a unique_ptr to a Box.
- The destructor calls delete on the internal pointer or uses the deleter
function if created similarly to unique_ptr
- Copy construction is disabled.
- Move constructor swaps the internal pointers to not have to perform any
extra initializations. Although this could be omitted from the standard and
let implementations decide how to perform the move while keeping or
discarding the invariant of the moved object. Either way should work well
enough. I haven't yet thought about how up-casting and down-casting would
function, but it shouldn't be too far from the way unique_ptr does that.
- The swap function exchanges the internal pointers, maintaining all
invariants.
- To facilitate interoperation with C libraries, support for std::inout_ptr can
be provided. std::inout_ptr would delete the object and set the pointer to
nullptr in case
This does risk breaking an invariant, but it would throw soon enough, and
anyone who uses this would immediately assume this is the place where an
invalid state is raised.
- A method for checking a box's status would most likely be useful. It
would simply check if the pointer is a nullptr and assume everything is
fine otherwise. Note that std::out_ptr would not be implementable for the
box as it should always be initialized and thus no valid use case for
std::out_ptr should exist.
- To replace/modify the value of an existing Box, you can either modify it
via dereference operators * and -> or use an emplace() method which would
accept variadic arguments with perfect forwarding to a constructor and
allocate a new memory block in the heap. The previous block would be
returned by the method as a new Box. If the returned value is unused, it
would be deallocated as if a complete overwrite had taken place.
I really hope this isn't too long of a post and I'd love to get some
feedback about this.
I've attached a somewhat simple implementation of how such a class would
look. It does not consider custom deleters and casting.
Received on 2024-12-14 20:31:35