[DEV] add refPtr and RefCounter object to manage refconted element (somtimes better than sharedPtr: smaller, faster, less segmentation)

This commit is contained in:
Edouard DUPIN 2018-07-20 21:13:31 +02:00
parent 4bc9a2f27b
commit 2a211653e0
10 changed files with 810 additions and 61 deletions

37
ememory/RefCounter.cpp Normal file
View File

@ -0,0 +1,37 @@
/** @file
* @author Edouard DUPIN
* @copyright 2016, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file)
*/
#include <ememory/RefCounter.hpp>
#include <ememory/debug.hpp>
ememory::RefCounter::~RefCounter() {
if (m_refCount != 0) {
EMEMORY_ERROR("delete a RefCounted element that is keep by somewhere !! " << m_refCount);
}
}
void ememory::RefCounter::refKeep() {
m_refCount++;
}
void ememory::RefCounter::refRelease() {
int refCount = --m_refCount;
if (refCount == 0) {
// No more element ==> remove it.
this->~RefCounter();
ETK_DELETE(ememory::RefCounter, this);
// NOTE: Do nothing more than this ==> it will not work
return;
}
if (refCount < 0) {
EMEMORY_ERROR("request release a refcounted One more time than needed !! " << m_refCount);
m_refCount = 0;
}
}
int ememory::RefCounter::getRefCount() const {
return m_refCount;
}

49
ememory/RefCounter.hpp Normal file
View File

@ -0,0 +1,49 @@
/** @file
* @author Edouard DUPIN
* @copyright 2016, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file)
*/
#pragma once
#include <etk/types.hpp>
#include <ememory/RefPtr.hpp>
namespace ememory {
/**
* @brief Use the element as a refcounted element
*/
class RefCounter {
protected:
// Virtualize destructor in private to prevent user ot remove it without permition
virtual ~RefCounter();
private:
int32_t m_refCount = 1;
public:
/**
* @brief Keep a copy of this reference-counted element.
*/
void refKeep();
/**
* @brief Relese a copy of this reference-counted element.
*/
void refRelease();
/**
* @brief Get the number of time the object is required.
*/
int32_t getRefCount() const;
/**
* @brief Get the currect class SharedPtr
* @return Request SharedPtr
*/
//ememory::RefPtr<EMEMORY_TYPE> refFromThis();
/**
* @brief Get the currect class SharedPtr
* @return Request const SharedPtr
*/
//const ememory::RefPtr<EMEMORY_TYPE> refFromThis() const;
};
}
#include <ememory/details/EnableSharedFromThis.hxx>

167
ememory/RefPtr.hpp Normal file
View File

