Example #1
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
Example #2
0
    def _generate_namespace_body(self,
                                 ns: GeneratorNamespace,
                                 name: str = None):
        if name is None:
            name = ns.name
        fm = FunctionManager()
        body = TextHolder()
        cpp_scope_variable = "parent"

        def gen(kind: str, processor: Callable):
            nonlocal body
            sub_body = TextHolder()
            function_name = f'generate_{name}_{kind}'
            processor(ns=ns,
                      cpp_scope_variable=cpp_scope_variable,
                      body=sub_body,
                      pfm=fm)
            fm.add(function_name, "pybind11::module &", sub_body)
            body += f'{function_name}({cpp_scope_variable});'

        gen("sub_namespace", self._process_sub_namespace)
        gen("classes", self._process_classes)
        gen("enums", self._process_enums)
        gen("functions", self._process_namespace_functions)
        gen("variables", self._process_namespace_variables)
        gen("typedefs", self._process_typedefs)
        gen("caster", self._process_caster)
        return body, fm
Example #3
0
    def _generate_enum_body(self, e: GeneratorEnum):
        fm = FunctionManager()
        body = TextHolder()

        my_variable = "e"
        if self.options.arithmetic_enum:
            arithmetic_enum_code = ", pybind11::arithmetic()"
        else:
            arithmetic_enum_code = ""

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

        body += (
            f'pybind11::enum_<{e.full_name}> {my_variable}(parent, "{e.alias}"{arithmetic_enum_code});'
        )

        for v in e.variables.values():
            if self.options.inject_symbol_name:
                body += f'// {v.full_name}'

            body += f'{my_variable}.value("{v.alias}", {v.full_name});'
        if not e.is_strong_typed:
            body += f'{my_variable}.export_values();'

        # objects record
        body += f'{self.module_class}::objects.emplace("{e.full_name}", {my_variable});'
        return body, fm
Example #4
0
 def batch_process(self, ns: GeneratorNamespace, attr_name: str,
                   func: Callable):
     code = TextHolder()
     container: dict = getattr(ns, attr_name)
     for v in container.values():
         code += func(v)
     return code
Example #5
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
Example #6
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
Example #7
0
 def gen(kind: str, processor: Callable):
     nonlocal body
     sub_body = TextHolder()
     function_name = f'generate_{name}_{kind}'
     processor(ns=ns,
               cpp_scope_variable=cpp_scope_variable,
               body=sub_body,
               pfm=fm)
     fm.add(function_name, "pybind11::module &", sub_body)
     body += f'{function_name}({cpp_scope_variable});'
Example #8
0
 def _output_config(self):
     code = TextHolder()
     windows = self.options.string_encoding_windows
     linux = self.options.string_encoding_linux
     if windows == "utf-8" and linux == "utf-8":
         code += '#define AUTOCXXPY_ENCODING_UTF8'
     else:
         code += f'#define AUTOCXXPY_ENCODING_CUSTOM'
         code += f'#define AUTOCXXPY_ENCODING_CUSTOM_WINDOWS "{windows}"'
         code += f'#define AUTOCXXPY_ENCODING_CUSTOM_LINUX "{linux}"'
     self._save_template("config.h", "config.h", body=code)
Example #9
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});'
Example #10
0
 def _return_description_for_function(self, of: GeneratorFunction):
     code = TextHolder()
     return_elements = [
         '"retv"',
     ]
     wf = of
     for wi in wf.wrappers:
         arg = wf.args[wi.index]
         return_elements.append(f'"{arg.name}"')
         wf = wi.wrapper.wrap(f=wf, index=wi.index, wrapper_info=wi)
     return_str = ",".join(return_elements)
     code += f'return {return_str}'
     return code
Example #11
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
Example #12
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
Example #13
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()
             wrapper_code = self._generate_wrappers_for_class(
                 c, wrapper_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)
Example #14
0
    def _output_config(self):
        code = TextHolder()
        windows = self.options.string_encoding_windows
        linux = self.options.string_encoding_linux
        if windows == "utf-8" and linux == "utf-8":
            code += '#define AUTOCXXPY_ENCODING_UTF8'
        else:
            code += f'#define AUTOCXXPY_ENCODING_CUSTOM'
            code += f'#define AUTOCXXPY_ENCODING_CUSTOM_WINDOWS "{windows}"'
            code += f'#define AUTOCXXPY_ENCODING_CUSTOM_LINUX "{linux}"'
        parser_options = self.options.pre_processor_result.parser_result.parser_options
        for d in parser_options.definitions:
            d.replace('=', ' ')
            code += f'#define {d}'

        self._save_template("config.h", "config.h", body=code)
Example #15
0
    def _output_module(self):
        function_name = slugify(f'generate_{self.module_name}')
        function_body, fm = self._generate_namespace_body(
            self.options.g, self.module_name)

        module_body = TextHolder()
        module_body += 1
        module_body += f'{function_name}(m);'
        self._save_template(
            "module.cpp",
            "module.cpp",
            module_body=module_body,
            module_tag=self.module_tag,
        )
        self.function_manager.add(function_name, "pybind11::module &",
                                  function_body)
        self.function_manager.extend(fm)
Example #16
0
    def _generate_caster_body(self, ns: GeneratorNamespace):
        fm = FunctionManager()
        body = TextHolder()
        cpp_scope_variable = "c"
        body += "struct caster: c2py::caster{};"
        body += f"""auto {cpp_scope_variable} = c2py::caster::bind<caster>(parent, "{self.options.caster_class_name}"); """
        for c in ns.classes.values():
            if self.options.inject_symbol_name:
                body += f'// {c.full_name}'

            body += f'c2py::caster::try_generate<{c.full_name}>({cpp_scope_variable}, "to{c.name}");'
        for p in ns.typedefs.values():
            if self.options.inject_symbol_name:
                body += f'// {p.full_name}'

            body += f'c2py::caster::try_generate<{p.full_name}>({cpp_scope_variable}, "to{p.name}");'

        return body, fm
Example #17
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
Example #18
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)
Example #19
0
    def _process_namespace(self, ns: GeneratorNamespace):
        code = TextHolder()

        # import sub modules first
        code_filename_base = self._module_filename_base(ns)

        code += self._process_namespaces(ns)
        code += self._process_classes(ns)
        code += self._process_enums(ns)
        code += self._process_typedefs(ns)
        code += self._process_variables(ns)
        code += self._process_functions(ns)

        self._save_template(
            "hint.py.in",
            f'{code_filename_base}.pyi',
            hint_code=code,
        )

        return f"from . import {code_filename_base} as {ns.name}"
Example #20
0
 def _process_enum(self, e: GeneratorEnum):
     code = TextHolder()
     code += f'class {e.name}(Enum):' + Indent()
     code += self._process_variables(e)
     return code
Example #21
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
Example #22
0
 def _process_namespaces(self, ns: GeneratorNamespace):
     code = TextHolder()
     for n in ns.namespaces.values():
         code += self._process_namespace(n)
     return code
Example #23
0
 def _process_functions(self, ns: GeneratorNamespace):
     code = TextHolder()
     for ms in ns.functions.values():
         for m in ms:
             code += self._process_function(m)
     return code
Example #24
0
 def _process_methods(self, ns: GeneratorClass):
     code = TextHolder()
     for ms in ns.functions.values():
         for m in ms:
             code += self._process_method(m)
     return code
Example #25
0
 def __init__(self):
     self.body: TextHolder = TextHolder()
     self.function_manager = FunctionManager()