1# 2# ----- boost::shared_ptr ----------- 3# 4 5from .base import Parameter, ReturnValue, \ 6 join_ctype_and_name, CodeGenerationError, \ 7 param_type_matcher, return_type_matcher, CodegenErrorBase, \ 8 DeclarationsScope, CodeBlock, NotSupportedError, ForwardWrapperBase, ReverseWrapperBase, \ 9 TypeConfigurationError 10 11from pybindgen.cppclass import SmartPointerPolicy, CppClass, CppClassParameterBase, CppClassReturnValueBase, common_shared_object_return 12 13class BoostSharedPtr(SmartPointerPolicy): 14 def __init__(self, class_name): 15 """ 16 Create a memory policy for using boost::shared_ptr<> to manage instances of this object. 17 18 :param class_name: the full name of the class, e.g. foo::Bar 19 """ 20 self.class_name = class_name 21 self.pointer_template = '::boost::shared_ptr< %s >' 22 23 def get_pointer_name(self, class_name): 24 return self.pointer_template % (class_name,) 25 26 def get_delete_code(self, cpp_class): 27 return "self->obj.~shared_ptr< %s >();" % (cpp_class.full_name,) 28 29 def get_pointer_type(self, class_full_name): 30 return self.get_pointer_name(class_full_name) + ' ' 31 32 def get_pointer_to_void_name(self, object_name): 33 return "%s.get()" % object_name 34 35 def get_instance_creation_function(self): 36 return boost_shared_ptr_instance_creation_function 37 38 def get_pystruct_init_code(self, cpp_class, obj): 39 return "new(&%s->obj) %s;" % (obj, self.get_pointer_name(cpp_class.full_name),) 40 41 def register_ptr_parameter_and_return(self, cls, name): 42 class ThisClassSharedPtrParameter(CppClassSharedPtrParameter): 43 """Register this C++ class as pass-by-pointer parameter""" 44 CTYPES = [] 45 cpp_class = cls 46 cls.ThisClassSharedPtrParameter = ThisClassSharedPtrParameter 47 try: 48 param_type_matcher.register(self.get_pointer_name(cls.full_name), cls.ThisClassSharedPtrParameter) 49 except ValueError: 50 pass 51 52 class ThisClassSharedPtrReturn(CppClassSharedPtrReturnValue): 53 """Register this C++ class as pointer return""" 54 CTYPES = [] 55 cpp_class = cls 56 cls.ThisClassSharedPtrReturn = ThisClassSharedPtrReturn 57 try: 58 return_type_matcher.register(self.get_pointer_name(cls.full_name), cls.ThisClassSharedPtrReturn) 59 except ValueError: 60 pass 61 62 def register_ptr_alias_parameter_and_return(self, cls, alias): 63 alias_ptr = self.get_pointer_name(alias) 64 cls.ThisClassSharedPtrParameter.CTYPES.append(alias_ptr) 65 try: 66 param_type_matcher.register(alias_ptr, cls.ThisClassSharedPtrParameter) 67 except ValueError: pass 68 69 cls.ThisClassSharedPtrReturn.CTYPES.append(alias_ptr) 70 try: 71 return_type_matcher.register(alias_ptr, cls.ThisClassSharedPtrReturn) 72 except ValueError: pass 73 74def boost_shared_ptr_instance_creation_function(cpp_class, code_block, lvalue, 75 parameters, construct_type_name): 76 """ 77 boost::shared_ptr "instance creation function"; it is called whenever a new 78 C++ class instance needs to be created 79 80 :param cpp_class: the CppClass object whose instance is to be created 81 :param code_block: CodeBlock object on which the instance creation code should be generated 82 :param lvalue: lvalue expression that should hold the result in the end 83 :param parameters: stringified list of parameters 84 :param construct_type_name: actual name of type to be constructed (it is 85 not always the class name, sometimes it's 86 the python helper class) 87 """ 88 assert lvalue 89 assert not lvalue.startswith('None') 90 if cpp_class.incomplete_type: 91 raise CodeGenerationError("%s cannot be constructed (incomplete type)" 92 % cpp_class.full_name) 93 code_block.write_code( 94 "%s.reset (new %s(%s));" % (lvalue, construct_type_name, parameters)) 95 96 97class StdSharedPtr(BoostSharedPtr): 98 def __init__(self, class_name): 99 """ 100 Create a memory policy for using std::shared_ptr<> to manage instances of this object. 101 102 :param class_name: the full name of the class, e.g. foo::Bar 103 """ 104 self.class_name = class_name 105 self.pointer_template = '::std::shared_ptr< %s >' 106 107 def get_instance_creation_function(self): 108 return std_shared_ptr_instance_creation_function 109 110def std_shared_ptr_instance_creation_function(cpp_class, code_block, lvalue, 111 parameters, construct_type_name): 112 """ 113 std::shared_ptr "instance creation function"; it is called whenever a new 114 C++ class instance needs to be created 115 116 :param cpp_class: the CppClass object whose instance is to be created 117 :param code_block: CodeBlock object on which the instance creation code should be generated 118 :param lvalue: lvalue expression that should hold the result in the end 119 :param parameters: stringified list of parameters 120 :param construct_type_name: actual name of type to be constructed (it is 121 not always the class name, sometimes it's 122 the python helper class) 123 """ 124 assert lvalue 125 assert not lvalue.startswith('None') 126 if cpp_class.incomplete_type: 127 raise CodeGenerationError("%s cannot be constructed (incomplete type)" 128 % cpp_class.full_name) 129 code_block.write_code( 130 "%s = std::make_shared<%s>(%s);" % (lvalue, construct_type_name, parameters)) 131 132 133class CppClassSharedPtrParameter(CppClassParameterBase): 134 "Class* handlers" 135 CTYPES = [] 136 cpp_class = None #cppclass.CppClass('dummy') # CppClass instance 137 DIRECTIONS = [Parameter.DIRECTION_IN, 138 Parameter.DIRECTION_OUT, 139 Parameter.DIRECTION_INOUT] 140 SUPPORTS_TRANSFORMATIONS = False 141 142 def __init__(self, ctype, name, direction=Parameter.DIRECTION_IN, is_const=False, 143 null_ok=False, default_value=None): 144 """ 145 Type handler for a pointer-to-class parameter (MyClass*) 146 147 :param ctype: C type, normally 'MyClass*' 148 :param name: parameter name 149 150 :param is_const: if true, the parameter has a const attached to the leftmost 151 152 :param null_ok: if true, None is accepted and mapped into a C NULL pointer 153 154 :param default_value: default parameter value (as C expression 155 string); probably, the only default value that makes sense 156 here is probably 'NULL'. 157 158 .. note:: 159 160 Only arguments which are instances of C++ classes 161 wrapped by PyBindGen can be used as custodians. 162 """ 163 super(CppClassSharedPtrParameter, self).__init__( 164 ctype, name, direction, is_const, default_value) 165 self.null_ok = null_ok 166 167 168 def convert_python_to_c(self, wrapper): 169 "parses python args to get C++ value" 170 assert isinstance(wrapper, ForwardWrapperBase) 171 assert isinstance(self.cpp_class, CppClass) 172 173 self.py_name = wrapper.declarations.declare_variable( 174 self.cpp_class.pystruct+'*', self.name, 175 initializer=(self.default_value and 'NULL' or None)) 176 177 value_ptr = wrapper.declarations.declare_variable( 178 self.cpp_class.memory_policy.get_pointer_name(self.cpp_class.full_name), "%s_ptr" % self.name) 179 180 if self.null_ok: 181 num = wrapper.parse_params.add_parameter('O', ['&'+self.py_name], self.name, optional=bool(self.default_value)) 182 183 wrapper.before_call.write_error_check( 184 185 "%s && ((PyObject *) %s != Py_None) && !PyObject_IsInstance((PyObject *) %s, (PyObject *) &%s)" 186 % (self.py_name, self.py_name, self.py_name, self.cpp_class.pytypestruct), 187 188 'PyErr_SetString(PyExc_TypeError, "Parameter %i must be of type %s");' % (num, self.cpp_class.name)) 189 190 wrapper.before_call.write_code("if (%(PYNAME)s) {\n" 191 " if ((PyObject *) %(PYNAME)s == Py_None)\n" 192 " %(VALUE)s = NULL;\n" 193 " else\n" 194 " %(VALUE)s = %(PYNAME)s->obj;\n" 195 "} else {\n" 196 " %(VALUE)s = NULL;\n" 197 "}" % dict(PYNAME=self.py_name, VALUE=value_ptr)) 198 199 else: 200 201 wrapper.parse_params.add_parameter( 202 'O!', ['&'+self.cpp_class.pytypestruct, '&'+self.py_name], self.name, optional=bool(self.default_value)) 203 wrapper.before_call.write_code("if (%s) { %s = %s->obj; }" % (self.py_name, value_ptr, self.py_name)) 204 205 wrapper.call_params.append(value_ptr) 206 207 208 209 def convert_c_to_python(self, wrapper): 210 """foo""" 211 212 ## Value transformations 213 value = self.transformation.untransform( 214 self, wrapper.declarations, wrapper.after_call, self.value) 215 216 ## declare wrapper variable 217 py_name = wrapper.declarations.declare_variable( 218 self.cpp_class.pystruct+'*', 'py_'+self.cpp_class.name) 219 self.py_name = py_name 220 221 def write_create_new_wrapper(): 222 """Code path that creates a new wrapper for the parameter""" 223 224 ## Find out what Python wrapper to use, in case 225 ## automatic_type_narrowing is active and we are not forced to 226 ## make a copy of the object 227 if self.cpp_class.automatic_type_narrowing: 228 229 typeid_map_name = self.cpp_class.get_type_narrowing_root().typeid_map_name 230 wrapper_type = wrapper.declarations.declare_variable( 231 'PyTypeObject*', 'wrapper_type', '0') 232 wrapper.before_call.write_code( 233 '%s = %s.lookup_wrapper(typeid(*%s), &%s);' 234 % (wrapper_type, typeid_map_name, value, self.cpp_class.pytypestruct)) 235 else: 236 wrapper_type = '&'+self.cpp_class.pytypestruct 237 238 ## Create the Python wrapper object 239 self.cpp_class.write_allocate_pystruct(wrapper.before_call, py_name, wrapper_type) 240 self.py_name = py_name 241 242 wrapper.before_call.write_code("%s->flags = PYBINDGEN_WRAPPER_FLAG_NONE;" % py_name) 243 244 ## Assign the C++ value to the Python wrapper 245 wrapper.before_call.write_code("%s->obj = %s;" % (py_name, value)) 246 247 if self.cpp_class.helper_class is None: 248 try: 249 self.cpp_class.wrapper_registry.write_lookup_wrapper( 250 wrapper.before_call, self.cpp_class.pystruct, py_name, value) 251 except NotSupportedError: 252 write_create_new_wrapper() 253 self.cpp_class.wrapper_registry.write_register_new_wrapper(wrapper.before_call, py_name, 254 "%s->obj" % py_name) 255 else: 256 wrapper.before_call.write_code("if (%s == NULL)\n{" % py_name) 257 wrapper.before_call.indent() 258 write_create_new_wrapper() 259 self.cpp_class.wrapper_registry.write_register_new_wrapper(wrapper.before_call, py_name, 260 "%s->obj" % py_name) 261 wrapper.before_call.unindent() 262 wrapper.before_call.write_code('}') 263 wrapper.build_params.add_parameter("N", [py_name]) 264 else: 265 wrapper.before_call.write_code("if (typeid(*(%s)).name() == typeid(%s).name())\n{" 266 % (value, self.cpp_class.helper_class.name)) 267 wrapper.before_call.indent() 268 269 if self.type_traits.target_is_const: 270 wrapper.before_call.write_code( 271 "%s = (%s*) (((%s*) ((%s*) %s))->m_pyself);" 272 % (py_name, self.cpp_class.pystruct, 273 self.cpp_class.helper_class.name, self.cpp_class.full_name, value)) 274 wrapper.before_call.write_code("%s->obj = (%s*) (%s);" % 275 (py_name, self.cpp_class.full_name, value)) 276 else: 277 wrapper.before_call.write_code( 278 "%s = (%s*) (((%s*) %s)->m_pyself);" 279 % (py_name, self.cpp_class.pystruct, 280 self.cpp_class.helper_class.name, value)) 281 wrapper.before_call.write_code("%s->obj = %s;" % (py_name, value)) 282 wrapper.before_call.write_code("Py_INCREF(%s);" % py_name) 283 wrapper.before_call.unindent() 284 wrapper.before_call.write_code("} else {") 285 wrapper.before_call.indent() 286 287 try: 288 self.cpp_class.wrapper_registry.write_lookup_wrapper( 289 wrapper.before_call, self.cpp_class.pystruct, py_name, value) 290 except NotSupportedError: 291 write_create_new_wrapper() 292 self.cpp_class.wrapper_registry.write_register_new_wrapper( 293 wrapper.before_call, py_name, "%s->obj" % py_name) 294 else: 295 wrapper.before_call.write_code("if (%s == NULL)\n{" % py_name) 296 wrapper.before_call.indent() 297 write_create_new_wrapper() 298 self.cpp_class.wrapper_registry.write_register_new_wrapper(wrapper.before_call, py_name, 299 "%s->obj" % py_name) 300 wrapper.before_call.unindent() 301 wrapper.before_call.write_code('}') # closes if (%s == NULL) 302 303 wrapper.before_call.unindent() 304 wrapper.before_call.write_code("}") # closes if (typeid(*(%s)) == typeid(%s))\n{ 305 wrapper.build_params.add_parameter("N", [py_name]) 306 307 308 309 310class CppClassSharedPtrReturnValue(CppClassReturnValueBase): 311 "Class* return handler" 312 CTYPES = [] 313 SUPPORTS_TRANSFORMATIONS = True 314 cpp_class = None #cppclass.CppClass('dummy') # CppClass instance 315 316 def __init__(self, ctype, is_const=False): 317 """ 318 :param ctype: C type, normally 'MyClass*' 319 """ 320 super(CppClassSharedPtrReturnValue, self).__init__(ctype, is_const=is_const) 321 322 def get_c_error_return(self): # only used in reverse wrappers 323 """See ReturnValue.get_c_error_return""" 324 return "return NULL;" 325 326 def convert_c_to_python(self, wrapper): 327 """See ReturnValue.convert_c_to_python""" 328 329 ## Value transformations 330 value = self.transformation.untransform( 331 self, wrapper.declarations, wrapper.after_call, self.value) 332 333 # if value is NULL, return None 334 wrapper.after_call.write_code("if (!(%s)) {\n" 335 " Py_INCREF(Py_None);\n" 336 " return Py_None;\n" 337 "}" % value) 338 339 ## declare wrapper variable 340 py_name = wrapper.declarations.declare_variable( 341 self.cpp_class.pystruct+'*', 'py_'+self.cpp_class.name) 342 self.py_name = py_name 343 344 common_shared_object_return(value, py_name, self.cpp_class, wrapper.after_call, 345 self.type_traits, caller_owns_return=True, 346 reference_existing_object=False, 347 type_is_pointer=True) 348 349 # return the value 350 wrapper.build_params.add_parameter("N", [py_name], prepend=True) 351 352 353 def convert_python_to_c(self, wrapper): 354 """See ReturnValue.convert_python_to_c""" 355 name = wrapper.declarations.declare_variable( 356 self.cpp_class.pystruct+'*', "tmp_%s" % self.cpp_class.name) 357 wrapper.parse_params.add_parameter( 358 'O!', ['&'+self.cpp_class.pytypestruct, '&'+name]) 359 360 value = self.transformation.transform( 361 self, wrapper.declarations, wrapper.after_call, "%s->obj" % name) 362 363 # caller gets a shared pointer 364 wrapper.after_call.write_code("%s = %s;" % (self.value, value)) 365 366