Exemple #1
0
    def _generate_callback_wrapper(
        self,
        m: GeneratorMethod,
    ):
        # calling_back_code
        ret_type = m.ret_type
        args = m.args
        arguments_signature = ",".join(
            [self._to_cpp_variable(i) for i in args])
        arg_list = ",".join(
            ["this", f'"{m.alias}"', *[f"{i.name}" for i in args]])

        if m.has_overload:
            cast_expression = f"static_cast<{m.type}>(&{m.full_name})"
        else:
            cast_expression = f"&{m.full_name}"

        function_code = TextHolder()
        function_code += (
            f"{ret_type} {m.name}({arguments_signature}) override\n")
        function_code += "{\n" + Indent()
        calling_method = "call"
        if m.calling_type == CallingType.Async:
            calling_method = "async"
        elif m.calling_type == CallingType.Sync:
            calling_method = "sync"

        function_code += (
            f"return c2py::callback_wrapper<{cast_expression}>::{calling_method}("
            + Indent())
        function_code += f"{arg_list}" - IndentLater()
        function_code += f");"
        function_code += "}\n" - Indent()

        return function_code
Exemple #2
0
 def _process_function(self, of: GeneratorFunction):
     wf = of.resolve_wrappers()
     code = TextHolder()
     arg_decls = ", ".join([self._variable_with_hint(i) for i in wf.args])
     code += f'def {wf.name}({arg_decls})->{self._to_python_type(wf.ret_type)}:'
     if is_tuple_type(wf.ret_type):
         code += Indent(self._return_description_for_function(of))
     else:
         code += Indent("...")
     return code
Exemple #3
0
 def _generate_wrappers_for_class(self, c: GeneratorClass,
                                  wrapper_code: TextHolder):
     for ms in c.functions.values():
         for m in ms:
             if m.is_virtual and not m.is_final:
                 function_code = self._generate_callback_wrapper(m, )
                 wrapper_code += Indent(function_code)
     for super in c.super:
         wrapper_code += Indent(f'// methods from {super}')
         wrapper_code = self._generate_wrappers_for_class(
             super, wrapper_code)
     return wrapper_code
Exemple #4
0
    def _output_generated_functions(self):
        """
        :return:  # of files named 'generated_functions_??.cpp generated
        """
        # declaration
        decls = TextHolder()
        for f in self.function_manager.functions:
            decls += f'void {f.name}({f.arg_type} parent);'
        self._save_template('generated_functions.h',
                            'generated_functions.h',
                            includes=self._generate_includes(),
                            declarations=decls)

        # definitions
        header_padding_lines = 20
        total_lines = header_padding_lines
        for f in self.function_manager.functions:
            total_lines += f.body.line_count + 3

        prefer_lines_per_file = self.options.max_lines_per_file

        defs = TextHolder()
        i = 0
        for f in self.function_manager.functions:
            new_text = TextHolder()
            new_text += f'void {f.name}({f.arg_type} parent)'
            new_text += "{" + Indent()
            new_text += f.body
            new_text += "}" - Indent()
            if defs.line_count + new_text.line_count + header_padding_lines >= prefer_lines_per_file:
                self._save_template(
                    'generated_functions.cpp',
                    f'generated_functions_{i}.cpp',
                    includes=self._generate_includes(),
                    definitions=defs,
                )
                defs = new_text
                i += 1
            else:
                defs += new_text
        if len(str(defs)):
            self._save_template(
                'generated_functions.cpp',
                f'generated_functions_{i}.cpp',
                includes=self._generate_includes(),
                definitions=defs,
            )
        return i + 1  # return the number of generated_functions_.cpp generated
Exemple #5
0
    def _process_method(self, of: GeneratorMethod):
        wf = of.resolve_wrappers()
        code = TextHolder()
        arg_decls = ", ".join([self._variable_with_hint(i) for i in wf.args])

        self_text = 'self, '
        if wf.is_static:
            code += "@staticmethod"
            self_text = ""
        if wf.has_overload:
            code += "@overload"

        code += f'def {wf.name}({self_text}{arg_decls})->{self._to_python_type(wf.ret_type)}:'
        if is_tuple_type(wf.ret_type):
            code += Indent(self._return_description_for_function(of))
        else:
            code += Indent("...")
        return code
