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