This is the reference page for jv::BoundedPoly
.
To see the summary and examples, go there: index.html.
Concepts
jv::is_storable
template <typename T, typename Storage>
struct is_storable {
static constexpr bool value = ...;
};
template <typename T, typename Storage>
constexpr bool is_storable_v = is_storable<T, Storage>::value;
Type trait that checks if we can store T
in Storage
.
It provides the constant boolean. member value
as a result.
is_storable_v
is provided as an helper.
Parameter | Description |
---|---|
T : typename |
The type we want to store |
Storage : typename |
The type we want to store into |
value = true
if and only if the following properties are true:
-
Storage
meets theTrivialType
requirement -
T
is aligned onStorage
(=alignof(Storage)
is a multiple ofalignof(T)
) -
T
is smaller thanStorage
(=sizeof(T) ⇐ sizeof(Storage)
)
jv::is_movable
template <typename T, typename Mover, typename A>
struct is_movable {
static constexpr bool value = ...;
};
template <typename T, typename Mover, typename A>
constexpr bool is_movable_v = is_movable<T, Storage>::value;
Given a Mover
that abstracts the move operation on T
, checks if it can handle A
.
The abstraction of move operations is provided by invoking Mover
with (T&& src, void* dst)
.
It provides the constant boolean. member value
as a result.
is_movable_v
is provided as an helper.
Parameter | Description |
---|---|
T : typename |
The type we want to abstract |
Mover : typename |
The type that must provide a move operation for T |
A : typename |
The type we want to move with the abstraction provided by Mover |
Mover
must provide an abstraction of a move operation on T
, and A
must be abstractable using this abstraction. This means:
-
Mover
must be nothrow destructible. -
Mover
must be invocable with the signaturevoid(T&& src, void* dst) noexcept
.-
It must construct-move an
A
fromsrc
intodst
. For instance:new (dst) A((A&&)src)
. -
It must always succeed: you cannot throw an exception (noexcept) nor return an error code (void).
-
-
If
Mover
is not empty (=Mover
is a stateful mover):-
Must provide constructor
Mover(A const*) noexcept
.It will be called with nullptr
. Only the type of the argument is relevant. This lets theMover
know which type it will move. -
Must be nothrow copyable.
-
We want every implementation of the interface Interface
being movable using Mover
.
Moving is inherently dependent of the true class moved, so we need to create a polymorphic method to let derived implementation customize how they must be moved.
struct Interface {
virtual ~Interface() {}
virtual void move_to(void* dst) && noexcept = 0;
...
};
struct Mover {
void operator()(Interface&& src, void* dst) noexcept {
src.move_to(dst);
}
};
Then, each derived implementation must implement move_to
.
Note that the implementation is similar for every derived class.
struct Derived : Interface {
void move_to(void* dst) && noexcept {
new (dst) Derived(std::move(*this));
}
};
is_movable<Interface, Mover, Derived> is true .
|
Types
jv::UniversalMover
template <typename T>
class UniversalMover {
public:
template <typename A>
constexpr UniversalMover(A const*) noexcept;
constexpr void operator()(T&& src, void* dst) const noexcept;
};
Stateful Mover that can handle any type A
derived from T
.
Internally, holds a function pointer to an A
-specific function.
Satisfies jv::is_movable<T, UniversalMover<T>, A>
.
Parameter | Precondition | Description |
---|---|---|
T : typename |
None |
The type we want to abstract the move of its child classes. |
jv::VirtualMover
template <typename T; void (T::*Method)(void*)&& noexcept>
class VirtualMover {
public:
constexpr void operator()(T&& src, void* dst) const noexcept;
};
Stateless Mover that invoke a polymorphic method of T
that must be redefined by any derived class A
.
Satisfies jv::is_movable<T, VirtualMover<T>, A>
.
Parameter | Description |
---|---|
T : typename |
The type we want to abstract the move of its child classes. |
Method: pointer to a member function of |
The method we must call to move the value. |
jv::BoundedPoly
template <typename Storage, typename Base, typename Mover = UniversalMover<Base>>
class BoundedPoly {
public:
Base& get() noexcept;
Base const& get() const noexcept;
};
Type abstractor for polymorphic instances of Base
, stored on stack in Storage
, using Mover
to perform move operations.
Parameter | Precondition | Description |
---|---|---|
Storage : typename |
The type in which the instances are stored. |
|
Base : typename |
|
The polymorphic common base of the instances stored in BoundedPoly. |
Mover : typename |
|
The abstractor of move operations. |
BoundedPoly
provides an abstraction of derived classes of Base
.
Its size is sizeof(Storage) + sizeof(Mover)
(+ potentially some padding to keep them aligned).
Unline std::any
, std::variant
and std::unique_ptr
, it is guaranteed that BoundedPoly
has no empty state.
This allows us to not test for empty state before doing stuff in methods, but it requires that move operations on stored values do not throw.
Mover
is provided as a customization point.
Ideally, Base
should provide a virtual method move_to(void*) && noexcept
.
So the move call is dispatched using polymorphism.
In this case, the mover would be [](Base&& src, void* dst) noexcept { src.move_to(dst); }
. Such a Mover
is stateless: it only redirects to the correct virtual method.
But on existing hierarchies, it may be impossible to add such a method.
For these cases, stateful Mover
exist
For instance UniversalMover<Base>
stores as state a function pointer to void(Base&&,void*) noexcept
.
It changes depending on the actual type stored.
This adds an extra cost (a function pointer stored) so it is preferable to use a stateless Mover
that calls a virtual method.
BoundedPoly::can_handle
template <typename T>
struct can_handle {
static constexpr bool value = ...;
};
template <typename T>
static constexpr bool can_handle_v = can_handle<T>::value;
Check if T
can be handled by BoundedPoly<Storage, Base, Mover>
.
It is true
if and only if T
satisfies:
-
std::is_nothrow_move_constructible<T>
-
std::is_base_of<Base, T>
-
jv::is_movable<Base, Mover, T>
-
jv::is_storable<T, Storage>
BoundedPoly::BoundedPoly
template<typename Derived>
BoundedPoly(Derived&& derived); (1)
template <typename Derived, typename... Args>
BoundedPoly(std::in_place_type_t<Derived>, Args&&... args); (2)
BoundedPoly(BoundedPoly const&) = delete; (3)
BoundedPoly(BoundedPoly&& other) noexcept; (4)
1. Construct from the argument into the internal storage. |
|
---|---|
Requirements |
|
Throws |
If |
2. Construct directly in the storage |
|
Requirements |
|
Throws |
Either if |
3. Copy construction is deleted. |
|
4. Move construction. |
|
Note |
The |
BoundedPoly::operator=
template <typename Derived>
auto operator=(Derived derived) noexcept -> BoundedPoly&; (1)
auto operator=(BoundedPoly const&) -> BoundedPoly& = delete; (2)
auto operator=(BoundedPoly&& other) noexcept -> BoundedPoly&; (3)
1. Construct from the argument into the internal storage. |
|||
---|---|---|---|
Requirements |
|
||
Throws |
If
|
||
2. Copy assignment is deleted. |
|||
3. Move assignment. |
|||
Note |
The |
BoundedPoly::~BoundedPoly
~BoundedPoly() noexcept;
Destroys the stored value.
BoundedPoly::emplace
template <typename Derived, typename... Args>
void emplace(Args&&... args) noexcept;
Construct Derived into the internal storage using the provided arguments. |
|||
---|---|---|---|
Requirements |
|
||
Throws |
If
|
BoundedPoly::get
auto get() noexcept -> Base&;
auto get() const noexcept -> Base const&;
Returns a reference to the stored value.
BoundedPoly::operator*
auto operator*() noexcept -> Base&;
auto operator*() const noexcept -> Base const&;
Returns a reference to the stored value.
BoundedPoly::operator->
auto operator->() noexcept -> Base*;
auto operator->() const noexcept -> Base const*;
Dereferences a member of Base
.
BoundedPoly::swap
void swap(BoundedPoly& other) noexcept;
Exchanges stored value with other
.
jv::BoundedPolyVM
template <typename Storage, typename IBase,
void (IBase::*Method)(void* dst)&& noexcept>
using BoundedPolyVM = BoundedPoly<Storage, IBase, VirtualMover<IBase, Method>>;
Helper alias of BoundedPoly
which takes a virtual method as a Mover.
Parameter | Description |
---|---|
Storage : typename |
|
The type in which the instances are stored. |
Base : typename |
|
The polymorphic common base of the instances stored in BoundedPoly. |
Method: pointer to a member function of |
|