+ implementation

This commit is contained in:
Roland Conybeare 2023-09-23 15:48:36 -04:00
commit 87b4bfa795
10 changed files with 1446 additions and 0 deletions

View 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 */

View 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 */

View 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 */

View 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 */