@ -0,0 +1,167 @@
/** @file
* @author Edouard DUPIN
* @copyright 2016, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file)
*/
#pragma once
#include <ememory/debug.hpp>
#include <ememory/RefCounter.hpp>
namespace ememory {
class RefCounter;
/**
* @brief ememory::Ref is a smart pointer on RefCounted element.
* this is the good way to use a RefCounter object to not missed to remove one of them.
*
* @note this is smaller than SharedPtr in memory impact and code done, then it is faster.
*/
template<typename EMEMORY_TYPE>
class RefPtr {
private:
EMEMORY_TYPE* m_element = null; //!< Pointer on the Data
public:
#ifndef PARSE_DOXYGEN
template<class EMEMORY_TYPE2,
typename etk::EnableIf< etk::IsSame<EMEMORY_TYPE2, EMEMORY_TYPE>::value
&& etk::IsBaseOf<ememory::RefCounter, EMEMORY_TYPE2>::value
, int>::type = 0>
RefPtr(EMEMORY_TYPE2* _element);
#else
/**
* @brief Contructor whith the pointer of data
* @param[in] _element allocated data (RefPtr will remove it)
*/
RefPtr(EMEMORY_TYPE2* _element);
#endif
public:
/**
* @brief Contructor on null
*/
RefPtr(etk::NullPtr);
/**
* @brief Contructor empty
*/
RefPtr();
/**
* @brief creator (to use wher create a new object (using nuw or ...)
* @param[in] _obj Pointer on the Data
*/
static inline RefPtr create(EMEMORY_TYPE* _obj);
/**
* @brief Contructor (API for casting)
* @param[in] _obj Pointer on the Data
*/
RefPtr(EMEMORY_TYPE* _obj);
/**
* @brief copy Contructor
* @param[in] _obj RefPtr to copy
*/
RefPtr(const RefPtr<EMEMORY_TYPE>& _obj);
/**
* @brief copy Contructor
* @param[in] _obj RefPtr to copy
*/
RefPtr(RefPtr<EMEMORY_TYPE>&& _obj);
/**
* @brief Destructor
*/
~RefPtr();
/**
* @brief Asignement operator
* @param[in] _obj RefPtr to copy
* @return Reference on this
*/
RefPtr& operator= (const RefPtr<EMEMORY_TYPE>& _obj);
/**
* @brief Asignement operator (asign null)
* @return Reference on this
*/
RefPtr& operator= (etk::NullPtr);
public:
#ifndef PARSE_DOXYGEN
template<class EMEMORY_TYPE2,
typename etk::EnableIf< etk::IsBaseOf<EMEMORY_TYPE, EMEMORY_TYPE2>::value
, int>::type = 0>
RefPtr(const RefPtr<EMEMORY_TYPE2>& _obj);
template<class EMEMORY_TYPE2,
typename etk::EnableIf< etk::IsBaseOf<EMEMORY_TYPE, EMEMORY_TYPE2>::value
, int>::type = 0>
RefPtr& operator= (const RefPtr<EMEMORY_TYPE2>& _obj);
#endif
public:
/**
* @brief Reset the RefPtr ==> Remove data if needed
*/
void reset();
/**
* @brief Get the number of conencted RefPtr
* @return Number of RefPtr on this data
*/
int useCount() const;
/**
* @brief Check if the RefPtr have an internal data (not null)
* @return true The pointer is not asigned, false otherwise
*/
bool operator==(etk::NullPtr) const;
/**
* @brief Check if two RefPtr are the same data (maybe not the same cast)
* @param[in] _obj Object to compare
* @return true The Object have the same pointer reference, false otherwise
*/
template<class EMEMORY_TYPE2>
bool operator==(const RefPtr<EMEMORY_TYPE2>& _obj) const;
/**
* @brief Check if the RefPtr have NOT an internal data (null)
* @return true The pointer is asigned, false otherwise
*/
bool operator!=(etk::NullPtr) const;
/**
* @brief Check if two RefPtr are NOT the same data (maybe not the same cast)
* @param[in] _obj Object to compare
* @return true The Object have NOT the same pointer reference, false otherwise
*/
template<class EMEMORY_TYPE2>
bool operator!=(const RefPtr<EMEMORY_TYPE2>& _obj) const;
/**
* @brief Get a const pointer on the data
* @return Data const pointer
*/
const EMEMORY_TYPE* get() const;
/**
* @brief Get a pointer on the data
* @return Data pointer
*/
EMEMORY_TYPE* get();
/**
* @brief Const dereferences the stored pointer.
* @return Const pointer on the Data
*/
const EMEMORY_TYPE* operator->() const;
/**
* @brief Dereferences the stored pointer.
* @return Pointer on the Data
*/
EMEMORY_TYPE* operator->();
/**
* @brief Get a const reference on the data
* @return Data const reference
*/
const EMEMORY_TYPE& operator*() const;
/**
* @brief Get a reference on the data
* @return Data reference
*/
EMEMORY_TYPE& operator*();
/**
* @brief Swap 2 Object inside the RefPtr
* @param[in] _obj Object to swap with
*/
void swap(RefPtr<EMEMORY_TYPE>& _obj);
// TODO: unique
// TODO: bool
};
}
#include <ememory/details/RefPtr.hxx>

View File

@ -1,4 +1,8 @@
/** @file
* @author Edouard DUPIN
* @copyright 2016, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file)
*/
#pragma once
#include <etk/types.hpp>

200
ememory/details/RefPtr.hxx Normal file
View File

