1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
|
#if ISPTR_USE_MODULES
import isptr;
#else
#include <intrusive_shared_ptr/ref_counted.h>
#include <intrusive_shared_ptr/refcnt_ptr.h>
#endif
#include <doctest/doctest.h>
#include <type_traits>
#include <cstdint>
#include <cstddef>
using namespace isptr;
namespace
{
template<class T, class Inner, class Converter>
class ref_counted_delegating_traits;
template<class T, class Inner, class Converter>
struct ref_counted_weak_delegating_traits
{
using strong_value_type = Inner;
using strong_ptr_traits = ref_counted_delegating_traits<T, Inner, Converter>;
static void add_ref(const void * ref) noexcept
{ real_weak_from_delegating_weak(ref)->add_ref(); }
static void sub_ref(const void * ref) noexcept
{ real_weak_from_delegating_weak(ref)->sub_ref(); }
static const Inner * lock(const void * ref) noexcept
{
auto outer_strong = real_weak_from_delegating_weak(ref)->lock_owner();
return outer_strong ? delegating_from_real(outer_strong) : nullptr;
}
static Inner * lock(void * ref) noexcept
{
auto outer_strong = real_weak_from_delegating_weak(ref)->lock_owner();
return outer_strong ? delegating_from_real(outer_strong) : nullptr;
}
private:
static T * real_from_delegating(Inner * pinner)
{ return const_cast<T *>(Converter::real_from_delegating(const_cast<const Inner *>(pinner))); }
static const T * real_from_delegating(const Inner * pinner)
{ return const_cast<const T *>(Converter::real_from_delegating(pinner)); }
static Inner * delegating_from_real(T * pouter)
{ return const_cast<Inner *>(Converter::delegating_from_real(const_cast<const T *>(pouter))); }
static const Inner * delegating_from_real(const T * pouter)
{ return const_cast<const Inner *>(Converter::delegating_from_real(pouter)); }
static typename T::weak_value_type * real_weak_from_delegating_weak(void * ref)
{ return static_cast<typename T::weak_value_type *>(ref); }
static const typename T::weak_value_type * real_weak_from_delegating_weak(const void * ref)
{ return static_cast<const typename T::weak_value_type *>(ref); }
};
template<class T, class Inner, class Converter>
class ref_counted_delegating_traits
{
public:
using weak_value_type = void;
public:
using weak_ptr_traits = std::conditional_t<T::provides_weak_references, ref_counted_weak_delegating_traits<T, Inner, Converter>, void>;
static void add_ref(const Inner * pinner) noexcept
{ real_from_delegating(pinner)->add_ref(); }
static void sub_ref(const Inner * pinner) noexcept
{ real_from_delegating(pinner)->sub_ref(); }
static const void * get_weak_value(const Inner * pinner)
{ return real_from_delegating(pinner)->get_weak_value(); }
static void * get_weak_value(Inner * pinner)
{ return const_cast<void *>(ref_counted_delegating_traits::get_weak_value(const_cast<const Inner *>(pinner))); }
private:
static T * real_from_delegating(Inner * pinner)
{ return const_cast<T *>(Converter::real_from_delegating(const_cast<const Inner *>(pinner))); }
static const T * real_from_delegating(const Inner * pinner)
{ return const_cast<const T *>(Converter::real_from_delegating(pinner)); }
};
template<class T, class Inner, class Converter>
inline
intrusive_shared_ptr<void, ref_counted_weak_delegating_traits<T,Inner, Converter>>
weak_cast(const intrusive_shared_ptr<Inner, ref_counted_delegating_traits<T,Inner, Converter>> & src)
{
using dst_type = intrusive_shared_ptr<void, ref_counted_weak_delegating_traits<T,Inner, Converter>>;
return dst_type::noref(ref_counted_delegating_traits<T,Inner, Converter>::get_weak_value(src.get()));
}
template<class T, class Inner, class Converter>
inline
intrusive_shared_ptr<const void, ref_counted_weak_delegating_traits<T,Inner, Converter>>
weak_cast(const intrusive_shared_ptr<const Inner, ref_counted_delegating_traits<T,Inner, Converter>> & src)
{
using dst_type = intrusive_shared_ptr<const void, ref_counted_weak_delegating_traits<T,Inner, Converter>>;
return dst_type::noref(ref_counted_delegating_traits<T,Inner, Converter>::get_weak_value(src.get()));
}
template<class T, class Inner, class Converter>
inline
intrusive_shared_ptr<Inner, ref_counted_delegating_traits<T,Inner, Converter>>
strong_cast(const intrusive_shared_ptr<void, ref_counted_weak_delegating_traits<T,Inner, Converter>> & src) noexcept
{
using dst_type = intrusive_shared_ptr<Inner, ref_counted_delegating_traits<T,Inner, Converter>>;
return dst_type::noref(ref_counted_weak_delegating_traits<T,Inner, Converter>::lock(src.get()));
}
template<class T, class Inner, class Converter>
inline
intrusive_shared_ptr<const Inner, ref_counted_delegating_traits<T,Inner, Converter>>
strong_cast(const intrusive_shared_ptr<const void, ref_counted_weak_delegating_traits<T,Inner, Converter>> & src) noexcept
{
using dst_type = intrusive_shared_ptr<const Inner, ref_counted_delegating_traits<T,Inner, Converter>>;
return dst_type::noref(ref_counted_weak_delegating_traits<T,Inner, Converter>::lock(src.get()));
}
class outer : public ref_counted<outer>
{
friend ref_counted;
private:
struct inner_converter
{
static const outer * real_from_delegating(const int * pinner) noexcept
{
outer * dummy = nullptr;
size_t distance = (uintptr_t)&(dummy->_inner) - (uintptr_t)dummy;
return (const outer *)((std::byte *)pinner - distance);
//return offsetof(outer, _inner);
}
};
using inner_traits = ref_counted_delegating_traits<outer, int, inner_converter>;
friend ref_counted_delegating_traits<outer, int, inner_converter>;
public:
using inner_ptr = intrusive_shared_ptr<int, inner_traits>;
using const_inner_ptr = intrusive_shared_ptr<const int, inner_traits>;
inner_ptr get_inner_ptr() noexcept
{
return inner_ptr::ref(&_inner);
}
const_inner_ptr get_inner_ptr() const noexcept
{
return const_inner_ptr::ref(&_inner);
}
int & inner()
{ return _inner; }
private:
int _inner = 0;
};
class weak_outer : public ref_counted<weak_outer, ref_counted_flags::provide_weak_references>
{
friend ref_counted;
private:
struct inner_converter
{
static const weak_outer * real_from_delegating(const int * pinner) noexcept
{
weak_outer * dummy = nullptr;
size_t distance = (uintptr_t)&(dummy->_inner) - (uintptr_t)dummy;
return (const weak_outer *)((std::byte *)pinner - distance);
//return offsetof(outer, _inner);
}
static const int * delegating_from_real(const weak_outer * pouter) noexcept
{ return &pouter->_inner; }
};
using inner_traits = ref_counted_delegating_traits<weak_outer, int, inner_converter>;
friend ref_counted_delegating_traits<weak_outer, int, inner_converter>;
public:
using inner_ptr = intrusive_shared_ptr<int, inner_traits>;
using const_inner_ptr = intrusive_shared_ptr<const int, inner_traits>;
using weak_inner_ptr = intrusive_shared_ptr<void, inner_traits::weak_ptr_traits>;
using const_weak_inner_ptr = intrusive_shared_ptr<const void, inner_traits::weak_ptr_traits>;
class weak_value_type : public weak_reference<weak_outer>
{
friend weak_outer;
friend ref_counted_weak_delegating_traits<weak_outer, int, inner_converter>;
private:
weak_value_type(intptr_t count, weak_outer * owner): weak_reference(count, owner)
{};
};
inner_ptr get_inner_ptr() noexcept
{
return inner_ptr::ref(&_inner);
}
const_inner_ptr get_inner_ptr() const noexcept
{
return const_inner_ptr::ref(&_inner);
}
weak_inner_ptr get_weak_inner_ptr()
{
return weak_inner_ptr::noref(inner_traits::get_weak_value(&_inner));
}
const_weak_inner_ptr get_weak_inner_ptr() const
{
return const_weak_inner_ptr::noref(inner_traits::get_weak_value(&_inner));
}
weak_value_type * make_weak_reference(intptr_t count) const
{
return new weak_value_type(count, const_cast<weak_outer *>(this));
}
int & inner()
{ return _inner; }
private:
int _inner = 0;
};
}
TEST_SUITE("inner_ref_counted") {
TEST_CASE( "Inner counting" ) {
auto pouter = refcnt_attach(new outer());
auto pinner = pouter->get_inner_ptr();
CHECK(pinner);
*pinner = 3;
CHECK(pouter->inner() == 3);
}
TEST_CASE( "Weak inner counting" ) {
SUBCASE( "Non const" ) {
auto pouter = refcnt_attach(new weak_outer());
auto pinner1 = pouter->get_inner_ptr();
CHECK(pinner1);
auto weak1 = pouter->get_weak_inner_ptr();
CHECK(weak1);
auto weak2 = weak_cast(pinner1);
CHECK(weak1 == weak2);
auto pinner2 = strong_cast(weak2);
CHECK(pinner2 == pinner1);
}
SUBCASE( "Const" ) {
auto pouter = refcnt_attach(const_cast<const weak_outer *>(new weak_outer()));
auto pinner1 = pouter->get_inner_ptr();
CHECK(pinner1);
auto weak1 = pouter->get_weak_inner_ptr();
CHECK(weak1);
auto weak2 = weak_cast(pinner1);
CHECK(weak1 == weak2);
auto pinner2 = strong_cast(weak2);
CHECK(pinner2 == pinner1);
}
}
}
|