+ implementation
This commit is contained in:
parent
0e18026fba
commit
87b4bfa795
10 changed files with 1446 additions and 0 deletions
92
include/cxxutil/demangle.hpp
Normal file
92
include/cxxutil/demangle.hpp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/* @file demangle.hpp */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <array> // std::array
|
||||
#include <utility> // std::index_sequence
|
||||
|
||||
namespace xo {
|
||||
namespace reflect {
|
||||
|
||||
template <std::size_t...Idxs>
|
||||
constexpr auto
|
||||
substring_as_array(std::string_view str,
|
||||
std::index_sequence<Idxs...> indexes)
|
||||
{
|
||||
//return std::array<char, indexes.size()+1>{str[Idxs]..., '\n'};
|
||||
return std::array<char, indexes.size()>{str[Idxs]...};
|
||||
} /*substring_as_array*/
|
||||
|
||||
template <typename T> constexpr auto type_name_array() {
|
||||
#if defined(__clang__)
|
||||
constexpr auto prefix = std::string_view{"[T = "};
|
||||
constexpr auto suffix = std::string_view{"]"};
|
||||
constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
|
||||
#elif defined(__GNUC__)
|
||||
constexpr auto prefix = std::string_view{"with T = "};
|
||||
constexpr auto suffix = std::string_view{"]"};
|
||||
constexpr auto function = std::string_view{__PRETTY_FUNCTION__};
|
||||
#elif defined(_MSC_VER)
|
||||
constexpr auto prefix = std::string_view{"type_name_array<"};
|
||||
constexpr auto suffix = std::string_view{">(void)"};
|
||||
constexpr auto function = std::string_view{__FUNCSIG__};
|
||||
#else
|
||||
# error type_name_array: Unsupported compiler
|
||||
#endif
|
||||
|
||||
constexpr auto start = function.find(prefix) + prefix.size();
|
||||
constexpr auto end = function.rfind(suffix);
|
||||
|
||||
//static_assert(start < end);
|
||||
|
||||
constexpr auto name = function.substr(start, (end - start));
|
||||
|
||||
constexpr auto ixseq = std::make_index_sequence<name.size()>{};
|
||||
|
||||
return substring_as_array(name, ixseq);
|
||||
} /*type_name_array*/
|
||||
|
||||
template <typename T>
|
||||
struct type_name_holder {
|
||||
static inline constexpr auto value = type_name_array<T>();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr auto type_name() -> std::string_view
|
||||
{
|
||||
constexpr auto& value = type_name_holder<T>::value;
|
||||
return std::string_view{value.data(), value.size()};
|
||||
}
|
||||
|
||||
#ifdef NOT_IN_USE
|
||||
template <std::string_view const&... Strs>
|
||||
struct join
|
||||
{
|
||||
// Join all strings into a single std::array of chars
|
||||
static constexpr auto impl() noexcept
|
||||
{
|
||||
constexpr std::size_t len = (Strs.size() + ... + 0);
|
||||
std::array<char, len + 1> arr{};
|
||||
auto append = [i = 0, &arr](auto const& s) mutable {
|
||||
for (auto c : s) arr[i++] = c;
|
||||
};
|
||||
(append(Strs), ...);
|
||||
arr[len] = 0;
|
||||
return arr;
|
||||
}
|
||||
// Give the joined string static storage
|
||||
static constexpr auto arr = impl();
|
||||
// View as a std::string_view
|
||||
static constexpr std::string_view value {arr.data(), arr.size() - 1};
|
||||
};
|
||||
|
||||
// Helper to get the value out
|
||||
template <std::string_view const&... Strs>
|
||||
static constexpr auto join_v = join<Strs...>::value;
|
||||
#endif
|
||||
} /*namespace reflect*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end demangle.hpp */
|
||||
29
include/refcnt/Displayable.hpp
Normal file
29
include/refcnt/Displayable.hpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* @file Displayable.hpp */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "refcnt/Refcounted.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace ref {
|
||||
class Displayable : public Refcount {
|
||||
public:
|
||||
/* write some kind of human-readable representation on stream */
|
||||
virtual void display(std::ostream & os) const = 0;
|
||||
std::string display_string() const;
|
||||
}; /*Displayable*/
|
||||
|
||||
/* see also
|
||||
* operator<<(std::ostream &, intrusive_ptr<T> const &)
|
||||
* in [Refcounted.hpp]
|
||||
*/
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream &os, Displayable const & x) {
|
||||
x.display(os);
|
||||
return os;
|
||||
} /*operator<<*/
|
||||
|
||||
} /*namespace ref*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end Displayable.hpp */
|
||||
294
include/refcnt/Refcounted.hpp
Normal file
294
include/refcnt/Refcounted.hpp
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
/* @file Refcounted.hpp */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "indentlog/scope.hpp"
|
||||
#include "cxxutil/demangle.hpp"
|
||||
|
||||
//#include <boost/intrusive_ptr.hpp>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
|
||||
namespace xo {
|
||||
namespace ref {
|
||||
class Refcount;
|
||||
|
||||
template<typename T>
|
||||
class Borrow;
|
||||
|
||||
/* originally used boost::instrusive_ptr<>.
|
||||
* ran into a bug. probably mine, but implemented
|
||||
* refcounting inline for debugging
|
||||
*/
|
||||
template<typename T>
|
||||
class intrusive_ptr {
|
||||
public:
|
||||
using element_type = T;
|
||||
|
||||
public:
|
||||
intrusive_ptr() : ptr_(nullptr) {}
|
||||
intrusive_ptr(T * x) : ptr_(x) {
|
||||
intrusive_ptr_log_ctor(sc_self_type, this, x);
|
||||
intrusive_ptr_add_ref(ptr_);
|
||||
} /*ctor*/
|
||||
|
||||
/* NOTE: need exactly this form for copy-constructor
|
||||
* clang11 will not recognize template form below as
|
||||
* supplying copy ctor, and default version is broken for
|
||||
* instrusive_ptr.
|
||||
*/
|
||||
intrusive_ptr(intrusive_ptr const & x) : ptr_(x.get()) {
|
||||
intrusive_ptr_log_cctor(sc_self_type, this, x.get());
|
||||
intrusive_ptr_add_ref(ptr_);
|
||||
} /*cctor*/
|
||||
|
||||
/* create from instrusive pointer to some related type S */
|
||||
template<typename S>
|
||||
intrusive_ptr(intrusive_ptr<S> const & x) : ptr_(x.get()) {
|
||||
intrusive_ptr_log_cctor(sc_self_type, this, x.get());
|
||||
intrusive_ptr_add_ref(ptr_);
|
||||
} /*cctor*/
|
||||
|
||||
/* move ctor -- in this case don't need to update refcount */
|
||||
intrusive_ptr(intrusive_ptr && x) : ptr_{std::move(x.ptr_)} {
|
||||
intrusive_ptr_log_mctor(sc_self_type, this, ptr_);
|
||||
/* since we're moving from x, need to make sure x dtor
|
||||
* doesn't decrement refcount
|
||||
*/
|
||||
x.ptr_ = nullptr;
|
||||
}
|
||||
|
||||
/* aliasing ctor. see ctor (8) here:
|
||||
* [[https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr]]
|
||||
* and this dicsussion:
|
||||
* [[https://stackoverflow.com/questions/49178231/pybind11-multiple-inheritance-with-custom-holder-type-fails-to-cast-to-base-type/73131206#73131206]]
|
||||
*/
|
||||
template<typename Y>
|
||||
intrusive_ptr(intrusive_ptr<Y> const & /*x*/, element_type * y) : ptr_{y} {
|
||||
if (std::is_same<Y, element_type>()) {
|
||||
intrusive_ptr_log_actor(sc_self_type, this, y);
|
||||
intrusive_ptr_add_ref(ptr_);
|
||||
; /* trivial aliasing, proceed */
|
||||
} else {
|
||||
using xo::xtag;
|
||||
throw std::runtime_error(tostr("attempt to use aliasing ctor with",
|
||||
xtag("Y", reflect::type_name<Y>()),
|
||||
xtag("T", reflect::type_name<T>())));
|
||||
}
|
||||
} /*ctor*/
|
||||
|
||||
~intrusive_ptr() {
|
||||
T * x = this->ptr_;
|
||||
|
||||
intrusive_ptr_log_dtor(sc_self_type, this, x);
|
||||
|
||||
this->ptr_ = nullptr;
|
||||
|
||||
intrusive_ptr_release(x);
|
||||
} /*dtor*/
|
||||
|
||||
static bool compare(intrusive_ptr<T> const & x,
|
||||
intrusive_ptr<T> const & y) {
|
||||
return ptrdiff_t(x.get() - y.get());
|
||||
}
|
||||
|
||||
Borrow<T> borrow() const;
|
||||
|
||||
T * get() const { return ptr_; }
|
||||
|
||||
T * operator->() const { return ptr_; }
|
||||
|
||||
operator bool() const { return ptr_ != nullptr; }
|
||||
|
||||
intrusive_ptr<T> & operator=(intrusive_ptr<T> const & rhs) {
|
||||
T * x = rhs.get();
|
||||
|
||||
intrusive_ptr_log_assign(sc_self_type, this, x);
|
||||
|
||||
T * old = this->ptr_;
|
||||
this->ptr_ = x;
|
||||
|
||||
intrusive_ptr_add_ref(x);
|
||||
intrusive_ptr_release(old);
|
||||
|
||||
return *this;
|
||||
} /*operator=*/
|
||||
|
||||
intrusive_ptr<T> & operator=(intrusive_ptr<T> && rhs) {
|
||||
intrusive_ptr_log_massign(sc_self_type, this, rhs.get());
|
||||
|
||||
std::swap(this->ptr_, rhs.ptr_);
|
||||
|
||||
/* dtor on rhs will decrement refcount on old value of this->ptr_
|
||||
* don't increment for new value, since refcount just transfers from rhs to *this
|
||||
*/
|
||||
|
||||
return *this;
|
||||
} /*operator=*/
|
||||
|
||||
private:
|
||||
static constexpr std::string_view sc_self_type = xo::reflect::type_name<intrusive_ptr<T>>();
|
||||
|
||||
private:
|
||||
T * ptr_ = nullptr;
|
||||
}; /*intrusive_ptr*/
|
||||
|
||||
template<typename T>
|
||||
inline bool operator==(intrusive_ptr<T> const & x, intrusive_ptr<T> const & y) { return intrusive_ptr<T>::compare(x, y) == 0; }
|
||||
|
||||
template<typename T>
|
||||
using rp = intrusive_ptr<T>;
|
||||
|
||||
class Refcount {
|
||||
public:
|
||||
Refcount() : reference_counter_(0) {}
|
||||
/* WARNING: virtual dtor here is essential,
|
||||
* since it's what allows us to invoke delete on a Refcount*,
|
||||
* for an object of some derived class type T. Otherwise clang
|
||||
* will use different addresses for Refcount-part and T-part of
|
||||
* such instance, which means pointer given to delete will not be
|
||||
* the same as pointer returned from new
|
||||
*/
|
||||
virtual ~Refcount() = default;
|
||||
|
||||
uint32_t reference_counter() const { return reference_counter_.load(); }
|
||||
|
||||
private:
|
||||
friend uint32_t intrusive_ptr_refcount(Refcount *);
|
||||
friend void intrusive_ptr_add_ref(Refcount *);
|
||||
friend void intrusive_ptr_release(Refcount *);
|
||||
|
||||
private:
|
||||
std::atomic<uint32_t> reference_counter_;
|
||||
}; /*Refcount*/
|
||||
|
||||
inline uint32_t
|
||||
intrusive_ptr_refcount(Refcount * x) {
|
||||
/* reporting accurately for diagnostics */
|
||||
if (x)
|
||||
return x->reference_counter_.load();
|
||||
else
|
||||
return 0;
|
||||
} /*intrusive_ptr_refcount*/
|
||||
|
||||
void intrusive_ptr_set_debug(bool x);
|
||||
void intrusive_ptr_log_ctor(std::string_view const & self_type,
|
||||
void * this_ptr,
|
||||
Refcount * x);
|
||||
/* here actor short for 'aliasing ctor' */
|
||||
void intrusive_ptr_log_actor(std::string_view const & self_type,
|
||||
void * this_ptr,
|
||||
Refcount * x);
|
||||
void intrusive_ptr_log_cctor(std::string_view const & self_type,
|
||||
void * this_ptr,
|
||||
Refcount * x);
|
||||
void intrusive_ptr_log_mctor(std::string_view const & self_type,
|
||||
void *this_ptr,
|
||||
Refcount * x);
|
||||
void intrusive_ptr_log_dtor(std::string_view const & self_type,
|
||||
void * this_ptr,
|
||||
Refcount * x);
|
||||
void intrusive_ptr_log_assign(std::string_view const & self_type,
|
||||
void * this_ptr,
|
||||
Refcount * x);
|
||||
void intrusive_ptr_log_massign(std::string_view const & self_type,
|
||||
void *this_ptr,
|
||||
Refcount * x);
|
||||
void intrusive_ptr_add_ref(Refcount * x);
|
||||
void intrusive_ptr_release(Refcount * x);
|
||||
|
||||
template<typename T>
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream & os, intrusive_ptr<T> const & x) {
|
||||
if(x.get()) {
|
||||
os << *(x.get());
|
||||
} else {
|
||||
os << "<nullptr " << reflect::type_name<T>() << ">";
|
||||
}
|
||||
return os;
|
||||
} /*operator<<*/
|
||||
|
||||
/* borrow a reference-counted pointer to pass down the stack
|
||||
* 1. borrowed pointer intended to replace:
|
||||
* a. code like
|
||||
* foo(rp<T> x),
|
||||
* passing rp<T> by value requires increment/decrement pair,
|
||||
* which is superfluous given that caller holds reference throughout
|
||||
* b. code like
|
||||
* foo(rp<T> const & x)
|
||||
* passing rp<T> by reference requires double-indirection in called
|
||||
* code
|
||||
* 2. borrowed pointer does not check/maintain reference count.
|
||||
* it should never be stored in a struct; intended strictly
|
||||
* to be passed down stack
|
||||
* 3. just the same, want to be able to copy the borrowed pointer,
|
||||
* to avoid double-indirection
|
||||
* 4. also can promote borrowed pointer to full reference-counted
|
||||
* whenever desired
|
||||
*/
|
||||
template<typename T>
|
||||
class Borrow {
|
||||
public:
|
||||
template<typename S>
|
||||
Borrow(rp<S> const & x) : ptr_(x.get()) {}
|
||||
|
||||
Borrow(Borrow const & x) = default;
|
||||
|
||||
/* convert from another borrow, if it has compatible pointer type */
|
||||
template<typename S>
|
||||
Borrow(Borrow<S> const & x) : ptr_(x.get()) {}
|
||||
|
||||
/* dynamic cast from a pointer to an object of some convertible type */
|
||||
template<typename S>
|
||||
static Borrow<T> from(Borrow<S> x) {
|
||||
return Borrow(dynamic_cast<T *>(x.get()));
|
||||
} /*from*/
|
||||
|
||||
/* promote from native pointer */
|
||||
static Borrow<T> from_native(T * x) {
|
||||
return Borrow(x);
|
||||
} /*from_native*/
|
||||
|
||||
T * get() const { return ptr_; }
|
||||
|
||||
rp<T> promote() const { return rp<T>(ptr_); }
|
||||
|
||||
T & operator*() const { return *ptr_; }
|
||||
T * operator->() const { return ptr_; }
|
||||
|
||||
operator bool() const { return ptr_ != nullptr; }
|
||||
|
||||
static int32_t compare(Borrow const & x, Borrow const & y) {
|
||||
return ptrdiff_t(x.get() - y.get());
|
||||
} /*compare*/
|
||||
|
||||
static int32_t compare(rp<T> const & x, Borrow const & y) {
|
||||
return ptrdiff_t(x.get() - y.get());
|
||||
} /*compare*/
|
||||
|
||||
private:
|
||||
Borrow(T * x) : ptr_(x) {}
|
||||
|
||||
private:
|
||||
T * ptr_ = nullptr;
|
||||
}; /*Borrow*/
|
||||
|
||||
template<typename T>
|
||||
inline bool operator==(Borrow<T> x, Borrow<T> y) { return Borrow<T>::compare(x, y) == 0; }
|
||||
|
||||
template<typename T>
|
||||
inline bool operator==(rp<T> const & x, Borrow<T> y) { return Borrow<T>::compare(x, y) == 0; }
|
||||
|
||||
template<typename T>
|
||||
using brw = Borrow<T>;
|
||||
|
||||
template<typename T>
|
||||
Borrow<T>
|
||||
intrusive_ptr<T>::borrow() const {
|
||||
return Borrow<T>(*this);
|
||||
} /*borrow*/
|
||||
|
||||
} /*namespace ref*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end Refcounted.hpp */
|
||||
28
include/refcnt/Unowned.hpp
Normal file
28
include/refcnt/Unowned.hpp
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/* @file Unowned.hpp */
|
||||
|
||||
namespace xo {
|
||||
namespace ref {
|
||||
/* use this is a holder type for pointers that pybind11 should treat
|
||||
* as "not-my-problem". in particular that pybind11 should never delete.
|
||||
*/
|
||||
template<typename T>
|
||||
class unowned_ptr {
|
||||
public:
|
||||
unowned_ptr(T * x) : ptr_{x} {}
|
||||
unowned_ptr(unowned_ptr const & x) = default;
|
||||
~unowned_ptr() = default;
|
||||
|
||||
T * get() const { return ptr_; }
|
||||
T * operator->() const { return ptr_; }
|
||||
|
||||
operator bool() const { return ptr_ != nullptr; }
|
||||
|
||||
unowned_ptr<T> & operator=(unowned_ptr<T> const & rhs) = default;
|
||||
|
||||
private:
|
||||
T * ptr_ = nullptr;
|
||||
}; /*unowned_ptr*/
|
||||
} /*namespace ref*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end Unowned.hpp */
|
||||
Loading…
Add table
Add a link
Reference in a new issue