forked from pz4kybsvg/Conception
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
442 lines
19 KiB
442 lines
19 KiB
#pragma once
|
|
|
|
/* Portions copyright (c) 2015 Stanford University and the Authors.
|
|
Authors: Michael Sherman
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
not use this file except in compliance with the License. You may obtain a
|
|
copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
|
|
|
|
(Adapted from Simbody's ClonePtr class.)
|
|
*/
|
|
|
|
#include <cstddef>
|
|
#include <memory>
|
|
#include <ostream>
|
|
#include <utility>
|
|
|
|
#include "drake/common/drake_assert.h"
|
|
#include "drake/common/drake_deprecated.h"
|
|
#include "drake/common/fmt.h"
|
|
|
|
namespace drake {
|
|
|
|
// TODO(SeanCurtis-TRI): Consider extending this to add the Deleter as well.
|
|
/** A smart pointer with deep copy semantics.
|
|
|
|
This is _similar_ to `std::unique_ptr` in that it does not permit shared
|
|
ownership of the contained object. However, unlike `std::unique_ptr`,
|
|
%copyable_unique_ptr supports copy and assignment operations, by insisting that
|
|
the contained object be "copyable". To be copyable, the class must have either
|
|
an accessible copy constructor, or it must have an accessible clone method
|
|
with signature @code
|
|
std::unique_ptr<Foo> Clone() const;
|
|
@endcode
|
|
where Foo is the type of the managed object. By "accessible" we mean either
|
|
that the copy constructor or clone method is public, or
|
|
`friend copyable_unique_ptr<Foo>;` appears in Foo's class declaration.
|
|
|
|
<!-- Developer note: if you change or extend the definition of an acceptable
|
|
clone method here, be sure to consider whether drake::is_cloneable should
|
|
be changed as well. -->
|
|
|
|
Generally, the API is modeled as closely as possible on the C++ standard
|
|
`std::unique_ptr` API and %copyable_unique_ptr<T> is interoperable with
|
|
`unique_ptr<T>` wherever that makes sense. However, there are some differences:
|
|
|
|
1. It always uses a default deleter.
|
|
2. There is no array version.
|
|
3. To allow for future copy-on-write optimizations, there is a distinction
|
|
between writable and const access, the get() method is modified to return
|
|
only a const pointer, with get_mutable() added to return a writable pointer.
|
|
Furthermore, dereferencing (operator*()) a mutable pointer will give a
|
|
mutable reference (in so far as T is not declared const), and dereferencing
|
|
a const pointer will give a const reference.
|
|
|
|
This class is entirely inline and has no computational or space overhead except
|
|
when copying is required; it contains just a single pointer and does no
|
|
reference counting.
|
|
|
|
__Usage__
|
|
|
|
In the simplest use case, the instantiation type will match the type of object
|
|
it references, e.g.:
|
|
@code
|
|
copyable_unique_ptr<Foo> ptr = make_unique<Foo>(...);
|
|
@endcode
|
|
In this case, as long `Foo` is deemed compatible, the behavior will be as
|
|
expected, i.e., when `ptr` copies, it will contain a reference to a new
|
|
instance of `Foo`.
|
|
|
|
%copyable_unique_ptr can also be used with polymorphic classes -- a
|
|
%copyable_unique_ptr, instantiated on a _base_ class, references an
|
|
instance of a _derived_ class. When copying the object, we would want the copy
|
|
to likewise contain an instance of the derived class. For example:
|
|
|
|
@code
|
|
copyable_unique_ptr<Base> cu_ptr = make_unique<Derived>();
|
|
copyable_unique_ptr<Base> other_cu_ptr = cu_ptr; // Triggers a copy.
|
|
is_dynamic_castable<Derived>(other_cu_ptr.get()); // Should be true.
|
|
@endcode
|
|
|
|
This works for well-designed polymorphic classes.
|
|
|
|
@warning Ill-formed polymorphic classes can lead to fatal type slicing of the
|
|
referenced object, such that the new copy contains an instance of `Base`
|
|
instead of `Derived`. Some mistakes that would lead to this degenerate
|
|
behavior:
|
|
|
|
- The `Base` class's Clone() implementation does not invoke the `Derived`
|
|
class's implementation of a suitable virtual method.
|
|
|
|
<!--
|
|
For future developers:
|
|
- the copyability of a base class does *not* imply anything about the
|
|
copyability of a derived class. In other words, `copyable_unique_ptr<Base>`
|
|
can be compilable while `copyable_unique_ptr<Derived>` is not.
|
|
- Given the pointer `copyable_unique_ptr<Base> ptr(new Derived())`, even if
|
|
this copies "correctly" (such that the copy contains an instance of
|
|
`Derived`), this does _not_ imply that `copyable_unique_ptr<Derived>` is
|
|
compilable.
|
|
-->
|
|
|
|
@tparam T The type of the contained object, which *must* be copyable as
|
|
defined above. May be an abstract or concrete type.
|
|
*/
|
|
template <typename T>
|
|
class copyable_unique_ptr : public std::unique_ptr<T> {
|
|
public:
|
|
/** @name Constructors */
|
|
/**@{*/
|
|
|
|
/** Default constructor stores a `nullptr`. No heap allocation is performed.
|
|
The empty() method will return true when called on a default-constructed
|
|
%copyable_unique_ptr. */
|
|
copyable_unique_ptr() noexcept : std::unique_ptr<T>() {}
|
|
|
|
/** Given a raw pointer to a writable heap-allocated object, take over
|
|
ownership of that object. No copying occurs. */
|
|
explicit copyable_unique_ptr(T* raw) noexcept : std::unique_ptr<T>(raw) {}
|
|
|
|
/** Constructs a unique instance of T as a copy of the provided model value.
|
|
*/
|
|
explicit copyable_unique_ptr(const T& value)
|
|
: std::unique_ptr<T>(CopyOrNull(&value)) {}
|
|
|
|
/** Copy constructor is deep; the new %copyable_unique_ptr object contains a
|
|
new copy of the object in the source, created via the source object's
|
|
copy constructor or `Clone()` method. If the source container is empty this
|
|
one will be empty also. */
|
|
copyable_unique_ptr(const copyable_unique_ptr& cu_ptr)
|
|
: std::unique_ptr<T>(CopyOrNull(cu_ptr.get())) {}
|
|
|
|
/** Copy constructor from a standard `unique_ptr` of _compatible_ type. The
|
|
copy is deep; the new %copyable_unique_ptr object contains a new copy of the
|
|
object in the source, created via the source object's copy constructor or
|
|
`Clone()` method. If the source container is empty this one will be empty
|
|
also. */
|
|
template <typename U>
|
|
explicit copyable_unique_ptr(const std::unique_ptr<U>& u_ptr)
|
|
: std::unique_ptr<T>(CopyOrNull(u_ptr.get())) {}
|
|
|
|
/** Move constructor is very fast and leaves the source empty. Ownership
|
|
is transferred from the source to the new %copyable_unique_ptr. If the source
|
|
was empty this one will be empty also. No heap activity occurs. */
|
|
copyable_unique_ptr(copyable_unique_ptr&& cu_ptr) noexcept
|
|
: std::unique_ptr<T>(cu_ptr.release()) {}
|
|
|
|
/** Move constructor from a standard `unique_ptr`. The move is very fast and
|
|
leaves the source empty. Ownership is transferred from the source to the new
|
|
%copyable_unique_ptr. If the source was empty this one will be empty also. No
|
|
heap activity occurs. */
|
|
explicit copyable_unique_ptr(std::unique_ptr<T>&& u_ptr) noexcept
|
|
: std::unique_ptr<T>(u_ptr.release()) {}
|
|
|
|
/** Move construction from a compatible standard `unique_ptr`. Type `U*` must
|
|
be implicitly convertible to type `T*`. Ownership is transferred from the
|
|
source to the new %copyable_unique_ptr. If the source was empty this one will
|
|
be empty also. No heap activity occurs. */
|
|
template <typename U>
|
|
explicit copyable_unique_ptr(std::unique_ptr<U>&& u_ptr) noexcept
|
|
: std::unique_ptr<T>(u_ptr.release()) {}
|
|
|
|
/**@}*/
|
|
|
|
/** @name Assignment */
|
|
/**@{*/
|
|
|
|
/** This form of assignment replaces the currently-held object by
|
|
the given source object and takes over ownership of the source object. The
|
|
currently-held object (if any) is deleted. */
|
|
copyable_unique_ptr& operator=(T* raw) noexcept {
|
|
std::unique_ptr<T>::reset(raw);
|
|
return *this;
|
|
}
|
|
|
|
/** This form of assignment replaces the currently-held object by a
|
|
heap-allocated copy of the source object, created using its copy
|
|
constructor or `Clone()` method. The currently-held object (if any) is
|
|
deleted. */
|
|
copyable_unique_ptr& operator=(const T& ref) {
|
|
std::unique_ptr<T>::reset(CopyOrNull(&ref));
|
|
return *this;
|
|
}
|
|
|
|
/** Copy assignment from %copyable_unique_ptr replaces the currently-held
|
|
object by a copy of the object held in the source container, created using
|
|
the source object's copy constructor or `Clone()` method. The currently-held
|
|
object (if any) is deleted. If the source container is empty this one will be
|
|
empty also after the assignment. Nothing happens if the source and
|
|
destination are the same container. */
|
|
copyable_unique_ptr& operator=(const copyable_unique_ptr& cu_ptr) {
|
|
return operator=(static_cast<const std::unique_ptr<T>&>(cu_ptr));
|
|
}
|
|
|
|
/** Copy assignment from a compatible %copyable_unique_ptr replaces the
|
|
currently-held object by a copy of the object held in the source container,
|
|
created using the source object's copy constructor or `Clone()` method. The
|
|
currently-held object (if any) is deleted. If the source container is empty
|
|
this one will be empty also after the assignment. Nothing happens if the
|
|
source and destination are the same container. */
|
|
template <typename U>
|
|
copyable_unique_ptr& operator=(const copyable_unique_ptr<U>& cu_ptr) {
|
|
return operator=(static_cast<const std::unique_ptr<U>&>(cu_ptr));
|
|
}
|
|
|
|
/** Copy assignment from a standard `unique_ptr` replaces the
|
|
currently-held object by a copy of the object held in the source container,
|
|
created using the source object's copy constructor or `Clone()` method. The
|
|
currently-held object (if any) is deleted. If the source container is empty
|
|
this one will be empty also after the assignment. Nothing happens if the
|
|
source and destination are the same container. */
|
|
copyable_unique_ptr& operator=(const std::unique_ptr<T>& src) {
|
|
if (&src != this) {
|
|
// can't be same ptr unless null
|
|
DRAKE_DEMAND((get() != src.get()) || !get());
|
|
std::unique_ptr<T>::reset(CopyOrNull(src.get()));
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/** Copy assignment from a compatible standard `unique_ptr` replaces the
|
|
currently-held object by a copy of the object held in the source container,
|
|
created using the source object's copy constructor or `Clone()` method. The
|
|
currently-held object (if any) is deleted. If the source container is empty
|
|
this one will be empty also after the assignment. Nothing happens if the
|
|
source and destination are the same container. */
|
|
template <typename U>
|
|
copyable_unique_ptr& operator=(const std::unique_ptr<U>& u_ptr) {
|
|
// can't be same ptr unless null
|
|
DRAKE_DEMAND((get() != u_ptr.get()) || !get());
|
|
std::unique_ptr<T>::reset(CopyOrNull(u_ptr.get()));
|
|
return *this;
|
|
}
|
|
|
|
/** Move assignment replaces the currently-held object by the source object,
|
|
leaving the source empty. The currently-held object (if any) is deleted.
|
|
The instance is _not_ copied. Nothing happens if the source and destination
|
|
are the same containers. */
|
|
copyable_unique_ptr& operator=(copyable_unique_ptr&& cu_ptr) noexcept {
|
|
std::unique_ptr<T>::reset(cu_ptr.release());
|
|
return *this;
|
|
}
|
|
|
|
/** Move assignment replaces the currently-held object by the compatible
|
|
source object, leaving the source empty. The currently-held object (if any)
|
|
is deleted. The instance is _not_ copied. Nothing happens if the source and
|
|
destination are the same containers. */
|
|
template <typename U>
|
|
copyable_unique_ptr& operator=(copyable_unique_ptr<U>&& cu_ptr) noexcept {
|
|
std::unique_ptr<T>::reset(cu_ptr.release());
|
|
return *this;
|
|
}
|
|
|
|
/** Move assignment replaces the currently-held object by the source object,
|
|
leaving the source empty. The currently-held object (if any) is deleted.
|
|
The instance is _not_ copied. Nothing happens if the source and destination
|
|
are the same containers. */
|
|
copyable_unique_ptr& operator=(std::unique_ptr<T>&& u_ptr) noexcept {
|
|
std::unique_ptr<T>::reset(u_ptr.release());
|
|
return *this;
|
|
}
|
|
|
|
/** Move assignment replaces the currently-held object by the compatible
|
|
source object, leaving the source empty. The currently-held object (if
|
|
any) is deleted. The instance is _not_ copied. Nothing happens if the source
|
|
and destination are the same containers. */
|
|
template <typename U>
|
|
copyable_unique_ptr& operator=(std::unique_ptr<U>&& u_ptr) noexcept {
|
|
std::unique_ptr<T>::reset(u_ptr.release());
|
|
return *this;
|
|
}
|
|
|
|
/**@}*/
|
|
|
|
/** @name Observers */
|
|
/**@{*/
|
|
|
|
/** Return true if this container is empty, which is the state the container
|
|
is in immediately after default construction and various other
|
|
operations. */
|
|
bool empty() const noexcept { return !(*this); }
|
|
|
|
/** Return a const pointer to the contained object if any, or `nullptr`.
|
|
Note that this is different than `%get()` for the standard smart pointers
|
|
like `std::unique_ptr` which return a writable pointer. Use get_mutable()
|
|
here for that purpose. */
|
|
const T* get() const noexcept { return std::unique_ptr<T>::get(); }
|
|
|
|
// TODO(SeanCurtis-TRI): Consider adding some debug assertions about whether
|
|
// T is const or not. If so, it would be nice to give feedback that calling
|
|
// the mutable version makes no sense.
|
|
/** Return a writable pointer to the contained object if any, or `nullptr`.
|
|
Note that you need write access to this container in order to get write
|
|
access to the object it contains.
|
|
|
|
@warning If %copyable_unique_ptr is instantiated on a const template
|
|
parameter (e.g., `copyable_unique_ptr<const Foo>`), then get_mutable()
|
|
returns a const pointer. */
|
|
T* get_mutable() noexcept { return std::unique_ptr<T>::get(); }
|
|
|
|
// TODO(15344) We need to shore up this const correctness hole. Rather than an
|
|
// is-a relationship, we need some alternative relationship that will provide
|
|
// the same functionality but not be upcastable. One possibility is to own
|
|
// an unique_ptr and forward various APIs. Another is to implement from
|
|
// scratch. The current "is-A" relationship was intended so that the
|
|
// copyable_unique_ptr could be used where unique_ptrs are used. What would
|
|
// the impact of such a change in the relationship be to Drake and Drake
|
|
// users?
|
|
|
|
/** Return a const reference to the contained object. Note that this is
|
|
different from `std::unique_ptr::operator*()` which would return a non-const
|
|
reference (if `T` is non-const), even if the container itself is const. For
|
|
a const %copyable_unique_ptr will always return a const reference to its
|
|
contained value.
|
|
|
|
@warning Currently %copyable_unique_ptr is a std::unique_ptr. As such, a
|
|
const copyable_unique_ptr<Foo> can be upcast to a const unique_ptr<Foo> and
|
|
the parent's behavior will provide a mutable reference. This is strongly
|
|
discouraged and will break as the implementation of this class changes to
|
|
shore up this gap in the const correctness protection.
|
|
|
|
@pre `this != nullptr` reports `true`. */
|
|
const T& operator*() const {
|
|
DRAKE_ASSERT(!empty());
|
|
return *get();
|
|
}
|
|
|
|
/** Return a writable reference to the contained object (if T is itself not
|
|
const). Note that you need write access to this container in order to get
|
|
write access to the object it contains.
|
|
|
|
We *strongly* recommend, that, if dereferencing a %copyable_unique_ptr
|
|
without the intention of mutating the underlying value, prefer to dereference
|
|
a *const* %copyable_unique_ptr (or use *my_ptr.get()) and not a mutable
|
|
%copyable_unique_ptr. As "copy-on-write" behavior is introduced in the
|
|
future, this recommended practice will prevent unwanted copies of the
|
|
underlying value.
|
|
|
|
If %copyable_unique_ptr is instantiated on a const template parameter (e.g.,
|
|
`copyable_unique_ptr<const Foo>`), then operator*() must return a const
|
|
reference.
|
|
|
|
@pre `this != nullptr` reports `true`. */
|
|
T& operator*() {
|
|
DRAKE_ASSERT(!empty());
|
|
return *get_mutable();
|
|
}
|
|
|
|
/**@}*/
|
|
private:
|
|
// The can_copy() and can_clone() methods must be defined within the
|
|
// copyable_unique_ptr class so that they have the same method access as
|
|
// the class does. That way we can use them to determine whether
|
|
// copyable_unique_ptr can get access. That precludes using helper classes
|
|
// like drake::is_cloneable because those may have different access due to an
|
|
// explicit friend declaration giving copyable_unique_ptr<Foo> access to Foo's
|
|
// private business. The static_assert below ensures that at least one of
|
|
// these must return true.
|
|
|
|
// SFINAE magic explanation. We're combining several tricks here:
|
|
// (1) "..." as a parameter type is a last choice; an exact type match is
|
|
// preferred in overload resolution.
|
|
// (2) Use a dummy template parameter U that is always just T but defers
|
|
// instantiation so that substitution failure is not fatal.
|
|
// (3) We construct a non-evaluated copy constructor and Clone method in
|
|
// templatized methods to prevent instantiation if the needed method
|
|
// doesn't exist or isn't accessible. If instantiation is successful,
|
|
// we produce an exact-match method that trumps the "..."-using method.
|
|
// (4) Make these constexpr so they can be used in static_assert.
|
|
|
|
// True iff type T provides a copy constructor that is accessible from
|
|
// %copyable_unique_ptr<T>. Invoke with `can_copy(1)`; the argument is used
|
|
// to select the right method.
|
|
static constexpr bool can_copy(...) { return false; }
|
|
|
|
// If this instantiates successfully it will be the preferred method called
|
|
// when an integer argument is provided.
|
|
template <typename U = T>
|
|
static constexpr std::enable_if_t<
|
|
std::is_same_v<decltype(U(std::declval<const U&>())), U>,
|
|
bool>
|
|
can_copy(int) {
|
|
return true;
|
|
}
|
|
|
|
// True iff type T provides a `Clone()` method with the appropriate
|
|
// signature (see class documentation) that is accessible from
|
|
// %copyable_unique_ptr<T>. Invoke with `can_clone(1)`; the argument is used
|
|
// to select the right method.
|
|
static constexpr bool can_clone(...) { return false; }
|
|
|
|
// If this instantiates successfully it will be the preferred method called
|
|
// when an integer argument is provide.
|
|
template <typename U = T>
|
|
static constexpr std::enable_if_t<
|
|
std::is_same_v<decltype(std::declval<const U>().Clone()),
|
|
std::unique_ptr<std::remove_const_t<U>>>,
|
|
bool>
|
|
can_clone(int) {
|
|
return true;
|
|
}
|
|
|
|
// If src is non-null, clone it; otherwise return nullptr.
|
|
// If both can_copy() and can_clone() return true, we will prefer the Clone()
|
|
// function over the copy constructor.
|
|
// The caller has ownership over the return value.
|
|
static T* CopyOrNull(const T* raw) {
|
|
constexpr bool check_can_clone = can_clone(1);
|
|
constexpr bool check_can_copy = can_copy(1);
|
|
static_assert(
|
|
check_can_clone || check_can_copy,
|
|
"copyable_unique_ptr<T> can only be used with a 'copyable' class T, "
|
|
"requiring either a copy constructor or a Clone method of the form "
|
|
"'unique_ptr<T> Clone() const'.");
|
|
if (raw == nullptr) {
|
|
return nullptr;
|
|
}
|
|
if constexpr (check_can_clone) {
|
|
return raw->Clone().release();
|
|
} else {
|
|
return new T(*raw);
|
|
}
|
|
}
|
|
};
|
|
|
|
// TODO(jwnimmer-tri) On 2023-06-01 also remove the <ostream> include above.
|
|
template <class charT, class traits, class T>
|
|
DRAKE_DEPRECATED("2023-06-01",
|
|
"Use fmt or spdlog for logging, not operator<<. "
|
|
"See https://github.com/RobotLocomotion/drake/issues/17742 for details.")
|
|
std::basic_ostream<charT, traits>& operator<<(
|
|
std::basic_ostream<charT, traits>& os,
|
|
const copyable_unique_ptr<T>& cu_ptr) {
|
|
os << cu_ptr.get();
|
|
return os;
|
|
}
|
|
|
|
} // namespace drake
|
|
|
|
DRAKE_FORMATTER_AS(typename T, drake, copyable_unique_ptr<T>, x,
|
|
static_cast<const void*>(x.get()))
|