Exemple #6
0
    def _process_sub_namespace(self, ns: GeneratorNamespace,
                               cpp_scope_variable: str, body: TextHolder,
                               pfm: FunctionManager):
        for n in ns.namespaces.values():
            assert n.name, "sub Namespace has no name, someting wrong in Parser or preprocessor"
            function_name = slugify(f"generate_sub_namespace_{n.full_name}")
            function_body, fm = self._generate_namespace_body(n, n.name)
            if self.options.inject_symbol_name:
                body += f'// {n.full_name}'

            body += '{' + Indent()
            body += f'auto m = {cpp_scope_variable}.def_submodule("{n.name}");'
            body += f'{function_name}(m);'
            body += '}' - Indent()
            # todo: generate alias (namespace alias)

            pfm.add(function_name, "pybind11::module &", function_body)
            pfm.extend(fm)
Exemple #7
0
    def _process_namespace_functions(self, ns: GeneratorNamespace,
                                     cpp_scope_variable: str, body: TextHolder,
                                     pfm: FunctionManager):
        namespace_name = ns.name
        if not namespace_name:
            # trick: a unnamed namespace should be global namespace.
            namespace_name = self.module_name

        max_calls_per_function = self.options.max_template_calls_per_function
        n = 0
        i = 0
        sub_body = TextHolder()
        if ns.functions:
            for fs in ns.functions.values():
                for f in fs:
                    has_overload: bool = False
                    if len(fs) > 1:
                        has_overload = True
                    for m in fs:
                        sub_body += (
                            f"""{cpp_scope_variable}.def("{m.alias}",""" +
                            Indent())
                        sub_body += self._generate_calling_wrapper(
                            f, has_overload, append=',')
                        sub_body += f"pybind11::return_value_policy::reference,"
                        sub_body += f"pybind11::call_guard<pybind11::gil_scoped_release>()"
                        sub_body += f""");\n""" - Indent()
                        n += 1
                        if n == max_calls_per_function:
                            n = 0
                            function_name = f'generate_{namespace_name}_functions_{i}'
                            pfm.add(function_name, "pybind11::module &",
                                    sub_body)
                            body += f'{function_name}({cpp_scope_variable});'

                            sub_body = TextHolder()
                            i += 1
        function_name = f'generate_{namespace_name}_functions_{i}'
        pfm.add(function_name, "pybind11::module &", sub_body)
        body += f'{function_name}({cpp_scope_variable});'
Exemple #8
0
    def _process_class_functions(self, c: GeneratorClass, body: TextHolder,
                                 my_variable: str):
        # functions
        for ms in c.functions.values():
            has_overload: bool = False
            if len(ms) > 1:
                has_overload = True
            for m in ms:
                if self.options.inject_symbol_name:
                    body += f'// {m.full_name}'

                if m.is_static:
                    body += (f"""{my_variable}.def_static("{m.alias}",""" +
                             Indent())
                else:
                    body += (f"""{my_variable}.def("{m.alias}",""" + Indent())
                body += self._generate_calling_wrapper(m,
                                                       has_overload,
                                                       append=',')
                body += f"pybind11::return_value_policy::reference,"
                body += f"pybind11::call_guard<pybind11::gil_scoped_release>()"
                body += f""");\n""" - Indent()
Exemple #9
0
    def _process_class(self, c: GeneratorClass):
        code = TextHolder()

        # parent_place = f'({c.parent.name})' if c.parent and c.parent.name else ''
        super_list = ",".join(
            [self._to_python_type(i.full_name) for i in c.super])
        parent_place = f'({super_list})'
        code += f'class {c.name}{parent_place}:' + Indent()
        code += self._process_typedefs(c)
        code += self._process_classes(c)
        code += self._process_variables(c)
        code += self._process_enums(c)
        code += self._process_methods(c)
        return code
Exemple #10
0
 def _output_wrappers(self):
     wrappers = ""
     # generate callback wrappers
     for c in self.objects.values():
         if (isinstance(c, GeneratorClass) and self._has_wrapper(c)):
             py_class_name = "Py" + c.name
             wrapper_code = TextHolder()
             for ms in c.functions.values():
                 for m in ms:
                     if m.is_virtual and not m.is_final:
                         function_code = self._generate_callback_wrapper(
                             m, )
                         wrapper_code += Indent(function_code)
             py_class_code = self._render_file("wrapper_class.h",
                                               py_class_name=py_class_name,
                                               class_fullname=c.full_name,
                                               body=wrapper_code)
             wrappers += py_class_code
     self._save_template(f"wrappers.hpp", wrappers=wrappers)
