Commit f531a67e authored by Simon Morlat's avatar Simon Morlat

Simplify HybridObject, add documentation and new test cases.

There is no need to inherit from std::enable_shared_from_this; and this was causing problems as HybridObject may not always be contained in a shared_ptr (case where creation is triggered by C code).
Instead, since the refcounting is done by the HybridObject, it is easy to instanciate shared_ptr<> automatically calling unref(), at any time, when needed.
parent 5dc5f6a2
......@@ -42,6 +42,7 @@ class BELLESIP_EXPORT Object {
public:
Object();
Object *ref();
const Object *ref() const;
void unref();
//Overrides should keep the size of toString() lower than BELLE_SIP_MAX_TO_STRING_SIZE
virtual std::string toString() const;
......@@ -60,9 +61,10 @@ class BELLESIP_EXPORT Object {
protected:
virtual ~Object(); /*the destructor must be kept protected, never public, including for all classes inherting from this*/
Object(const Object &other);
void constUnref()const;
private:
void init();
belle_sip_cpp_object_t mObject;
mutable belle_sip_cpp_object_t mObject;
belle_sip_error_code marshal(char* buff, size_t buff_size, size_t *offset);
};
......@@ -79,66 +81,80 @@ class BELLESIP_EXPORT Object {
* }
* The C object can be obtained with toC() method, directly casted in the expected type.
* The C++ object can be obtained from C object with static method toCpp().
* The destructor must be kept protected so that no one can call delete operator on the object. Instead unref() must be used.
* The destructor MUST be kept protected so that no one can call delete operator on the object. Instead unref() must be used.
* make_shared<>() MUST NOT be used to instanciate an HybridObject, use create() instead.
* a shared_ptr<> can be obtained at any time from an HybridObject using getSharedFromThis().
* The static getSharedFromThis(_Ctype*) method can be used to directly obtain a shared_ptr from the C object.
*
* The clone() method must be overriden to return a new _CppType contructed with copy contructor,
* or return nullptr if the object is defined to be not clonable.
*
* Rational for using this template:
* - You have an existing library in C where all C objects are inheriting from belle_sip_object_t (for refcounting, data_set etc...).
* - You want to use C++ in your library without making any disruption in the API.
* If you don't care about belle_sip_object_t inheritance in your C api, don't use this.
* IMPORTANT:
* If you don't care about belle_sip_object_t inheritance in your C api,
* or if you don't need any C api associated to this object at all, DON'T USE THIS.
* An usage example is shown in tester/object_tester.cc .
**/
template <typename _CType, typename _CppType>
class HybridObject : public Object, public std::enable_shared_from_this<HybridObject<_CType, _CppType> > {
class HybridObject : public Object {
public:
//Ref is managed by shared_ptr, unref will be called on last ref.
//Create the C++ object returned as a shared_ptr. Ref is managed by shared_ptr, unref will be called on last ref.
template <typename... _Args>
static inline std::shared_ptr<_CppType> create(_Args&&... __args) {
return std::shared_ptr<_CppType>(new _CppType(std::forward<_Args>(__args)...), std::mem_fun(&Object::unref));
return (new _CppType(std::forward<_Args>(__args)...))->toSharedPtr();
}
//Convenience creator to get a C object. Automatically aquires a ref. Consumers have the responsibility to unref
//Convenience creator to get the C object object instead. Automatically aquires a ref. Consumers have the responsibility to unref
template <typename... _Args>
static inline _CType *createCObject(_Args&&... __args) {
_CppType *obj = new _CppType(std::forward<_Args>(__args)...);
obj->ref();
return obj->toC();
}
//Obtain the C object from this.
_CType *toC(){
return static_cast<_CType*>(getCPtr());
}
//Obtain the C object this, when it is const.
const _CType *toC()const{
return static_cast<const _CType*>(getCPtr());
}
//Obtain the C++ object as a normal pointer.
static _CppType *toCpp(_CType *ptr){
return static_cast<_CppType *>(getCppObject(ptr));
}
//Obtain the C++ object as a normal pointer, when the c++ object is const.
static const _CppType *toCpp(const _CType *ptr){
return static_cast<const _CppType *>(getCppObject(ptr));
}
std::shared_ptr<const _CppType> getSharedFromThis() const {
try {
return std::dynamic_pointer_cast<const _CppType>(this->shared_from_this());
} catch (const std::exception &up) {
belle_sip_error("getSharedFromThis() exception: Object not created with 'make_shared'. Error: [%s].", up.what());
return nullptr;
}
return nullptr;
}
std::shared_ptr<_CppType> getSharedFromThis () {
return std::const_pointer_cast<_CppType>(static_cast<const _CppType *>(this)->getSharedFromThis());
}
std::shared_ptr<_CppType> toSharedPtr() {
//Obtain a shared_ptr from the C++ object.
std::shared_ptr<_CppType> getSharedFromThis() {
this->ref();
return std::shared_ptr<_CppType>(static_cast<_CppType *>(this), std::mem_fun(&Object::unref));
}
std::shared_ptr<const _CppType> toSharedPtr() const {
return std::shared_ptr<const _CppType>(static_cast<const _CppType *>(this), std::mem_fun(&Object::unref));
//Obtain a shared_ptr from the C++ object in the const case.
std::shared_ptr<const _CppType> getSharedFromThis () const {
this->ref();
return std::shared_ptr<const _CppType>(static_cast<const _CppType *>(this), std::mem_fun(&Object::constUnref));
}
//Convenience method for easy CType -> shared_ptr<CppType> conversion
static std::shared_ptr<_CppType> toSharedPtr(const _CType *ptr) {
return toCpp(const_cast<_CType *>(ptr))->toSharedPtr();
static std::shared_ptr<_CppType> getSharedFromThis(_CType *ptr) {
return toCpp(ptr)->getSharedFromThis();
}
//Convenience method for easy CType -> shared_ptr<CppType> conversion
static std::shared_ptr<const _CppType> getSharedFromThis(const _CType *ptr) {
return toCpp(const_cast<_CType *>(ptr))->getSharedFromThis();
}
// Obtain a shared_ptr that takes ownership of the object.
// Use this method with caution as it will transfer the ownership of the object to the shared_ptr, unlike getSharedFromThis() that
// gives you a new reference to the object.
// This method should be only useful to transfer into a shared_ptr an object that was created as a normal pointer, for example
// by clone() method.
// There should be NO const variant of this method.
std::shared_ptr<_CppType> toSharedPtr(){
return std::shared_ptr<_CppType>(static_cast<_CppType *>(this), std::mem_fun(&Object::unref));
}
//Convenience method for easy bctbx_list(_Ctype) -> std::list<_CppType> conversion
static std::list<_CppType> getCppListFromCList(const bctbx_list_t *cList) {
std::list<_CppType> result;
......@@ -169,19 +185,10 @@ class HybridObject : public Object, public std::enable_shared_from_this<HybridOb
protected:
virtual ~HybridObject() {}
HybridObject() {}
HybridObject(const HybridObject<_CType, _CppType> &other) : Object(other), std::enable_shared_from_this<HybridObject<_CType, _CppType>> (other) {}
HybridObject(const HybridObject<_CType, _CppType> &other) : Object(other) {}
};
/**
* Convenience function to create a std::shared_ptr that calls Object::unref() instead of delete expression.
*/
template <typename _T, typename... _Args>
inline std::shared_ptr<_T> make_shared(_Args&&... __args) {
return std::shared_ptr<_T>(new _T(std::forward<_Args>(__args)...), std::mem_fun(&Object::unref));
}
}//end of namespace
extern "C" {
......
......@@ -65,10 +65,19 @@ Object *Object::ref(){
return this;
}
const Object *Object::ref() const{
belle_sip_object_ref(&mObject);
return this;
}
void Object::unref(){
belle_sip_object_unref(&mObject);
}
void Object::constUnref()const{
belle_sip_object_unref(&mObject);
}
std::string Object::toString() const {
std::ostringstream ss;
ss << "bellesip::Object. cObject(";
......
......@@ -87,7 +87,13 @@ class Event : public HybridObject<LinphoneEvent, Event> {
void doSomething(){
throw BctbxException("Unimplemented");
}
Event *clone()const{
return new Event(*this);
}
protected:
Event(const Event& orig) : HybridObject<LinphoneEvent, Event>(orig){
mState = orig.mState;
}
//~Event() = default; //we shouls have the destructor private but in order to test the delete exception
//we'll make it public.
private:
......@@ -140,21 +146,44 @@ static void dual_object(void){
BC_ASSERT_TRUE(object_destroyed);
}
static void dual_object_clone(void){
int object_destroyed = 0;
std::shared_ptr<Event> ev = Event::create();
std::shared_ptr<Event> cloned_ev = ev->clone()->toSharedPtr();
belle_sip_object_t *c_obj = cloned_ev->getCObject();
BC_ASSERT_PTR_NOT_NULL(c_obj);
if (c_obj){
belle_sip_object_weak_ref(c_obj, on_object_destroyed, &object_destroyed);
}
ev.reset();
BC_ASSERT_FALSE(object_destroyed); // the reset() shall not cause the cloned object to be destroyed of course.
cloned_ev.reset();
BC_ASSERT_TRUE(object_destroyed); // Now the object should be destroyed.
}
static void dual_object_shared_ptr(void){
int object_destroyed = 0;
std::shared_ptr<Event> ev = bellesip::make_shared<Event>();
std::shared_ptr<Event> ev = Event::create();
belle_sip_object_t *c_obj = ev->getCObject();
BC_ASSERT_PTR_NOT_NULL(c_obj);
if (c_obj){
belle_sip_object_weak_ref(c_obj, on_object_destroyed, &object_destroyed);
}
//Here we mix manual reference from C and shared_ptr.
belle_sip_object_ref(c_obj);
ev.reset();
BC_ASSERT_FALSE(object_destroyed); // the reset() shall not cause the object to be destroyed.
ev = Event::toCpp((LinphoneEvent*)c_obj)->getSharedFromThis(); // we get again a shared_ptr.
belle_sip_object_unref(c_obj);
BC_ASSERT_FALSE(object_destroyed); // the unref() shall not cause the object to be destroyed, since the shared_ptr has a reference to it.
ev.reset();
BC_ASSERT_TRUE(object_destroyed);
BC_ASSERT_TRUE(object_destroyed); // Now the object should be destroyed.
}
static void dual_object_shared_from_this(void){
int object_destroyed = 0;
std::shared_ptr<Event> ev = bellesip::make_shared<Event>();
std::shared_ptr<Event> ev = Event::create();
std::shared_ptr<Event> otherptr;
belle_sip_object_t *c_obj = ev->getCObject();
BC_ASSERT_PTR_NOT_NULL(c_obj);
......@@ -168,6 +197,21 @@ static void dual_object_shared_from_this(void){
BC_ASSERT_TRUE(object_destroyed);
}
static void dual_object_shared_from_this_from_c(void){
int object_destroyed = 0;
LinphoneEvent *event = Event::createCObject();
std::shared_ptr<Event> otherptr;
BC_ASSERT_PTR_NOT_NULL(event);
if (event){
belle_sip_object_weak_ref(event, on_object_destroyed, &object_destroyed);
}
otherptr = Event::getSharedFromThis(event);
otherptr.reset(); // the reset() of the shared_ptr shall not cause the object to be destroyed, because we still have the C ref obtained from createCObject().
BC_ASSERT_FALSE(object_destroyed);
belle_sip_object_unref(event);
BC_ASSERT_TRUE(object_destroyed);
}
static void main_loop_cpp_do_later(void){
int test = 0;
belle_sip_main_loop_t *ml = belle_sip_main_loop_new();
......@@ -182,8 +226,10 @@ static void main_loop_cpp_do_later(void){
static test_t object_tests[] = {
TEST_NO_TAG("Basic test", basic_test),
TEST_NO_TAG("Hybrid C/C++ object", dual_object),
TEST_NO_TAG("Hybrid C/C++ object clone", dual_object_clone),
TEST_NO_TAG("Hybrid C/C++ object with shared_ptr", dual_object_shared_ptr),
TEST_NO_TAG("Hybrid C/C++ object with shared_from_this", dual_object_shared_from_this),
TEST_NO_TAG("Hybrid C/C++ object with sharedFromThis()", dual_object_shared_from_this),
TEST_NO_TAG("Hybrid C/C++ object with sharedFromThis() from C object", dual_object_shared_from_this_from_c),
TEST_NO_TAG("Mainloop's do_later in c++", main_loop_cpp_do_later)
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment