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
 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)}:'
     code += Indent("...")
     return code
    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
 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
    def _generate_namespace_body(self, ns: GeneratorNamespace):
        fm = FunctionManager()
        body = TextHolder()
        cpp_scope_variable = "parent"

        self._process_sub_namespace(ns=ns,
                                    cpp_scope_variable=cpp_scope_variable,
                                    body=body,
                                    pfm=fm)
        self._process_classes(ns=ns,
                              cpp_scope_variable=cpp_scope_variable,
                              body=body,
                              pfm=fm)
        self._process_enums(ns=ns,
                            cpp_scope_variable=cpp_scope_variable,
                            body=body,
                            pfm=fm)
        self._process_namespace_functions(
            ns=ns, cpp_scope_variable=cpp_scope_variable, body=body, pfm=fm)
        self._process_namespace_variables(
            ns=ns, cpp_scope_variable=cpp_scope_variable, body=body, pfm=fm)
        self._process_typedefs(ns=ns,
                               cpp_scope_variable=cpp_scope_variable,
                               body=body,
                               pfm=fm)

        self._process_caster(ns=ns,
                             cpp_scope_variable=cpp_scope_variable,
                             body=body,
                             pfm=fm)

        return body, fm
    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
Beispiel #7
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
Beispiel #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)
Beispiel #9
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
    def _generate_caster_body(self, ns: GeneratorNamespace):
        fm = FunctionManager()
        body = TextHolder()
        cpp_scope_variable = "c"
        body += "struct caster: autocxxpy::caster{};"
        body += f"""auto {cpp_scope_variable} = autocxxpy::caster::bind<caster>(parent, "{self.options.caster_class_name}"); """
        for c in ns.classes.values():
            body += f'autocxxpy::caster::try_generate<{c.full_name}>({cpp_scope_variable}, "to{c.name})");'
        for p in ns.typedefs.values():
            body += f'autocxxpy::caster::try_generate<{p.full_name}>({cpp_scope_variable}, "to{p.name})");'

        return body, fm
Beispiel #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
Beispiel #12
0
    def _output_module(self):
        function_name = slugify(f'generate_{self.module_name}')
        function_body, fm = self._generate_namespace_body(self.options.g)

        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)
    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)}:'
        code += Indent("...")
        return code
 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)
Beispiel #15
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}"
    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 = ""
        body += (
            f'pybind11::enum_<{e.full_name}> {my_variable}(parent, "{e.alias}"{arithmetic_enum_code});'
        )

        for v in e.variables.values():
            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
Beispiel #17
0
 def _process_enum(self, e: GeneratorEnum):
     code = TextHolder()
     code += f'class {e.name}(Enum):' + Indent()
     code += self._process_variables(e)
     return code
Beispiel #18
0
 def _process_namespaces(self, ns: GeneratorNamespace):
     code = TextHolder()
     for n in ns.namespaces.values():
         code += self._process_namespace(n)
     return code
Beispiel #19
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
Beispiel #20
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
 def __init__(self):
     self.body: TextHolder = TextHolder()
     self.function_manager = FunctionManager()