Exemple #11
0
    def _generate_calling_wrapper(m: GeneratorFunction,
                                  has_overload,
                                  append=''):
        code = TextHolder()
        if m.wrappers:
            has_this = False
            if isinstance(m, GeneratorMethod) and not m.is_static:
                has_this = True
            if len(m.wrappers) == 1:

                wi = m.wrappers[0]
                code += f'c2py::{wi.wrapper.name} < ' + Indent()
                code += f'c2py::function_constant<{m.address}>,'
                code += f'std::integral_constant<int, {wi.index}{" + 1/*self*/" if has_this else ""}>'
                code += f'>::value{append}' - Indent()
            else:  # >= 2
                code += f'c2py::apply_function_transform<' + Indent()
                code += f'c2py::function_constant<{m.address}>,'
                code += 'brigand::list<' + Indent()
                lines = [
                    f'c2py::indexed_transform_holder<'
                    f'c2py::{wi.wrapper.name}, {wi.index}{" + 1/*self*/" if has_this else ""}>'
                    for wi in m.wrappers
                ]
                code.append_lines(lines, ',')
                code += '>' - Indent()

                code += f'>::value{append}' - Indent()
        else:
            if has_overload:
                code += f'static_cast<{m.type}>(' + Indent()
                code += f"""&{m.full_name}"""
                code += f"""),""" - IndentLater()
            else:
                code += f"""&{m.full_name},"""

        return code
Exemple #12
0
 def _process_enum(self, e: GeneratorEnum):
     code = TextHolder()
     code += f'class {e.name}(Enum):' + Indent()
     code += self._process_variables(e)
     return code
Exemple #13
0
    def _generate_class_body(self, c: GeneratorClass):
        body = TextHolder()
        fm = FunctionManager()

        my_variable = "c"
        has_wrapper = self._has_wrapper(c)
        wrapper_class_name = "Py" + c.name
        public_destructor = c.destructor is None or c.destructor.access == "public"
        hold_base_type = c.full_name if not has_wrapper else wrapper_class_name

        wrapper_field = "" if not has_wrapper else f', {wrapper_class_name}'
        holder_field = "" if public_destructor else f',std::unique_ptr<{hold_base_type}, pybind11::nodelete>'
        parents_field = "" if not c.super else f', {", ".join([i.full_name for i in c.super])}'

        if self.options.inject_symbol_name:
            body += f'// {c.full_name}'

        body += f"pybind11::class_<" + Indent()
        body += f"{c.full_name}"
        if wrapper_field:
            body += wrapper_field
        if holder_field:
            body += holder_field
        if parents_field:
            body += parents_field
        body += (f"""> {my_variable}(parent, "{c.name}");\n""" - Indent())

        # constructor
        constructor_name = c.full_name if not has_wrapper else wrapper_class_name
        if c.constructors:
            arg_list = ""
            for con in c.constructors:
                arg_list = ",".join([arg.type for arg in con.args])

                comma = ',' if arg_list else ''
                body += f"""if constexpr (std::is_constructible_v<""" + Indent(
                )
                body += f"""{constructor_name}{comma}{arg_list}"""
                body += f""">)""" - Indent()
                body += Indent(
                    f"""{my_variable}.def(pybind11::init<{arg_list}>());\n""")
        else:
            body += f"""if constexpr (std::is_default_constructible_v<{constructor_name}>)"""
            body += Indent(f"""{my_variable}.def(pybind11::init<>());\n""")

        self._process_class_functions(c, body, my_variable)
        self._process_class_variables(ns=c,
                                      body=body,
                                      cpp_scope_variable=my_variable,
                                      pfm=fm)
        self._process_enums(ns=c,
                            body=body,
                            cpp_scope_variable=my_variable,
                            pfm=fm)
        self._process_classes(ns=c,
                              body=body,
                              cpp_scope_variable=my_variable,
                              pfm=fm)

        # post_register
        body += f'AUTOCXXPY_POST_REGISTER_CLASS({self.module_tag}, {c.full_name}, {my_variable});\n'

        # objects record
        body += f'{self.module_class}::objects.emplace("{c.full_name}", {my_variable});'

        return body, fm