mirror of
https://github.com/zeek/zeek.git
synced 2025-10-11 19:18:19 +00:00
Merge remote-tracking branch 'origin/topic/neverlord/intrusive-ptr'
* origin/topic/neverlord/intrusive-ptr: Add API documentation to IntrusivePtr Rename make{ Counted => _intrusive } Remove braces around single return statements Integrate review feedback Add and use new IntrusivePt type in Zeek
This commit is contained in:
commit
5cece12333
10 changed files with 350 additions and 77 deletions
301
src/IntrusivePtr.h
Normal file
301
src/IntrusivePtr.h
Normal file
|
@ -0,0 +1,301 @@
|
|||
// See the file "COPYING" in the main distribution directory for copyright.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
/**
|
||||
* An intrusive, reference counting smart pointer implementation. Much like
|
||||
* @c std::shared_ptr, this smart pointer models shared ownership of an object
|
||||
* through a pointer. Several @c IntrusivePtr instances may point to the same
|
||||
* object.
|
||||
*
|
||||
* The @c IntrusivePtr requires two free functions associated to @c T that must
|
||||
* be available via argument-dependent lookup: @c Ref and @c Unref. The former
|
||||
* increments the reference by one whenever a new owner participates in the
|
||||
* lifetime of the shared object and the latter decrements the reference count
|
||||
* by one. Once the reference count reaches zero, @c Unref also is responsible
|
||||
* for destroying the shared object.
|
||||
*
|
||||
* The @c IntrusivePtr works with any type that offers the two free functions,
|
||||
* but most notably is designed to work with @c BroObj and its subtypes.
|
||||
*
|
||||
* The same object may get managed via @c IntrusivePtr in one part of the
|
||||
* code base while another part of the program manages it manually by passing
|
||||
* raw pointers and calling @c Ref and @c Unref explicitly. However, new code
|
||||
* should use a smart pointer whenever possible to reduce boilerplate code and
|
||||
* increase robustness of the code (in particular w.r.t. exceptions).
|
||||
*/
|
||||
template <class T>
|
||||
class IntrusivePtr {
|
||||
public:
|
||||
// -- member types
|
||||
|
||||
using pointer = T*;
|
||||
|
||||
using const_pointer = const T*;
|
||||
|
||||
using element_type = T;
|
||||
|
||||
using reference = T&;
|
||||
|
||||
using const_reference = const T&;
|
||||
|
||||
// -- constructors, destructors, and assignment operators
|
||||
|
||||
constexpr IntrusivePtr() noexcept : ptr_(nullptr)
|
||||
{
|
||||
// nop
|
||||
}
|
||||
|
||||
constexpr IntrusivePtr(std::nullptr_t) noexcept : IntrusivePtr()
|
||||
{
|
||||
// nop
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new intrusive pointer for managing the lifetime of the object
|
||||
* pointed to by @c raw_ptr.
|
||||
* @param raw_ptr Pointer to the shared object.
|
||||
* @param add_ref Denotes whether the reference count of the object shall be
|
||||
* increased during construction.
|
||||
*/
|
||||
IntrusivePtr(pointer raw_ptr, bool add_ref) noexcept
|
||||
{
|
||||
setPtr(raw_ptr, add_ref);
|
||||
}
|
||||
|
||||
IntrusivePtr(IntrusivePtr&& other) noexcept : ptr_(other.detach())
|
||||
{
|
||||
// nop
|
||||
}
|
||||
|
||||
IntrusivePtr(const IntrusivePtr& other) noexcept
|
||||
{
|
||||
setPtr(other.get(), true);
|
||||
}
|
||||
|
||||
template <class U, class = std::enable_if_t<std::is_convertible_v<U*, T*>>>
|
||||
IntrusivePtr(IntrusivePtr<U> other) noexcept : ptr_(other.detach())
|
||||
{
|
||||
// nop
|
||||
}
|
||||
|
||||
~IntrusivePtr()
|
||||
{
|
||||
if ( ptr_ )
|
||||
Unref(ptr_);
|
||||
}
|
||||
|
||||
void swap(IntrusivePtr& other) noexcept
|
||||
{
|
||||
std::swap(ptr_, other.ptr_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches an object from the automated lifetime management and sets this
|
||||
* intrusive pointer to @c nullptr.
|
||||
* @returns the raw pointer without modifying the reference count.
|
||||
*/
|
||||
pointer detach() noexcept
|
||||
{
|
||||
auto result = ptr_;
|
||||
if ( result )
|
||||
ptr_ = nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function for assigning a new raw pointer. Equivalent to calling
|
||||
* @c operator= with an @c IntrusivePtr constructed from the arguments.
|
||||
* @param new_value Pointer to the new shared object.
|
||||
* @param add_ref Denotes whether the reference count of the new shared object
|
||||
* shall be increased.
|
||||
*/
|
||||
void reset(pointer new_value = nullptr, bool add_ref = true) noexcept
|
||||
{
|
||||
auto old = ptr_;
|
||||
setPtr(new_value, add_ref);
|
||||
if ( old )
|
||||
Unref(old);
|
||||
}
|
||||
|
||||
IntrusivePtr& operator=(IntrusivePtr other) noexcept
|
||||
{
|
||||
swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
pointer get() const noexcept
|
||||
{
|
||||
return ptr_;
|
||||
}
|
||||
|
||||
pointer operator->() const noexcept
|
||||
{
|
||||
return ptr_;
|
||||
}
|
||||
|
||||
reference operator*() const noexcept
|
||||
{
|
||||
return *ptr_;
|
||||
}
|
||||
|
||||
bool operator!() const noexcept
|
||||
{
|
||||
return !ptr_;
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return ptr_ != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
void setPtr(pointer raw_ptr, bool add_ref) noexcept
|
||||
{
|
||||
ptr_ = raw_ptr;
|
||||
if ( raw_ptr && add_ref )
|
||||
Ref(raw_ptr);
|
||||
}
|
||||
|
||||
pointer ptr_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience function for creating a reference counted object and wrapping it
|
||||
* into an intrusive pointers.
|
||||
* @param args Arguments for constructing the shared object of type @c T.
|
||||
* @returns an @c IntrusivePtr pointing to the new object.
|
||||
* @note This function assumes that any @c T starts with a reference count of 1.
|
||||
* @relates IntrusivePtr
|
||||
*/
|
||||
template <class T, class... Ts>
|
||||
IntrusivePtr<T> make_intrusive(Ts&&... args)
|
||||
{
|
||||
// Assumes that objects start with a reference count of 1!
|
||||
return {new T(std::forward<Ts>(args)...), false};
|
||||
}
|
||||
|
||||
// -- comparison to nullptr ----------------------------------------------------
|
||||
|
||||
/**
|
||||
* @relates IntrusivePtr
|
||||
*/
|
||||
template <class T>
|
||||
bool operator==(const IntrusivePtr<T>& x, std::nullptr_t) {
|
||||
return !x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @relates IntrusivePtr
|
||||
*/
|
||||
template <class T>
|
||||
bool operator==(std::nullptr_t, const IntrusivePtr<T>& x) {
|
||||
return !x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @relates IntrusivePtr
|
||||
*/
|
||||
template <class T>
|
||||
bool operator!=(const IntrusivePtr<T>& x, std::nullptr_t) {
|
||||
return static_cast<bool>(x);
|
||||
}
|
||||
|
||||
/**
|
||||
* @relates IntrusivePtr
|
||||
*/
|
||||
template <class T>
|
||||
bool operator!=(std::nullptr_t, const IntrusivePtr<T>& x) {
|
||||
return static_cast<bool>(x);
|
||||
}
|
||||
|
||||
// -- comparison to raw pointer ------------------------------------------------
|
||||
|
||||
/**
|
||||
* @relates IntrusivePtr
|
||||
*/
|
||||
template <class T>
|
||||
bool operator==(const IntrusivePtr<T>& x, const T* y) {
|
||||
return x.get() == y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @relates IntrusivePtr
|
||||
*/
|
||||
template <class T>
|
||||
bool operator==(const T* x, const IntrusivePtr<T>& y) {
|
||||
return x == y.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @relates IntrusivePtr
|
||||
*/
|
||||
template <class T>
|
||||
bool operator!=(const IntrusivePtr<T>& x, const T* y) {
|
||||
return x.get() != y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @relates IntrusivePtr
|
||||
*/
|
||||
template <class T>
|
||||
bool operator!=(const T* x, const IntrusivePtr<T>& y) {
|
||||
return x != y.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @relates IntrusivePtr
|
||||
*/
|
||||
template <class T>
|
||||
bool operator<(const IntrusivePtr<T>& x, const T* y)
|
||||
{
|
||||
return x.get() < y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @relates IntrusivePtr
|
||||
*/
|
||||
template <class T>
|
||||
bool operator<(const T* x, const IntrusivePtr<T>& y)
|
||||
{
|
||||
return x < y.get();
|
||||
}
|
||||
|
||||
// -- comparison to intrusive pointer ------------------------------------------
|
||||
|
||||
// Using trailing return type and decltype() here removes this function from
|
||||
// overload resolution if the two pointers types are not comparable (SFINAE).
|
||||
|
||||
/**
|
||||
* @relates IntrusivePtr
|
||||
*/
|
||||
template <class T, class U>
|
||||
auto operator==(const IntrusivePtr<T>& x, const IntrusivePtr<U>& y)
|
||||
-> decltype(x.get() == y.get())
|
||||
{
|
||||
return x.get() == y.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @relates IntrusivePtr
|
||||
*/
|
||||
template <class T, class U>
|
||||
auto operator!=(const IntrusivePtr<T>& x, const IntrusivePtr<U>& y)
|
||||
-> decltype(x.get() != y.get())
|
||||
{
|
||||
return x.get() != y.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @relates IntrusivePtr
|
||||
*/
|
||||
template <class T>
|
||||
auto operator<(const IntrusivePtr<T>& x, const IntrusivePtr<T>& y)
|
||||
-> decltype(x.get() < y.get())
|
||||
{
|
||||
return x.get() < y.get();
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue