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