@ -0,0 +1,200 @@
/** @file
* @author Edouard DUPIN
* @copyright 2016, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file)
*/
#pragma once
#include <ememory/debug.hpp>
#include <ememory/RefPtr.hpp>
template<typename EMEMORY_TYPE>
template<class EMEMORY_TYPE2,
typename etk::EnableIf< etk::IsSame<EMEMORY_TYPE2, EMEMORY_TYPE>::value
&& etk::IsBaseOf<ememory::RefCounter, EMEMORY_TYPE2>::value
, int>::type>
ememory::RefPtr<EMEMORY_TYPE>::RefPtr(EMEMORY_TYPE2* _element):
m_element(_element) {
EMEMORY_DBG("Create shared");
if (m_element == null) {
return;
}
EMEMORY_DBG("Check if the user use the memory allocator or personal system...");
ETK_MEM_CHECK_POINTER(_element);
EMEMORY_DBG(" ==> get previous pointer");
m_element->refKeep();
return;
}
template<typename EMEMORY_TYPE>
ememory::RefPtr<EMEMORY_TYPE> ememory::RefPtr<EMEMORY_TYPE>::create(EMEMORY_TYPE* _obj) {
EMEMORY_DBG("Create shared");
ememory::RefPtr<EMEMORY_TYPE> tmp{_obj};
_obj->refRelease();
return tmp;
}
template<typename EMEMORY_TYPE>
ememory::RefPtr<EMEMORY_TYPE>::RefPtr():
m_element(null) {
EMEMORY_DBG("Create shared");
}
template<typename EMEMORY_TYPE>
ememory::RefPtr<EMEMORY_TYPE>::RefPtr(etk::NullPtr):
m_element(null) {
EMEMORY_DBG("Create shared");
}
template<typename EMEMORY_TYPE>
ememory::RefPtr<EMEMORY_TYPE>::RefPtr(EMEMORY_TYPE* _obj):
m_element(_obj) {
EMEMORY_DBG("Create shared (from a cast)");
if (m_element == null) {
return;
}
m_element->refKeep();
}
template<typename EMEMORY_TYPE>
ememory::RefPtr<EMEMORY_TYPE>::~RefPtr() {
EMEMORY_DBG("delete shared");
reset();
}
template<typename EMEMORY_TYPE>
ememory::RefPtr<EMEMORY_TYPE>::RefPtr(const ememory::RefPtr<EMEMORY_TYPE>& _obj):
m_element(_obj.m_element) {
if (m_element == null) {
return;
}
m_element->refKeep();
};
template<typename EMEMORY_TYPE>
ememory::RefPtr<EMEMORY_TYPE>& ememory::RefPtr<EMEMORY_TYPE>::operator= (const ememory::RefPtr<EMEMORY_TYPE>& _obj) {
reset();
m_element = _obj.m_element;
if (m_element == null) {
return *this;
}
m_element->refKeep();
return *this;
}
template<typename EMEMORY_TYPE>
ememory::RefPtr<EMEMORY_TYPE>& ememory::RefPtr<EMEMORY_TYPE>::operator= (etk::NullPtr) {
reset();
return *this;
}
template<typename EMEMORY_TYPE>
ememory::RefPtr<EMEMORY_TYPE>::RefPtr(ememory::RefPtr<EMEMORY_TYPE>&& _obj) {
m_element = _obj.m_element;
_obj.m_element = null;
}
template<typename EMEMORY_TYPE>
template<class EMEMORY_TYPE2,
typename etk::EnableIf< etk::IsBaseOf<EMEMORY_TYPE, EMEMORY_TYPE2>::value
, int>::type>
ememory::RefPtr<EMEMORY_TYPE>::RefPtr(const ememory::RefPtr<EMEMORY_TYPE2>& _obj):
m_element(const_cast<EMEMORY_TYPE2*>(_obj.get())) {
if (m_element == null) {
return;
}
m_element->refKeep();
}
template<typename EMEMORY_TYPE>
template<class EMEMORY_TYPE2,
typename etk::EnableIf< etk::IsBaseOf<EMEMORY_TYPE, EMEMORY_TYPE2>::value
, int>::type>
ememory::RefPtr<EMEMORY_TYPE>& ememory::RefPtr<EMEMORY_TYPE>::operator= (const RefPtr<EMEMORY_TYPE2>& _obj) {
reset();
m_element = const_cast<EMEMORY_TYPE2*>(_obj.get());
if (m_element == null) {
return *this;
}
m_element->refKeep();
return *this;
}
template<typename EMEMORY_TYPE>
void ememory::RefPtr<EMEMORY_TYPE>::reset() {
if(m_element == null) {
return;
}
EMEMORY_DBG("reset RefPtr (start)");
m_element->refRelease();
m_element = null;
EMEMORY_DBG("reset RefPtr (stop)");
}
template<typename EMEMORY_TYPE>
int ememory::RefPtr<EMEMORY_TYPE>::useCount() const {
if (m_element == null) {
return 0;
}
return m_element->getRefCount();
}
template<typename EMEMORY_TYPE>
bool ememory::RefPtr<EMEMORY_TYPE>::operator==(etk::NullPtr) const {
return m_element == null;
}
template<typename EMEMORY_TYPE>
template<class EMEMORY_TYPE2>
bool ememory::RefPtr<EMEMORY_TYPE>::operator==(const RefPtr<EMEMORY_TYPE2>& _obj) const {
return m_element == _obj.get();
}
template<typename EMEMORY_TYPE>
bool ememory::RefPtr<EMEMORY_TYPE>::operator!=(etk::NullPtr) const {
return m_element != null;
}
template<typename EMEMORY_TYPE>
template<typename EMEMORY_TYPE2>
bool ememory::RefPtr<EMEMORY_TYPE>::operator!=(const RefPtr<EMEMORY_TYPE2>& _obj) const {
return m_element != _obj.get();
}
template<typename EMEMORY_TYPE>
const EMEMORY_TYPE* ememory::RefPtr<EMEMORY_TYPE>::get() const {
return m_element;
}
template<typename EMEMORY_TYPE>
EMEMORY_TYPE* ememory::RefPtr<EMEMORY_TYPE>::get() {
return m_element;
}
template<typename EMEMORY_TYPE>
const EMEMORY_TYPE* ememory::RefPtr<EMEMORY_TYPE>::operator->() const {
return m_element;
}
template<typename EMEMORY_TYPE>
EMEMORY_TYPE* ememory::RefPtr<EMEMORY_TYPE>::operator->() {
return m_element;
}
template<typename EMEMORY_TYPE>
const EMEMORY_TYPE& ememory::RefPtr<EMEMORY_TYPE>::operator*() const {
return *m_element;
}
template<typename EMEMORY_TYPE>
EMEMORY_TYPE& ememory::RefPtr<EMEMORY_TYPE>::operator*() {
return *m_element;
}
template<typename EMEMORY_TYPE>
void ememory::RefPtr<EMEMORY_TYPE>::swap(RefPtr& _obj) {
etk::swap(_obj.m_element, m_element);
}

