Esempio n. 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 autocxxpy::callback_wrapper<{cast_expression}>::{calling_method}("
            + Indent())
        function_code += f"{arg_list}" - IndentLater()
        function_code += f");"
        function_code += "}\n" - Indent()

        return function_code
Esempio n. 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
Esempio n. 3
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)
            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)
Esempio n. 4
0
 def _process_namespace_functions(self, ns: GeneratorNamespace, cpp_scope_variable: str,
                                  body: TextHolder, pfm: FunctionManager):
     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:
                     body += (
                         f"""{cpp_scope_variable}.def("{m.alias}",""" + Indent()
                     )
                     body += self._generate_calling_wrapper(f, has_overload, append=',')
                     body += f"pybind11::return_value_policy::reference,"
                     body += f"pybind11::call_guard<pybind11::gil_scoped_release>()"
                     body += f""");\n""" - Indent()
Esempio n. 5
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
        total_lines = 0
        for f in self.function_manager.functions:
            total_lines += f.body.line_count

        prefer_lines_per_file = self.options.max_lines_per_file - 100
        if total_lines > self.options.max_lines_per_file:
            prefer_lines_per_file = total_lines / int(
                total_lines / self.options.max_lines_per_file)

        defs = TextHolder()
        i = 0
        for f in self.function_manager.functions:
            defs += f'void {f.name}({f.arg_type} parent)'
            defs += "{" + Indent()
            defs += f.body
            defs += "}" - Indent()
            if defs.line_count >= prefer_lines_per_file:
                self._save_template(
                    'generated_functions.cpp',
                    f'generated_functions_{i}.cpp',
                    includes=self._generate_includes(),
                    definitions=defs,
                )
                defs = TextHolder()
                i += 1
        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
Esempio n. 6
0
    def _generate_calling_wrapper(m: GeneratorFunction,
                                  has_overload,
                                  append=''):
        code = TextHolder()
        code += f'autocxxpy::apply_function_transform<' + Indent()
        code += f'autocxxpy::function_constant<' + Indent()

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

        code += '>, ' - Indent()

        code += 'brigand::list<' + Indent()
        has_this = False
        if isinstance(m, GeneratorMethod) and not m.is_static:
            has_this = True
        lines = [
            f'autocxxpy::indexed_transform_holder<'
            f'autocxxpy::{wi.wrapper.name}, {wi.index + 1 if has_this else wi.index}>'
            for wi in m.wrappers
        ]
        code.append_lines(lines, ',')
        code += '>' - Indent()

        code += f'>::value{append}' - Indent()
        return code
Esempio n. 7
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
Esempio n. 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 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::call_guard<pybind11::gil_scoped_release>()"
             body += f""");\n""" - Indent()
Esempio n. 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
Esempio n. 10
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

        if has_wrapper:
            if (c.destructor is None or c.destructor.access == "public"):
                body += f"""pybind11::class_<{c.full_name}, {wrapper_class_name}> {my_variable}(parent, "{c.name}");\n"""
            else:
                body += f"pybind11::class_<" + Indent()
                body += f"{c.full_name},"
                body += f"std::unique_ptr<{c.full_name}, pybind11::nodelete>,"
                body += f"{wrapper_class_name}"
                body += (f"""> {my_variable}(parent, "{c.name}");\n""" -
                         Indent())
        else:
            body += f"""pybind11::class_<{c.full_name}> {my_variable}(parent, "{c.name}");\n"""

        # 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
Esempio n. 11
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)
Esempio n. 12
0
 def _process_enum(self, e: GeneratorEnum):
     code = TextHolder()
     code += f'class {e.name}(Enum):' + Indent()
     code += self._process_variables(e)
     return code