From 26c0dccfebdba96c25ab8166f60f8d38f5643c74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Grisez?= Date: Fri, 24 Mar 2017 14:01:28 +0100 Subject: [PATCH] C++ wrapper: pubilc structures support --- wrappers/cpp/CMakeLists.txt | 1 + wrappers/cpp/abstractapi.py | 66 ++++++++++++------ wrappers/cpp/class_header.mustache | 18 ++++- wrappers/cpp/class_impl.mustache | 21 ++++++ wrappers/cpp/genwrapper.py | 105 ++++++++++++++++++----------- wrappers/cpp/object.cc | 2 +- wrappers/cpp/object.hh | 16 ++++- wrappers/cpp/tools.hh | 48 +++++++++++-- 8 files changed, 207 insertions(+), 70 deletions(-) diff --git a/wrappers/cpp/CMakeLists.txt b/wrappers/cpp/CMakeLists.txt index 2059de15c..4720a3054 100644 --- a/wrappers/cpp/CMakeLists.txt +++ b/wrappers/cpp/CMakeLists.txt @@ -38,6 +38,7 @@ target_link_libraries(linphone++ target_include_directories(linphone++ PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include PRIVATE ${CMAKE_BINARY_DIR}/include + PRIVATE ${CMAKE_SOURCE_DIR}/include PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE ${BCTOOLBOX_INCLUDE_DIRS} PRIVATE ${BELLESIP_INCLUDE_DIRS} diff --git a/wrappers/cpp/abstractapi.py b/wrappers/cpp/abstractapi.py index bc6da71bd..ef27d4ec0 100644 --- a/wrappers/cpp/abstractapi.py +++ b/wrappers/cpp/abstractapi.py @@ -178,7 +178,7 @@ class ArgName(Name): return Name.to_snake_case(self) -class PropertyName(Name): +class PropertyName(ArgName): pass @@ -357,6 +357,7 @@ class Property(DocumentableObject): DocumentableObject.__init__(self, name) self._setter = None self._getter = None + self._type = None def set_setter(self, setter): self._setter = setter @@ -367,6 +368,8 @@ class Property(DocumentableObject): def set_getter(self, getter): self._getter = getter + if self._type is None: + self._type = getter.returnType getter.parent = self def get_getter(self): @@ -384,6 +387,7 @@ class Class(DocumentableObject): self.classMethods = [] self._listenerInterface = None self.multilistener = False + self.refcountable = False def add_property(self, property): self.properties.append(property) @@ -482,13 +486,21 @@ class CParser(object): 'linphone_friend_new', # was deprecated when the wrapper generator was made 'linphone_friend_new_with_address', # was deprecated when the wrapper generator was made 'linphone_core_get_sound_source', # was deprecated when the wrapper generator was made - 'linphone_core_set_sound_source'] # was deprecated when the wrapper generator was made + 'linphone_core_set_sound_source', # was deprecated when the wrapper generator was made + 'linphone_core_get_sip_transports', # not wrappable + 'linphone_core_get_sip_transports_used'] # not wrappable self.classBl = ['LinphoneImEncryptionEngine', 'LinphoneImEncryptionEngineCbs', 'LinphoneImNotifPolicy', 'LpConfig', - 'LinphoneCallStats'] # temporarly blacklisted + 'LinphoneErrorInfo', + 'LinphonePayloadType', + 'LinphonePlayer'] # temporarly blacklisted + + # list of classes that must be concidered as refcountable even if + # they are no ref()/unref() methods + self.forcedRefcountableClasses = ['LinphoneFactory'] self.cProject = cProject @@ -531,8 +543,19 @@ class CParser(object): pass except Error as e: print('Could not parse \'{0}\' class: {1}'.format(_class.name, e.args[0])) + CParser._fix_all_types(self) + + def _class_is_refcountable(self, _class): + if _class.name in self.forcedRefcountableClasses: + return True + + for method in _class.instanceMethods: + if method.startswith(_class.cFunctionPrefix) and method[len(_class.cFunctionPrefix):] == 'ref': + return True + return False + def _fix_all_types(self): for _class in self.classesIndex.values() + self.interfacesIndex.values(): if _class is not None: @@ -563,24 +586,24 @@ class CParser(object): except Error as e: print('warning: some types could not be fixed in {0}() function: {1}'.format(method.name.to_snake_case(fullName=True), e.args[0])) - def _fix_type(self, type): - if isinstance(type, EnumType) and type.desc is None: - type.desc = self.enumsIndex[type.name] - elif isinstance(type, ClassType) and type.desc is None: - if type.name in self.classesIndex: - type.desc = self.classesIndex[type.name] + def _fix_type(self, _type): + if isinstance(_type, EnumType) and _type.desc is None: + _type.desc = self.enumsIndex[_type.name] + elif isinstance(_type, ClassType) and _type.desc is None: + if _type.name in self.classesIndex: + _type.desc = self.classesIndex[_type.name] else: - type.desc = self.interfacesIndex[type.name] - elif isinstance(type, ListType) and type.containedTypeDesc is None: - if type.containedTypeName in self.classesIndex: - type.containedTypeDesc = ClassType(type.containedTypeName, classDesc=self.classesIndex[type.containedTypeName]) - elif type.containedTypeName in self.interfacesIndex: - type.containedTypeDesc = ClassType(type.containedTypeName, classDesc=self.interfacesIndex[type.containedTypeName]) - elif type.containedTypeName in self.enumsIndex: - type.containedTypeDesc = EnumType(type.containedTypeName, enumDesc=self.enumsIndex[type.containedTypeName]) + _type.desc = self.interfacesIndex[_type.name] + elif isinstance(_type, ListType) and _type.containedTypeDesc is None: + if _type.containedTypeName in self.classesIndex: + _type.containedTypeDesc = ClassType(_type.containedTypeName, classDesc=self.classesIndex[_type.containedTypeName]) + elif _type.containedTypeName in self.interfacesIndex: + _type.containedTypeDesc = ClassType(_type.containedTypeName, classDesc=self.interfacesIndex[_type.containedTypeName]) + elif _type.containedTypeName in self.enumsIndex: + _type.containedTypeDesc = EnumType(_type.containedTypeName, enumDesc=self.enumsIndex[_type.containedTypeName]) else: - if type.containedTypeName is not None: - type.containedTypeDesc = CParser.parse_c_base_type(self, type.containedTypeName) + if _type.containedTypeName is not None: + _type.containedTypeDesc = CParser.parse_c_base_type(self, _type.containedTypeName) else: raise Error('bctbx_list_t type without specified contained type') @@ -621,6 +644,7 @@ class CParser(object): name = ClassName() name.from_camel_case(cclass.name, namespace=self.namespace.name) _class = Class(name) + _class.refcountable = CParser._class_is_refcountable(self, cclass) for cproperty in cclass.properties.values(): try: @@ -755,10 +779,8 @@ class CParser(object): elif cType.ctype in self.enumsIndex: absType = EnumType(cType.ctype, enumDesc=self.enumsIndex[cType.ctype]) elif cType.ctype in self.classesIndex or cType.ctype in self.interfacesIndex: - #params = {'classDesc': self.classesIndex[cType.ctype]} - #if 'const' in cType.completeType.split(' '): - #params['isconst'] = True absType = ClassType(cType.ctype) + absType.isconst = cType.completeType.startswith('const ') elif cType.ctype == self.cListType: absType = ListType(cType.containedType) else: diff --git a/wrappers/cpp/class_header.mustache b/wrappers/cpp/class_header.mustache index 160bb9b67..d3ed12f23 100644 --- a/wrappers/cpp/class_header.mustache +++ b/wrappers/cpp/class_header.mustache @@ -43,14 +43,23 @@ namespace linphone { {{/priorDeclarations}} {{#_class}} - class {{className}}: public {{{parentClassName}}} { + class {{className}}{{#parentClassName}}: public {{{parentClassName}}}{{/parentClassName}} { {{#friendClasses}} friend class {{name}}; {{/friendClasses}} public: {{#isNotListener}} + {{#isrefcountable}} {{{className}}}(void *ptr, bool takeRef=true); + {{/isrefcountable}} + {{#isnotrefcountable}} + LINPHONECXX_PUBLIC {{{className}}}(); + LINPHONECXX_PUBLIC {{{className}}}(const {{{className}}} &src); + {{{className}}}(const void *src); + LINPHONECXX_PUBLIC ~{{{className}}}(); + LINPHONECXX_PUBLIC const void *c_struct() const {return mPrivPtr;} + {{/isnotrefcountable}} {{/isNotListener}} {{#ismonolistenable}} @@ -71,6 +80,7 @@ namespace linphone { {{#isVcard}} LINPHONECXX_PUBLIC std::shared_ptr &getVcard(); {{/isVcard}} + {{#methods}} {{{prototype}}} @@ -91,7 +101,11 @@ namespace linphone { private: void *mCallbacks; {{/ismultilistenable}} - + + {{#isnotrefcountable}} + private: + void *mPrivPtr; + {{/isnotrefcountable}} }; {{/_class}} diff --git a/wrappers/cpp/class_impl.mustache b/wrappers/cpp/class_impl.mustache index a9e3aec11..142413ff9 100644 --- a/wrappers/cpp/class_impl.mustache +++ b/wrappers/cpp/class_impl.mustache @@ -47,12 +47,33 @@ static {{{returnType}}} {{{cbName}}}({{{declArgs}}}) { {{/wrapperCbs}} {{#isNotListener}} +{{#isrefcountable}} {{{namespace}}}::{{{className}}}::{{{className}}}(void *ptr, bool takeRef): {{{parentClassName}}}(ptr, takeRef) { {{#ismultilistenable}} mCallbacks = ({{{cListenerName}}} *)createCallbacks(&getListeners()); {{{callbacksAdder}}}(({{{cClassName}}} *)mPrivPtr, ({{{cListenerName}}} *)mCallbacks); {{/ismultilistenable}} } +{{/isrefcountable}} + +{{#isnotrefcountable}} +{{{namespace}}}::{{{className}}}::{{{className}}}() { + mPrivPtr = new {{{cClassName}}}; + memset(mPrivPtr, 0, sizeof({{{cClassName}}})); +} + +{{{namespace}}}::{{{className}}}::{{{className}}}(const {{{className}}} &src) { + mPrivPtr = new {{{cClassName}}}(*({{{cClassName}}} *)src.mPrivPtr); +} + +{{{namespace}}}::{{{className}}}::{{{className}}}(const void *src) { + mPrivPtr = new {{{cClassName}}}(*({{{cClassName}}} *)src); +} + +{{{namespace}}}::{{{className}}}::~{{{className}}}() { + delete ({{{cClassName}}} *)mPrivPtr; +} +{{/isnotrefcountable}} {{/isNotListener}} {{#ismonolistenable}} diff --git a/wrappers/cpp/genwrapper.py b/wrappers/cpp/genwrapper.py index 14c94fa1c..751c90428 100755 --- a/wrappers/cpp/genwrapper.py +++ b/wrappers/cpp/genwrapper.py @@ -66,22 +66,24 @@ class CppTranslator(object): ismonolistenable = (islistenable and not _class.multilistener) ismultilistenable = (islistenable and _class.multilistener) - classDict = {} - classDict['islistenable'] = islistenable - classDict['isnotlistenable'] = not islistenable - classDict['ismonolistenable'] = ismonolistenable - classDict['ismultilistenable'] = ismultilistenable - classDict['isNotListener'] = True - classDict['isfactory'] = (_class.name.to_c() == 'LinphoneFactory') - classDict['isVcard'] = (_class.name.to_c() == 'LinphoneVcard') - classDict['parentClassName'] = None - classDict['className'] = CppTranslator.translate_class_name(_class.name) - classDict['cClassName'] = '::' + _class.name.to_c() - classDict['parentClassName'] = 'Object' - classDict['methods'] = [] - classDict['staticMethods'] = [] - classDict['wrapperCbs'] = [] - classDict['friendClasses'] = [] + classDict = { + 'islistenable' : islistenable, + 'isnotlistenable' : not islistenable, + 'ismonolistenable' : ismonolistenable, + 'ismultilistenable' : ismultilistenable, + 'isrefcountable' : _class.refcountable, + 'isnotrefcountable' : not _class.refcountable, + 'isNotListener' : True, + 'isfactory' : (_class.name.to_c() == 'LinphoneFactory'), + 'isVcard' : (_class.name.to_c() == 'LinphoneVcard'), + 'className' : CppTranslator.translate_class_name(_class.name), + 'cClassName' : '::' + _class.name.to_c(), + 'parentClassName' : 'Object' if _class.refcountable else None, + 'methods' : [], + 'staticMethods' : [], + 'wrapperCbs' : [], + 'friendClasses' : [] + } if _class.name.to_c() == 'LinphoneCore': classDict['friendClasses'].append({'name': 'Factory'}); @@ -279,20 +281,33 @@ class CppTranslator(object): elif type(exprtype) is AbsApi.EnumType: cExpr = '(::{0}){1}'.format(exprtype.desc.name.to_c(), cppExpr) elif type(exprtype) is AbsApi.ClassType: - param = {} - param['ptrType'] = CppTranslator.translate_class_type(self, exprtype, namespace=usedNamespace) - param['ptrType'] = CppTranslator.sharedPtrTypeExtractor.match(param['ptrType']).group(2) - param['cPtrType'] = exprtype.desc.name.to_c() - param['cppExpr'] = cppExpr - param['object'] = 'const Object' if exprtype.isconst else 'Object' - cExpr = '(::{cPtrType} *)Object::sharedPtrToCPtr(std::static_pointer_cast<{object},{ptrType}>({cppExpr}))'.format(**param) + cPtrType = exprtype.desc.name.to_c() + if exprtype.desc.refcountable: + ptrType = CppTranslator.translate_class_type(self, exprtype, namespace=usedNamespace) + ptrType = CppTranslator.sharedPtrTypeExtractor.match(ptrType).group(2) + param = { + 'ptrType' : ptrType, + 'cPtrType': cPtrType, + 'cppExpr' : cppExpr, + 'object' : 'const Object' if exprtype.isconst else 'Object' + } + cExpr = '(::{cPtrType} *)Object::sharedPtrToCPtr(std::static_pointer_cast<{object},{ptrType}>({cppExpr}))'.format(**param) + else: + cExpr = '(const ::{_type} *)({expr}).c_struct()'.format(_type=cPtrType, expr=cppExpr) elif type(exprtype) is AbsApi.ListType: if type(exprtype.containedTypeDesc) is AbsApi.BaseType and exprtype.containedTypeDesc.name == 'string': cExpr = 'StringBctbxListWrapper({0}).c_list()'.format(cppExpr) elif type(exprtype.containedTypeDesc) is AbsApi.ClassType: ptrType = CppTranslator.translate_class_type(self, exprtype.containedTypeDesc, namespace=usedNamespace) - ptrType = CppTranslator.sharedPtrTypeExtractor.match(ptrType).group(2) - cExpr = 'ObjectBctbxListWrapper<{0}>({1}).c_list()'.format(ptrType, cppExpr) + if exprtype.containedTypeDesc.desc.refcountable: + ptrType = CppTranslator.sharedPtrTypeExtractor.match(ptrType).group(2) + cExpr = 'ObjectBctbxListWrapper<{0}>({1}).c_list()'.format(ptrType, cppExpr) + else: + cType = exprtype.containedTypeDesc.desc.name.to_c() + if exprtype.isconst: + cExpr = 'StructBctbxListWrapper<{0},{1}>({2}).c_list()'.format(ptrType, cType, cppExpr) + else: + cExpr = 'StructBctbxListWrapper<{0},{1}>::cppListToBctbxList({2})'.format(ptrType, cType, cppExpr) else: raise AbsApi.Error('translation of bctbx_list_t of enums or basic C types is not supported') @@ -315,19 +330,26 @@ class CppTranslator(object): return '({0}){1}'.format(cppEnumName, cExpr) elif type(exprtype) is AbsApi.ClassType: cppReturnType = CppTranslator.translate_class_type(self, exprtype, namespace=usedNamespace) - cppReturnType = CppTranslator.sharedPtrTypeExtractor.match(cppReturnType).group(2) - - if type(exprtype.parent) is AbsApi.Method and len(exprtype.parent.name.words) >=1 and (exprtype.parent.name.words == ['new'] or exprtype.parent.name.words[0] == 'create'): - return 'Object::cPtrToSharedPtr<{0}>((::belle_sip_object_t *){1}, false)'.format(cppReturnType, cExpr) + if exprtype.desc.refcountable: + cppReturnType = CppTranslator.sharedPtrTypeExtractor.match(cppReturnType).group(2) + + if type(exprtype.parent) is AbsApi.Method and len(exprtype.parent.name.words) >=1 and (exprtype.parent.name.words == ['new'] or exprtype.parent.name.words[0] == 'create'): + return 'Object::cPtrToSharedPtr<{0}>({1}, false)'.format(cppReturnType, cExpr) + else: + return 'Object::cPtrToSharedPtr<{0}>({1})'.format(cppReturnType, cExpr) else: - return 'Object::cPtrToSharedPtr<{0}>((::belle_sip_object_t *){1})'.format(cppReturnType, cExpr) + return '{0}({1})'.format(exprtype.desc.name.to_camel_case(), cExpr); elif type(exprtype) is AbsApi.ListType: if type(exprtype.containedTypeDesc) is AbsApi.BaseType and exprtype.containedTypeDesc.name == 'string': return 'StringBctbxListWrapper::bctbxListToCppList({0})'.format(cExpr) elif type(exprtype.containedTypeDesc) is AbsApi.ClassType: cppReturnType = CppTranslator.translate_class_type(self, exprtype.containedTypeDesc, namespace=usedNamespace) - cppReturnType = CppTranslator.sharedPtrTypeExtractor.match(cppReturnType).group(2) - return 'ObjectBctbxListWrapper<{0}>::bctbxListToCppList({1})'.format(cppReturnType, cExpr) + if exprtype.containedTypeDesc.desc.refcountable: + cppReturnType = CppTranslator.sharedPtrTypeExtractor.match(cppReturnType).group(2) + return 'ObjectBctbxListWrapper<{0}>::bctbxListToCppList({1})'.format(cppReturnType, cExpr) + else: + cType = exprtype.containedTypeDesc.desc.name.to_c() + return 'StructBctbxListWrapper<{0},{1}>::bctbxListToCppList({2})'.format(cppReturnType, cType, cExpr) else: raise AbsApi.Error('translation of bctbx_list_t of enums or basic C types is not supported') else: @@ -429,13 +451,18 @@ class CppTranslator(object): res = CppTranslator.translate_class_name(_type.desc.name, recursive=True, topAncestor=nsName) - if _type.isconst: - res = 'const ' + res - - if type(_type.parent) is AbsApi.Argument: - return 'const std::shared_ptr<{0}> &'.format(res) + if _type.desc.refcountable: + if _type.isconst: + res = 'const ' + res + if type(_type.parent) is AbsApi.Argument: + return 'const std::shared_ptr<{0}> &'.format(res) + else: + return 'std::shared_ptr<{0}>'.format(res) else: - return 'std::shared_ptr<{0}>'.format(res) + if type(_type.parent) is AbsApi.Argument: + return 'const {0} &'.format(res) + else: + return '{0}'.format(res) def translate_list_type(self, _type, **params): if _type.containedTypeDesc is None: @@ -679,6 +706,8 @@ def main(): render(renderer, header, includedir + '/enums.hh') mainHeader = MainHeader() + mainHeader.add_include('enums.hh') + impl = ClassImpl() for _class in parser.classesIndex.values() + parser.interfacesIndex.values(): diff --git a/wrappers/cpp/object.cc b/wrappers/cpp/object.cc index eb99658d8..cf23c6360 100644 --- a/wrappers/cpp/object.cc +++ b/wrappers/cpp/object.cc @@ -72,7 +72,7 @@ std::map &Object::getUserData() const { return *userData; } -Object *Object::getBackPtrFromCPtr(void *ptr) { +linphone::Object *linphone::Object::getBackPtrFromCPtr(const void *ptr) { return (Object *)belle_sip_object_data_get((::belle_sip_object_t *)ptr, "cpp_object"); } diff --git a/wrappers/cpp/object.hh b/wrappers/cpp/object.hh index 61a94676a..d069856d3 100644 --- a/wrappers/cpp/object.hh +++ b/wrappers/cpp/object.hh @@ -85,11 +85,25 @@ namespace linphone { } } } + template + static std::shared_ptr cPtrToSharedPtr(const void *ptr, bool takeRef=true) { + if (ptr == NULL) { + return nullptr; + } else { + Object *cppPtr = getBackPtrFromCPtr(ptr); + if (cppPtr == NULL) { + return std::make_shared((void *)ptr, takeRef); + } else { + return std::static_pointer_cast(cppPtr->shared_from_this()); + } + } + } static void *sharedPtrToCPtr(const std::shared_ptr &sharedPtr); + private: LINPHONECXX_PUBLIC std::map &getUserData() const; - static Object *getBackPtrFromCPtr(void *ptr); + static Object *getBackPtrFromCPtr(const void *ptr); template static void deleteSharedPtr(std::shared_ptr *ptr) {if (ptr != NULL) delete ptr;} static void deleteString(std::string *str) {if (str != NULL) delete str;} diff --git a/wrappers/cpp/tools.hh b/wrappers/cpp/tools.hh index 81c5dbcdb..5d2fae0da 100644 --- a/wrappers/cpp/tools.hh +++ b/wrappers/cpp/tools.hh @@ -43,14 +43,12 @@ namespace linphone { class ObjectBctbxListWrapper: public AbstractBctbxListWrapper { public: ObjectBctbxListWrapper(const std::list > &cppList) { - for(auto it=cppList.cbegin(); it!=cppList.cend(); it++) { - ::belle_sip_object_t *cPtr = (::belle_sip_object_t *)Object::sharedPtrToCPtr(std::static_pointer_cast(*it)); - if (cPtr != NULL) belle_sip_object_ref(cPtr); - mCList = bctbx_list_append(mCList, cPtr); - } + mCList = cppListToBctbxList(cppList); } virtual ~ObjectBctbxListWrapper() { - mCList = bctbx_list_free_with_data(mCList, unrefData); + if (mCList != NULL) { + bctbx_list_free_with_data(mCList, unrefData); + } } static std::list > bctbxListToCppList(const ::bctbx_list_t *bctbxList) { std::list > cppList; @@ -60,6 +58,15 @@ namespace linphone { } return cppList; } + static ::bctbx_list_t *cppListToBctbxList(const std::list > &cppList) { + bctbx_list_t *cList = NULL; + for(auto it=cppList.cbegin(); it!=cppList.cend(); it++) { + ::belle_sip_object_t *cPtr = (::belle_sip_object_t *)Object::sharedPtrToCPtr(std::static_pointer_cast(*it)); + if (cPtr != NULL) belle_sip_object_ref(cPtr); + cList = bctbx_list_append(cList, cPtr); + } + return cList; + } private: static void unrefData(void *data) { @@ -75,6 +82,35 @@ namespace linphone { static std::list bctbxListToCppList(const ::bctbx_list_t *bctbxList); }; + + template + class StructBctbxListWrapper: public AbstractBctbxListWrapper { + public: + StructBctbxListWrapper(const std::list &cppList): AbstractBctbxListWrapper() { + mCList = cppListToBctbxList(cppList); + } + virtual ~StructBctbxListWrapper() { + bctbx_list_free_with_data(mCList, (bctbx_list_free_func)deleteCStruct); + } + static std::list bctbxListToCppList(const ::bctbx_list_t *bctbxList) { + std::list cppList; + for(const bctbx_list_t *it = bctbx_list_first_elem(bctbxList); it != NULL; it = bctbx_list_next(it)) { + cppList->push_back(T(it->data)); + } + return cppList; + } + static bctbx_list_t *cppListToBctbxList(const std::list &cppList) { + bctbx_list_t *cList = NULL; + for(auto it=cppList.cbegin(); it!=cppList.cend(); it++) { + cList = bctbx_list_append(cList, new U(it->c_struct())); + } + return cList; + } + + private: + static void deleteCStruct(U *cStruct) {delete cStruct;} + }; + class StringUtilities { public: static std::string cStringToCpp(const char *cstr);