View File

@ -8,6 +8,7 @@
#include <etk/Vector.hpp>
#include <ethread/Mutex.hpp>
#include <ememory/SharedPtr.hpp>
#include <ememory/RefPtr.hpp>
#include <ememory/WeakPtr.hpp>
#include <ememory/EnableSharedFromThis.hpp>
#include <etk/Allocator.hpp>
@ -88,4 +89,81 @@ namespace ememory {
inline ememory::SharedPtr<EMEMORY_TYPE_CAST> constPointerCast(const ememory::SharedPtr<EMEMORY_TYPE>& _obj) {
return ememory::SharedPtr<EMEMORY_TYPE_CAST>(const_cast<EMEMORY_TYPE*>(_obj.get()), _obj.getCounter());
}
/**
* @brief Create a RefPtr with specific arguments
* @param[in] _args Multiples argument to add in the EMEMORY_TYPE public constructor.
* @return the requested created RefPtr
*/
template<class EMEMORY_TYPE,
typename etk::EnableIf< etk::IsBaseOf<ememory::RefCounter, EMEMORY_TYPE>::value
, int>::type = 0,
typename... EMEMORY_ARGS>
static ememory::RefPtr<EMEMORY_TYPE> makeRef(EMEMORY_ARGS && ..._args) {
return ememory::RefPtr<EMEMORY_TYPE>::create(ETK_NEW(EMEMORY_TYPE, etk::forward<EMEMORY_ARGS>(_args)...));
}
/**
* @brief Cast in Dynamic the input RefPtr into an other type like dynamic_cast on pointer
* @param[in] _obj Object To cast
* @return Casted Object
*/
template<class EMEMORY_TYPE_CAST, class EMEMORY_TYPE>
inline ememory::RefPtr<EMEMORY_TYPE_CAST> dynamicRefCast(ememory::RefPtr<EMEMORY_TYPE>& _obj) {
return ememory::RefPtr<EMEMORY_TYPE_CAST>(dynamic_cast<EMEMORY_TYPE_CAST*>(_obj.get()));
}
/**
* @brief CONST Cast in Dynamic the input RefPtr into an other type like dynamic_cast on pointer
* @param[in] _obj Object To cast
* @return Casted Object
*/
template<class EMEMORY_TYPE_CAST, class EMEMORY_TYPE>
inline const ememory::RefPtr<EMEMORY_TYPE_CAST> dynamicRefCast(const ememory::RefPtr<EMEMORY_TYPE>& _obj) {
return ememory::RefPtr<EMEMORY_TYPE_CAST>(dynamic_cast<EMEMORY_TYPE_CAST*>(const_cast<EMEMORY_TYPE*>(_obj.get())));
}
/**
* @brief Cast in static the input RefPtr into an other type like static_cast on pointer
* @param[in] _obj Object To cast
* @return Casted Object
*/
template<class EMEMORY_TYPE_CAST, class EMEMORY_TYPE>
inline ememory::RefPtr<EMEMORY_TYPE_CAST> staticRefCast(ememory::RefPtr<EMEMORY_TYPE>& _obj) {
return ememory::RefPtr<EMEMORY_TYPE_CAST>(static_cast<EMEMORY_TYPE_CAST*>(_obj.get()));
}
/**
* @brief CONST Cast in static the input RefPtr into an other type like static_cast on pointer
* @param[in] _obj Object To cast
* @return Casted Object
*/
template<class EMEMORY_TYPE_CAST, class EMEMORY_TYPE>
inline const ememory::RefPtr<EMEMORY_TYPE_CAST> staticRefCast(const ememory::RefPtr<EMEMORY_TYPE>& _obj) {
return ememory::RefPtr<EMEMORY_TYPE_CAST>(static_cast<EMEMORY_TYPE_CAST*>(const_cast<EMEMORY_TYPE*>(_obj.get())));
}
/**
* @brief Cast in reinterpret the input RefPtr into an other type like reinterpret_cast on pointer
* @param[in] _obj Object To cast
* @return Casted Object
*/
template<class EMEMORY_TYPE_CAST, class EMEMORY_TYPE>
inline ememory::RefPtr<EMEMORY_TYPE_CAST> reinterpretRefCast(ememory::RefPtr<EMEMORY_TYPE>& _obj) {
return ememory::RefPtr<EMEMORY_TYPE_CAST>(reinterpret_cast<EMEMORY_TYPE_CAST*>(_obj.get()));
}
/**
* @brief CONST Cast in reinterpret the input RefPtr into an other type like reinterpret_cast on pointer
* @param[in] _obj Object To cast
* @return Casted Object
*/
template<class EMEMORY_TYPE_CAST, class EMEMORY_TYPE>
inline const ememory::RefPtr<EMEMORY_TYPE_CAST> reinterpretRefCast(const ememory::RefPtr<EMEMORY_TYPE>& _obj) {
return ememory::RefPtr<EMEMORY_TYPE_CAST>(reinterpret_cast<EMEMORY_TYPE_CAST*>(const_cast<EMEMORY_TYPE*>(_obj.get())));
}
/**
* @brief Cast in const the input RefPtr into an other type like const_cast on pointer (remove constness)
* @param[in] _obj Object To cast
* @return Casted Object
*/
template<class EMEMORY_TYPE_CAST, class EMEMORY_TYPE>
inline ememory::RefPtr<EMEMORY_TYPE_CAST> constRefCast(const ememory::RefPtr<EMEMORY_TYPE>& _obj) {
return ememory::RefPtr<EMEMORY_TYPE_CAST>(const_cast<EMEMORY_TYPE*>(_obj.get()));
}
}

