object++.hh 6.66 KB
Newer Older
Simon Morlat's avatar
Simon Morlat committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
	belle-sip - SIP (RFC3261) library.
	Copyright (C) 2019  Belledonne Communications SARL

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

19 20
#ifndef belle_sip_object_plusplus_h
#define belle_sip_object_plusplus_h
Simon Morlat's avatar
Simon Morlat committed
21

22
#include "belle-sip/types.h"
Simon Morlat's avatar
Simon Morlat committed
23
#include "belle-sip/object.h"
24
#include "belle-sip/utils.h"
Simon Morlat's avatar
Simon Morlat committed
25 26

#include <memory>
27
#include <list>
Simon Morlat's avatar
Simon Morlat committed
28 29
#include <functional>

30
namespace bellesip {
Simon Morlat's avatar
Simon Morlat committed
31 32 33

class ObjectCAccessors;

34
class BELLESIP_EXPORT Object {
Simon Morlat's avatar
Simon Morlat committed
35 36 37 38 39 40 41 42 43 44 45
	friend ObjectCAccessors;
	public:
		Object();
		Object *ref();
		void unref();
		virtual belle_sip_error_code marshal(char* buff, size_t buff_size, size_t *offset);
		std::string toString()const{
			std::string ret(belle_sip_object_to_string(&mObject));
			return ret;
		}
		virtual Object *clone()const;
46 47
		belle_sip_cpp_object_t *getCObject();
		const belle_sip_cpp_object_t *getCObject()const;
Simon Morlat's avatar
Simon Morlat committed
48 49 50 51 52 53 54 55 56 57 58 59 60 61
		void *getCPtr(){
			return static_cast<void*>(getCObject());
		}
		const void *getCPtr()const{
			return static_cast<const void*>(getCObject());
		}
		static Object *getCppObject(void *);
		static const Object *getCppObject(const void *);

	protected:
		virtual ~Object(); /*the destructor must be kept protected, never public, including for all classes inherting from this*/
		Object(const Object &other);
	private:
		void init();
62 63 64
		belle_sip_cpp_object_t mObject;
		static belle_sip_cpp_object_t *sClone(belle_sip_cpp_object_t *);
		static belle_sip_error_code sMarshal(belle_sip_cpp_object_t* obj, char* buff, size_t buff_size, size_t *offset);
Simon Morlat's avatar
Simon Morlat committed
65 66 67 68 69 70
};

/**
 * Template class to help define an Object usable in both C and C++
 * The template arguments are:
 * - _CType : the type used to represent this object in C
71
 * - _CppType : the type used in C++ to implement this object. _CppType is used to be set to the type
Simon Morlat's avatar
Simon Morlat committed
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
 *   of the class inheriting from HybridObject.
 * Example:
 * typedef struct _CExample CExample;
 * class Example : public HybridObject<CExample, Example>{
 * ...
 * }
 * 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.
 *
 * 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.
 * An usage example is shown in tester/object_tester.cc .
**/
template <typename _CType, typename _CppType>
89
class BELLESIP_EXPORT HybridObject : public Object, public std::enable_shared_from_this<HybridObject<_CType, _CppType> > {
Simon Morlat's avatar
Simon Morlat committed
90
	public:
91 92 93 94 95 96 97 98 99 100 101
		//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));
		}
		//Convenience creator to get a C object. 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();
Simon Morlat's avatar
Simon Morlat committed
102 103 104 105 106 107 108 109 110 111 112 113 114
		}
		_CType *toC(){
			return static_cast<_CType*>(getCPtr());
		}
		const _CType *toC()const{
			return static_cast<const _CType*>(getCPtr());
		}
		static _CppType *toCpp(_CType *ptr){
			return static_cast<_CppType *>(getCppObject(ptr));
		}
		static const _CppType *toCpp(const _CType *ptr){
			return static_cast<const _CppType *>(getCppObject(ptr));
		}
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

		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() {
			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));
		}
		//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();
		}
		//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;
			for (auto it = cList; it; it = bctbx_list_next(it))
				result.push_back(toCpp(static_cast<_CType>(bctbx_list_get_data(it))));
			return result;
		}
		//Convenience method for easy bctbx_list(_Ctype) -> std::list<_CppType> conversion
		//Applies 'func' to get _CppType from _CType. Used in case we do not want to call  `toCpp` on _Ctype
		static std::list<_CppType> getCppListFromCList(const bctbx_list_t *cList, const std::function<_CppType (_CType)> &func) {
			std::list<_CppType> result;
			for (auto it = cList; it; it = bctbx_list_next(it))
			 	result.push_back(func(static_cast<_CType>(bctbx_list_get_data(it))));
			return result;
		}

Simon Morlat's avatar
Simon Morlat committed
156 157
	protected:
		virtual ~HybridObject() = default;
158 159
		HybridObject() {
		}
Simon Morlat's avatar
Simon Morlat committed
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
		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>
BELLESIP_EXPORT 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" {
175
	BELLE_SIP_DECLARE_VPTR(belle_sip_cpp_object_t);
Simon Morlat's avatar
Simon Morlat committed
176 177
}

178
#endif //belle_sip_object_plusplus_h