Merge branch 'feature/intrusive_ptr' into feature/world

This commit is contained in:
2019-01-15 07:50:28 +07:00
4 changed files with 730 additions and 0 deletions

View File

@@ -17,6 +17,7 @@
#include "mesh.hpp"
#include "module.hpp"
#include "path.hpp"
#include "refcount.hpp"
#include "streams.hpp"
#include "strfmts.hpp"
#include "strings.hpp"

View File

@@ -25,6 +25,9 @@ namespace e2d
template < typename T >
class module;
template < typename T >
class intrusive_ptr;
template < typename Char >
class basic_string_hash;
}

View File

@@ -0,0 +1,465 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#pragma once
#include "_utils.hpp"
namespace e2d
{
template < typename CounterValue = std::uint32_t >
struct ref_counter_thread_safe_policy;
template < typename CounterValue = std::uint32_t >
struct ref_counter_thread_unsafe_policy;
template < typename Derived
, typename CounterPolicy = ref_counter_thread_safe_policy<> >
class ref_counter;
template < typename Derived, typename CounterPolicy >
void intrusive_ptr_add_ref(const ref_counter<Derived, CounterPolicy>* rc) noexcept;
template < typename Derived, typename CounterPolicy >
void intrusive_ptr_release(const ref_counter<Derived, CounterPolicy>* rc) noexcept;
}
namespace e2d
{
template < typename CounterValue >
struct ref_counter_thread_safe_policy {
using counter_t = std::atomic<CounterValue>;
using counter_value_t = CounterValue;
static counter_value_t incr(counter_t& c) noexcept {
return c.fetch_add(
counter_value_t(1),
std::memory_order_acq_rel) + counter_value_t(1);
}
static counter_value_t decr(counter_t& c) noexcept {
return c.fetch_sub(
counter_value_t(1),
std::memory_order_acq_rel) - counter_value_t(1);
}
static counter_value_t load(const counter_t& c) noexcept {
return c.load(std::memory_order_acquire);
}
};
template < typename CounterValue >
struct ref_counter_thread_unsafe_policy {
using counter_t = CounterValue;
using counter_value_t = CounterValue;
static counter_value_t incr(counter_t& c) noexcept {
return ++c;
}
static counter_value_t decr(counter_t& c) noexcept {
return --c;
}
static counter_value_t load(const counter_t& c) noexcept {
return c;
}
};
template < typename Derived, typename CounterPolicy >
class ref_counter {
public:
using self_type = ref_counter;
using counter_t = typename CounterPolicy::counter_t;
using counter_value_t = typename CounterPolicy::counter_value_t;
friend void intrusive_ptr_add_ref<Derived, CounterPolicy>(const ref_counter* rc) noexcept;
friend void intrusive_ptr_release<Derived, CounterPolicy>(const ref_counter* rc) noexcept;
public:
ref_counter() = default;
~ref_counter() noexcept = default;
ref_counter(const ref_counter& other) noexcept {
E2D_UNUSED(other);
}
ref_counter& operator=(const ref_counter& other) noexcept {
E2D_UNUSED(other);
return *this;
}
counter_value_t use_count() const noexcept {
return CounterPolicy::load(counter_);
}
private:
mutable counter_t counter_{0};
};
}
namespace e2d
{
template < typename Derived, typename CounterPolicy >
void intrusive_ptr_add_ref(const ref_counter<Derived, CounterPolicy>* rc) noexcept {
CounterPolicy::incr(rc->counter_);
}
template < typename Derived, typename CounterPolicy >
void intrusive_ptr_release(const ref_counter<Derived, CounterPolicy>* rc) noexcept {
if ( 0u == CounterPolicy::decr(rc->counter_) ) {
delete static_cast<const Derived*>(rc);
}
}
}
namespace e2d
{
template < typename T >
class intrusive_ptr final {
public:
using self_type = intrusive_ptr;
using element_type = T;
template < typename U >
friend class intrusive_ptr;
constexpr static bool is_nothrow_add_ref_v = noexcept(intrusive_ptr_add_ref(std::declval<T*>()));
constexpr static bool is_nothrow_release_v = noexcept(intrusive_ptr_release(std::declval<T*>()));
constexpr static bool is_nothrow_counter_v = is_nothrow_add_ref_v && is_nothrow_release_v;
public:
intrusive_ptr() = default;
~intrusive_ptr() noexcept(is_nothrow_release_v);
intrusive_ptr(intrusive_ptr&& other) noexcept;
intrusive_ptr& operator=(intrusive_ptr&& other) noexcept(is_nothrow_release_v);
intrusive_ptr(const intrusive_ptr& other) noexcept(is_nothrow_add_ref_v);
intrusive_ptr& operator=(const intrusive_ptr& other) noexcept(is_nothrow_counter_v);
template < typename U
, typename = std::enable_if_t<std::is_convertible<U,T>::value> >
intrusive_ptr(intrusive_ptr<U>&& other) noexcept;
template < typename U
, typename = std::enable_if_t<std::is_convertible<U,T>::value> >
intrusive_ptr& operator=(intrusive_ptr<U>&& other) noexcept(is_nothrow_release_v);
template < typename U
, typename = std::enable_if_t<std::is_convertible<U,T>::value> >
intrusive_ptr(const intrusive_ptr<U>& other) noexcept(is_nothrow_add_ref_v);
template < typename U
, typename = std::enable_if_t<std::is_convertible<U,T>::value> >
intrusive_ptr& operator=(const intrusive_ptr<U>& other) noexcept(is_nothrow_counter_v);
intrusive_ptr(T* ptr, bool add_ref = true) noexcept(is_nothrow_add_ref_v);
void reset() noexcept(is_nothrow_release_v);
void reset(T* other) noexcept(is_nothrow_counter_v);
void reset(T* other, bool add_ref) noexcept(is_nothrow_counter_v);
T* get() const noexcept;
T* release() noexcept;
T& operator*() const noexcept;
T* operator->() const noexcept;
explicit operator bool() const noexcept;
void swap(intrusive_ptr<T>& other) noexcept;
private:
T* ptr_{nullptr};
};
}
namespace e2d
{
template < typename T >
intrusive_ptr<T>::~intrusive_ptr()
noexcept(is_nothrow_release_v)
{
if ( ptr_ ) {
intrusive_ptr_release(ptr_);
}
}
template < typename T >
intrusive_ptr<T>::intrusive_ptr(intrusive_ptr&& other) noexcept
: ptr_(other.ptr_) {
other.ptr_ = nullptr;
}
template < typename T >
intrusive_ptr<T>& intrusive_ptr<T>::operator=(intrusive_ptr&& other)
noexcept(is_nothrow_release_v)
{
self_type(std::move(other)).swap(*this);
return *this;
}
template < typename T >
intrusive_ptr<T>::intrusive_ptr(const intrusive_ptr& other)
noexcept(is_nothrow_add_ref_v)
: ptr_(other.ptr_) {
if ( ptr_ ) {
intrusive_ptr_add_ref(ptr_);
}
}
template < typename T >
intrusive_ptr<T>& intrusive_ptr<T>::operator=(const intrusive_ptr& other)
noexcept(is_nothrow_counter_v)
{
self_type(other).swap(*this);
return *this;
}
template < typename T >
template < typename U, typename >
intrusive_ptr<T>::intrusive_ptr(intrusive_ptr<U>&& other) noexcept
: ptr_(other.ptr_) {
other.ptr_ = nullptr;
}
template < typename T >
template < typename U, typename >
intrusive_ptr<T>& intrusive_ptr<T>::operator=(intrusive_ptr<U>&& other)
noexcept(is_nothrow_release_v)
{
self_type(std::move(other)).swap(*this);
return *this;
}
template < typename T >
template < typename U, typename >
intrusive_ptr<T>::intrusive_ptr(const intrusive_ptr<U>& other)
noexcept(is_nothrow_add_ref_v)
: ptr_(other.ptr_) {
if ( ptr_ ) {
intrusive_ptr_add_ref(ptr_);
}
}
template < typename T >
template < typename U, typename >
intrusive_ptr<T>& intrusive_ptr<T>::operator=(const intrusive_ptr<U>& other)
noexcept(is_nothrow_counter_v)
{
self_type(other).swap(*this);
return *this;
}
template < typename T >
intrusive_ptr<T>::intrusive_ptr(T* ptr, bool add_ref)
noexcept(is_nothrow_add_ref_v)
: ptr_(ptr) {
if ( ptr_ && add_ref ) {
intrusive_ptr_add_ref(ptr_);
}
}
template < typename T >
void intrusive_ptr<T>::reset()
noexcept(is_nothrow_release_v)
{
self_type().swap(*this);
}
template < typename T >
void intrusive_ptr<T>::reset(T* other)
noexcept(is_nothrow_counter_v)
{
self_type(other).swap(*this);
}
template < typename T >
void intrusive_ptr<T>::reset(T* other, bool add_ref)
noexcept(is_nothrow_counter_v)
{
self_type(other, add_ref).swap(*this);
}
template < typename T >
T* intrusive_ptr<T>::get() const noexcept {
return ptr_;
}
template < typename T >
T* intrusive_ptr<T>::release() noexcept {
T* result = ptr_;
ptr_ = nullptr;
return result;
}
template < typename T >
T& intrusive_ptr<T>::operator*() const noexcept {
E2D_ASSERT(ptr_);
return *ptr_;
}
template < typename T >
T* intrusive_ptr<T>::operator->() const noexcept {
E2D_ASSERT(ptr_);
return ptr_;
}
template < typename T >
intrusive_ptr<T>::operator bool() const noexcept {
return !!ptr_;
}
template < typename T >
void intrusive_ptr<T>::swap(intrusive_ptr<T>& other) noexcept {
T* tmp = ptr_;
ptr_ = other.ptr_;
other.ptr_ = tmp;
}
}
namespace e2d
{
//
// swap
//
template < typename T >
void swap(intrusive_ptr<T>& l, intrusive_ptr<T>& r) noexcept {
l.swap(r);
}
//
// make_intrusive
//
template < typename T, typename... Args >
intrusive_ptr<T> make_intrusive(Args&&... args) {
return intrusive_ptr<T>(new T(std::forward<Args>(args)...));
}
//
// iptr (==,!=) iptr
//
template < typename T, typename U >
bool operator==(const intrusive_ptr<T>& l, const intrusive_ptr<U>& r) noexcept {
return l.get() == r.get();
}
template < typename T, typename U >
bool operator!=(const intrusive_ptr<T>& l, const intrusive_ptr<U>& r) noexcept {
return l.get() != r.get();
}
//
// iptr (==,!=) ptr
//
template < typename T, typename U >
bool operator==(const intrusive_ptr<T>& l, const U* r) noexcept {
return l.get() == r;
}
template < typename T, typename U >
bool operator!=(const intrusive_ptr<T>& l, const U* r) noexcept {
return l.get() != r;
}
//
// ptr (==,!=) iptr
//
template < typename T, typename U >
bool operator==(const T* l, const intrusive_ptr<U>& r) noexcept {
return l == r.get();
}
template < typename T, typename U >
bool operator!=(const T* l, const intrusive_ptr<U>& r) noexcept {
return l != r.get();
}
//
// iptr (==,!=) nullptr
//
template < typename T >
bool operator==(const intrusive_ptr<T>& l, std::nullptr_t) noexcept {
return l.get() == nullptr;
}
template < typename T >
bool operator!=(const intrusive_ptr<T>& l, std::nullptr_t) noexcept {
return l.get() != nullptr;
}
//
// nullptr (==,!=) iptr
//
template < typename T >
bool operator==(std::nullptr_t, const intrusive_ptr<T>& r) noexcept {
return nullptr == r.get();
}
template < typename T >
bool operator!=(std::nullptr_t, const intrusive_ptr<T>& r) noexcept {
return nullptr != r.get();
}
//
// iptr (<,<=,>,>=) iptr
//
template < typename T >
bool operator<(const intrusive_ptr<T>& l, const intrusive_ptr<T>& r) noexcept {
return std::less<T*>()(l.get(), r.get());
}
template < typename T >
bool operator<=(const intrusive_ptr<T>& l, const intrusive_ptr<T>& r) noexcept {
return std::less_equal<T*>()(l.get(), r.get());
}
template < typename T >
bool operator>(const intrusive_ptr<T>& l, const intrusive_ptr<T>& r) noexcept {
return std::greater<T*>()(l.get(), r.get());
}
template < typename T >
bool operator>=(const intrusive_ptr<T>& l, const intrusive_ptr<T>& r) noexcept {
return std::greater_equal<T*>()(l.get(), r.get());
}
//
// iptr casts
//
template < typename T, typename U >
intrusive_ptr<T> const_pointer_cast(const intrusive_ptr<U>& p) noexcept {
return const_cast<T*>(p.get());
}
template < typename T, typename U >
intrusive_ptr<T> static_pointer_cast(const intrusive_ptr<U>& p) noexcept {
return static_cast<T*>(p.get());
}
template < typename T, typename U >
intrusive_ptr<T> dynamic_pointer_cast(const intrusive_ptr<U>& p) noexcept {
return dynamic_cast<T*>(p.get());
}
}
namespace std
{
template < typename T >
struct hash<e2d::intrusive_ptr<T>> final
: std::unary_function<e2d::intrusive_ptr<T>, std::size_t>
{
std::size_t operator()(const e2d::intrusive_ptr<T>& p) const noexcept {
return std::hash<T*>()(p.get());
}
};
}