View File

@ -28,6 +28,7 @@ def configure(target, my_module):
my_module.add_src_file([
'test/main.cpp',
'test/testUnique.cpp',
'test/testRef.cpp',
'test/testShared.cpp',
'test/testWeak.cpp',
'test/testEnableSharedFromThis.cpp',

View File

@ -29,21 +29,25 @@ def configure(target, my_module):
# add the file to compile:
my_module.add_src_file([
'ememory/debug.cpp',
'ememory/Counter.cpp'
'ememory/Counter.cpp',
'ememory/RefCounter.cpp',
])
my_module.add_header_file([
'ememory/debug.hpp',
'ememory/memory.hpp',
'ememory/Counter.hpp',
'ememory/RefCounter.hpp',
'ememory/RefPtr.hpp',
'ememory/SharedPtr.hpp',
'ememory/UniquePtr.hpp',
'ememory/WeakPtr.hpp',
'ememory/EnableSharedFromThis.hpp',
'ememory/details/memory.hxx',
'ememory/details/SharedPtr.hxx',
'ememory/details/RefPtr.hxx',
'ememory/details/WeakPtr.hxx',
'ememory/details/EnableSharedFromThis.hxx'
'ememory/details/EnableSharedFromThis.hxx',
])
# build in C++ mode

208
test/testRef.cpp Normal file
View File

@ -0,0 +1,208 @@
/** @file
* @author Edouard DUPIN
* @copyright 2016, Edouard DUPIN, all right reserved
* @license MPL v2.0 (see license file)
*/
#include <etest/etest.hpp>
#include <ememory/memory.hpp>
#include <test-debug/debug.hpp>
#include "main.hpp"
namespace {
class MyTestString:
public ememory::RefCounter,
public etk::String {
public:
MyTestString() = default;
MyTestString(const etk::String& _value):
etk::String(_value) {
}
MyTestString& operator = (const etk::String& _value) {
etk::String::operator=(_value);
return *this;
}
};
}
TEST(TestRef, createAndDestroy) {
ememory::RefPtr<MyTestString> data = ememory::makeRef<MyTestString>("coucou");
EXPECT_EQ(data.useCount(), 1);
EXPECT_EQ(*data, "coucou");
EXPECT_EQ(data == null, false);
EXPECT_EQ(data != null, true);
data.reset();
EXPECT_EQ(data.useCount(), 0);
EXPECT_EQ(data == null, true);
EXPECT_EQ(data != null, false);
}
TEST(TestRef, createAndDestroy_2) {
ememory::RefPtr<MyTestString> data = ememory::RefPtr<MyTestString>::create(ETK_NEW(MyTestString, "coucou"));
EXPECT_EQ(data.useCount(), 1);
EXPECT_EQ(*data, "coucou");
EXPECT_EQ(data == null, false);
EXPECT_EQ(data != null, true);
data.reset();
EXPECT_EQ(data.useCount(), 0);
EXPECT_EQ(data == null, true);
EXPECT_EQ(data != null, false);
}
TEST(TestRef, createAndDestroy_3) {
ememory::RefPtr<MyTestString> data = ememory::RefPtr<MyTestString>::create(new MyTestString("coucou"));
EXPECT_EQ(data.useCount(), 1);
EXPECT_EQ(*data, "coucou");
EXPECT_EQ(data == null, false);
EXPECT_EQ(data != null, true);
data.reset();
EXPECT_EQ(data.useCount(), 0);
EXPECT_EQ(data == null, true);
EXPECT_EQ(data != null, false);
}
TEST(TestRef, copy) {
ememory::RefPtr<MyTestString> data = ememory::makeRef<MyTestString>("coucou");
EXPECT_EQ(data.useCount(), 1);
EXPECT_EQ(*data, "coucou");
ememory::RefPtr<MyTestString> dataCopy = data;
EXPECT_EQ(data.useCount(), 2);
EXPECT_EQ(data == dataCopy, true);
EXPECT_EQ(data != dataCopy, false);
data.reset();
EXPECT_EQ(data == dataCopy, false);
EXPECT_EQ(data != dataCopy, true);
EXPECT_EQ(data.useCount(), 0);
EXPECT_EQ(dataCopy.useCount(), 1);
EXPECT_EQ(*dataCopy, "coucou");
dataCopy.reset();
EXPECT_EQ(dataCopy.useCount(), 0);
dataCopy.reset();
EXPECT_EQ(dataCopy.useCount(), 0);
}
TEST(TestRef, swap) {
ememory::RefPtr<MyTestString> data = ememory::makeRef<MyTestString>("coucou");
EXPECT_EQ(data.useCount(), 1);
EXPECT_EQ(*data, "coucou");
ememory::RefPtr<MyTestString> dataCopy;
EXPECT_EQ(dataCopy.useCount(), 0);
dataCopy.swap(data);
EXPECT_EQ(data.useCount(), 0);
EXPECT_EQ(dataCopy.useCount(), 1);
EXPECT_EQ(*dataCopy, "coucou");
}
TEST(TestRef, callOperator) {
ememory::RefPtr<MyTestString> data = ememory::makeRef<MyTestString>("coucou");
EXPECT_EQ(data->size(), 6);
}
static void functionCallRef(etk::String& _data) {
_data = "plop";
}
TEST(TestRef, callOperatorStar) {
ememory::RefPtr<MyTestString> data = ememory::makeRef<MyTestString>("coucou");
EXPECT_EQ(data->size(), 6);
EXPECT_EQ(*data, "coucou");
*data = "ragout";
EXPECT_EQ(data->size(), 6);
EXPECT_EQ(*data, "ragout");
functionCallRef(*data);
EXPECT_EQ(data->size(), 4);
EXPECT_EQ(*data, "plop");
}
namespace {
class basicClass : public ememory::RefCounter {
};
class heritedClass : public basicClass {
};
}
TEST(TestRef, heritage) {
ememory::RefPtr<heritedClass> data = ememory::makeRef<heritedClass>();
ememory::RefPtr<basicClass> data2 = data;
ememory::RefPtr<basicClass> data3(data);
EXPECT_NE(data, null);
EXPECT_NE(data2, null);
EXPECT_NE(data3, null);
}
static uint32_t isDestroy = 0;
namespace {
class testContructDestruct : public ememory::RefCounter {
private:
uint32_t m_addValue;
public:
testContructDestruct(uint32_t _addValue):
m_addValue(_addValue) {
isDestroy += m_addValue;
TEST_DEBUG("Create class " << m_addValue);
}
testContructDestruct(testContructDestruct&& _obj):
m_addValue(_obj.m_addValue) {
_obj.m_addValue = 0;
TEST_DEBUG("move contruction " << m_addValue);
}
virtual ~testContructDestruct() {
if (m_addValue == 0) {
TEST_DEBUG("Remove class (after move)");
return;
}
TEST_DEBUG("Remove Class " << m_addValue);
isDestroy -= m_addValue;
}
testContructDestruct& operator= (testContructDestruct&& _obj) {
TEST_DEBUG("move operator " << m_addValue);
if (this != &_obj) {
etk::swap(m_addValue, _obj.m_addValue);
}
return *this;
}
};
}
TEST(TestRef, destroyElementAtTheCorectMoment) {
isDestroy = 0;
{
etk::Vector<ememory::RefPtr<testContructDestruct>> list;
list.pushBack(ememory::makeRef<testContructDestruct>(55));
EXPECT_EQ(list.size(), 1);
EXPECT_EQ(isDestroy, 55);
auto it = list.erase(list.begin());
EXPECT_EQ(isDestroy, 0);
EXPECT_EQ(list.size(), 0);
EXPECT_EQ(it, list.end());
}
EXPECT_EQ(isDestroy, 0);
}
TEST(TestRef, destroyElementAtTheCorectMoment_2) {
isDestroy = 0;
{
etk::Vector<ememory::RefPtr<testContructDestruct>> list;
list.pushBack(ememory::makeRef<testContructDestruct>(4));
list.pushBack(ememory::makeRef<testContructDestruct>(30));
list.pushBack(ememory::makeRef<testContructDestruct>(1000));
list.pushBack(ememory::makeRef<testContructDestruct>(200));
EXPECT_EQ(list.size(), 4);
EXPECT_EQ(isDestroy, 1234);
auto it = list.erase(list.begin());
EXPECT_EQ(list.size(), 3);
EXPECT_EQ(isDestroy, 1230);
it = list.erase(list.begin()+1);
EXPECT_EQ(isDestroy, 230);
EXPECT_EQ(list.size(), 2);
}
EXPECT_EQ(isDestroy, 0);
}

View File

@ -128,68 +128,69 @@ TEST(TestShared, heritage) {
static uint32_t isDestroy = 0;
class testContructDestruct {
private:
uint32_t m_addValue;
public:
testContructDestruct(uint32_t _addValue):
m_addValue(_addValue) {
isDestroy += m_addValue;
TEST_DEBUG("Create class " << m_addValue);
}
testContructDestruct(testContructDestruct&& _obj):
m_addValue(_obj.m_addValue) {
_obj.m_addValue = 0;
TEST_DEBUG("move contruction " << m_addValue);
}
virtual ~testContructDestruct() {
if (m_addValue == 0) {
TEST_DEBUG("Remove class (after move)");
return;
namespace {
class testContructDestruct {
private:
uint32_t m_addValue;
public:
testContructDestruct(uint32_t _addValue):
m_addValue(_addValue) {
isDestroy += m_addValue;
TEST_DEBUG("Create class " << m_addValue);
}
TEST_DEBUG("Remove Class " << m_addValue);
isDestroy -= m_addValue;
}
testContructDestruct& operator= (testContructDestruct&& _obj) {
TEST_DEBUG("move operator " << m_addValue);
if (this != &_obj) {
etk::swap(m_addValue, _obj.m_addValue);
testContructDestruct(testContructDestruct&& _obj):
m_addValue(_obj.m_addValue) {
_obj.m_addValue = 0;
TEST_DEBUG("move contruction " << m_addValue);
}
return *this;
}
};
class testContructDestruct2 : public ememory::EnableSharedFromThis<testContructDestruct2> {
private:
uint32_t m_addValue;
public:
testContructDestruct2(uint32_t _addValue):
m_addValue(_addValue) {
isDestroy += m_addValue;
TEST_DEBUG("Create class " << m_addValue);
}
testContructDestruct2(testContructDestruct2&& _obj):
m_addValue(_obj.m_addValue) {
_obj.m_addValue = 0;
TEST_DEBUG("move contruction " << m_addValue);
}
virtual ~testContructDestruct2() {
if (m_addValue == 0) {
TEST_DEBUG("Remove class (after move)");
return;
virtual ~testContructDestruct() {
if (m_addValue == 0) {
TEST_DEBUG("Remove class (after move)");
return;
}
TEST_DEBUG("Remove Class " << m_addValue);
isDestroy -= m_addValue;
}
TEST_DEBUG("Remove Class " << m_addValue);
isDestroy -= m_addValue;
}
testContructDestruct2& operator= (testContructDestruct2&& _obj) {
TEST_DEBUG("move operator " << m_addValue);
if (this != &_obj) {
etk::swap(m_addValue, _obj.m_addValue);
testContructDestruct& operator= (testContructDestruct&& _obj) {
TEST_DEBUG("move operator " << m_addValue);
if (this != &_obj) {
etk::swap(m_addValue, _obj.m_addValue);
}
return *this;
}
return *this;
}
};
};
class testContructDestruct2 : public ememory::EnableSharedFromThis<testContructDestruct2> {
private:
uint32_t m_addValue;
public:
testContructDestruct2(uint32_t _addValue):
m_addValue(_addValue) {
isDestroy += m_addValue;
TEST_DEBUG("Create class " << m_addValue);
}
testContructDestruct2(testContructDestruct2&& _obj):
m_addValue(_obj.m_addValue) {
_obj.m_addValue = 0;
TEST_DEBUG("move contruction " << m_addValue);
}
virtual ~testContructDestruct2() {
if (m_addValue == 0) {
TEST_DEBUG("Remove class (after move)");
return;
}
TEST_DEBUG("Remove Class " << m_addValue);
isDestroy -= m_addValue;
}
testContructDestruct2& operator= (testContructDestruct2&& _obj) {
TEST_DEBUG("move operator " << m_addValue);
if (this != &_obj) {
etk::swap(m_addValue, _obj.m_addValue);
}
return *this;
}
};
}
TEST(TestShared, destroyElementAtTheCorectMoment) {
isDestroy = 0;