View File

@@ -0,0 +1,261 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include "_utils.hpp"
using namespace e2d;
namespace
{
class obj_t : public ref_counter<obj_t> {
public:
obj_t() {
++ctor_counter;
}
obj_t(int ni) : i(ni) {
++ctor_counter;
}
virtual ~obj_t() noexcept {
++dtor_counter;
}
int i{42};
public:
static int ctor_counter;
static int dtor_counter;
static void reset_counters() {
ctor_counter = 0u;
dtor_counter = 0u;
}
};
int obj_t::ctor_counter = 0;
int obj_t::dtor_counter = 0;
class derived_t final : public obj_t {
public:
derived_t() : obj_t(84) {}
int j{100500};
};
class derived2_t final : public obj_t {
public:
derived2_t() : obj_t(21) {}
int j{500100};
};
class obj2_t : public ref_counter<obj2_t, ref_counter_thread_unsafe_policy<>> {
public:
obj2_t() {
++ctor_counter;
}
obj2_t(int ni) : i(ni) {
++ctor_counter;
}
virtual ~obj2_t() noexcept {
++dtor_counter;
}
int i{42};
public:
static int ctor_counter;
static int dtor_counter;
static void reset_counters() {
ctor_counter = 0u;
dtor_counter = 0u;
}
};
int obj2_t::ctor_counter = 0;
int obj2_t::dtor_counter = 0;
}
TEST_CASE("refcount") {
{
intrusive_ptr<obj_t> p;
REQUIRE_FALSE(p);
REQUIRE(p.get() == nullptr);
REQUIRE(p.release() == nullptr);
}
{
obj_t::reset_counters();
intrusive_ptr<obj_t> p(new obj_t());
REQUIRE(p);
REQUIRE(p.get());
REQUIRE(p.get()->i == 42);
REQUIRE(p->i == 42);
REQUIRE((*p).i == 42);
REQUIRE(p->use_count() == 1u);
REQUIRE(obj_t::ctor_counter == 1u);
REQUIRE(obj_t::dtor_counter == 0);
p.reset();
REQUIRE_FALSE(p);
REQUIRE(p.get() == nullptr);
REQUIRE(p.release() == nullptr);
REQUIRE(obj_t::dtor_counter == 1u);
}
{
obj_t::reset_counters();
intrusive_ptr<obj_t> p(new obj_t());
obj_t* o = p.release();
REQUIRE_FALSE(p);
REQUIRE(p.get() == nullptr);
REQUIRE(obj_t::ctor_counter == 1u);
REQUIRE(obj_t::dtor_counter == 0u);
REQUIRE(o->use_count() == 1u);
{
intrusive_ptr<obj_t> p2(o, false);
REQUIRE(p2);
REQUIRE(p2->use_count() == 1u);
o = p2.release();
REQUIRE_FALSE(p2);
p2.reset(o, false);
REQUIRE(p2);
REQUIRE(p2->use_count() == 1u);
REQUIRE(obj_t::ctor_counter == 1u);
REQUIRE(obj_t::dtor_counter == 0u);
p2.reset(new obj_t());
REQUIRE(p2);
REQUIRE(obj_t::ctor_counter == 2u);
REQUIRE(obj_t::dtor_counter == 1u);
}
REQUIRE(obj_t::ctor_counter == 2u);
REQUIRE(obj_t::dtor_counter == 2u);
}
{
obj_t::reset_counters();
{
intrusive_ptr<obj_t> p1(new obj_t(10));
intrusive_ptr<obj_t> p2(new obj_t(20));
p1.swap(p2);
REQUIRE(p1->i == 20);
REQUIRE(p2->i == 10);
}
REQUIRE(obj_t::ctor_counter == 2u);
REQUIRE(obj_t::dtor_counter == 2u);
}
{
intrusive_ptr<obj_t> p1(new obj_t(10));
intrusive_ptr<obj_t> p2(new obj_t(20));
{
intrusive_ptr<obj_t> p3;
REQUIRE_FALSE(p2 == nullptr);
REQUIRE_FALSE(nullptr == p2);
REQUIRE(p2 != nullptr);
REQUIRE(nullptr != p2);
REQUIRE(p3 == nullptr);
REQUIRE(nullptr == p3);
REQUIRE_FALSE(p3 != nullptr);
REQUIRE_FALSE(nullptr != p3);
}
{
REQUIRE((p1 == p1) == (p1.get() == p1.get()));
REQUIRE((p1 != p1) == (p1.get() != p1.get()));
REQUIRE((p1 == p1.get()) == (p1.get() == p1.get()));
REQUIRE((p1 != p1.get()) == (p1.get() != p1.get()));
REQUIRE((p1.get() == p1) == (p1.get() == p1.get()));
REQUIRE((p1.get() != p1) == (p1.get() != p1.get()));
REQUIRE((p1 < p1) == p1.get() < p1.get());
REQUIRE((p1 <= p1) == p1.get() <= p1.get());
REQUIRE((p1 > p1) == p1.get() > p1.get());
REQUIRE((p1 >= p1) == p1.get() >= p1.get());
}
{
REQUIRE((p2 == p1) == (p2.get() == p1.get()));
REQUIRE((p2 != p1) == (p2.get() != p1.get()));
REQUIRE((p2 == p1.get()) == (p2.get() == p1.get()));
REQUIRE((p2 != p1.get()) == (p2.get() != p1.get()));
REQUIRE((p2.get() == p1) == (p2.get() == p1.get()));
REQUIRE((p2.get() != p1) == (p2.get() != p1.get()));
REQUIRE((p2 < p1) == p2.get() < p1.get());
REQUIRE((p2 <= p1) == p2.get() <= p1.get());
REQUIRE((p2 > p1) == p2.get() > p1.get());
REQUIRE((p2 >= p1) == p2.get() >= p1.get());
}
}
{
obj_t::reset_counters();
{
intrusive_ptr<obj_t> p1(new derived_t());
REQUIRE(p1->i == 84);
intrusive_ptr<obj_t> p2 = p1;
REQUIRE(p2->i == 84);
REQUIRE(p2->use_count() == 2u);
}
REQUIRE(obj_t::ctor_counter == 1u);
REQUIRE(obj_t::dtor_counter == 1u);
{
intrusive_ptr<obj_t> p1(new derived_t());
intrusive_ptr<obj_t> p2;
p2 = p1;
REQUIRE(p2->i == 84);
}
REQUIRE(obj_t::ctor_counter == 2u);
REQUIRE(obj_t::dtor_counter == 2u);
{
intrusive_ptr<obj_t> p1(new derived_t());
intrusive_ptr<obj_t> p2 = std::move(p1);
REQUIRE(p2->i == 84);
REQUIRE_FALSE(p1);
}
REQUIRE(obj_t::ctor_counter == 3u);
REQUIRE(obj_t::dtor_counter == 3u);
{
intrusive_ptr<obj_t> p1(new derived_t());
intrusive_ptr<obj_t> p2;
p2 = std::move(p1);
REQUIRE(p2->i == 84);
REQUIRE_FALSE(p1);
}
REQUIRE(obj_t::ctor_counter == 4u);
REQUIRE(obj_t::dtor_counter == 4u);
}
{
std::make_unique<obj_t>(10);
intrusive_ptr<const obj_t> p1(new derived2_t());
intrusive_ptr<const derived2_t> p2 = static_pointer_cast<const derived2_t>(p1);
REQUIRE(p2->i == 21);
REQUIRE(p2->j == 500100);
intrusive_ptr<const derived2_t> p3 = dynamic_pointer_cast<const derived2_t>(p1);
REQUIRE(p3->i == 21);
REQUIRE(p3->j == 500100);
intrusive_ptr<const derived_t> p4 = dynamic_pointer_cast<const derived_t>(p1);
REQUIRE_FALSE(p4);
intrusive_ptr<obj_t> p5 = const_pointer_cast<obj_t>(p1);
REQUIRE(p5);
REQUIRE(p5->i == 21);
}
{
auto p1 = make_intrusive<obj_t>(10);
std::unordered_set<intrusive_ptr<obj_t>> s;
s.insert(p1);
}
{
auto p = make_intrusive<obj2_t>(10);
REQUIRE(p->i == 10);
REQUIRE(p->use_count() == 1u);
p.reset();
REQUIRE(obj2_t::ctor_counter == 1u);
REQUIRE(obj2_t::dtor_counter == 1u);
}
}