diff --git a/README.md b/README.md index 97fb220..3e68fdd 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,11 @@ r = Rectangle(4, 5) - By default, cppwg only rewrites wrapper files whose content has changed, leaving unchanged files untouched so build systems skip recompiling them. Pass `--overwrite` to force a full rewrite of all wrapper files. +- A templated class's instantiations share a single wrapper file: `Foo<2>` and + `Foo<3>` are wrapped in one `Foo.cppwg.hpp` / `Foo.cppwg.cpp` pair (declaring + and defining a `register_Foo_2_class` and `register_Foo_3_class` each) rather + than a separate file pair per instantiation. This reduces the number of + generated files, and the translation units the build has to compile. - To pass extra flags to the castxml clang frontend (e.g. to silence a diagnostic), use `--castxml_cflags`. Values starting with `-` must use `=`, e.g. `--castxml_cflags="-Wno-deprecated"`. diff --git a/cppwg/generators.py b/cppwg/generators.py index c33a285..74a7252 100644 --- a/cppwg/generators.py +++ b/cppwg/generators.py @@ -323,9 +323,10 @@ def discover_template_instantiations(self) -> None: instantiation_map: dict[str, list[list[str]]] = {} macro_only_files: list[str] = [] for filepath in self.package_info.source_cpp_files: - has_instantiations, file_map = ( - utils.find_template_instantiations_in_source_file(filepath) - ) + ( + has_instantiations, + file_map, + ) = utils.find_template_instantiations_in_source_file(filepath) if not has_instantiations: continue diff --git a/cppwg/info/class_info.py b/cppwg/info/class_info.py index afcae63..dfde083 100644 --- a/cppwg/info/class_info.py +++ b/cppwg/info/class_info.py @@ -514,6 +514,29 @@ def update_from_source(self, source_file_paths: list[str]) -> None: # Update the C++ and Python class names self.update_names() + def py_name_base(self) -> str: + """ + Return the shared Python-name base for the class, before template args. + + This is the common prefix of every instantiation's Python name (e.g. + "Foo" for py_names ["Foo_2_2", "Foo_3_3"]), and is used to name the + single wrapper file shared by all of a class's instantiations. For an + untemplated class it is the (override) name unchanged; for a templated + class it is cleaned the same way as the instantiation names. + """ + base = self.name_override or self.name + if not self.template_arg_lists: + return base + + for name, replacement in self.name_replacements.items(): + base = base.replace(name, replacement) + base = base.translate( + str.maketrans({"<": None, ">": None, ",": None, " ": None}) + ) + if len(base) > 1: + base = base[0].capitalize() + base[1:] + return base + def update_py_names(self) -> None: """ Set the Python names for the class, accounting for template args. @@ -524,34 +547,17 @@ def update_py_names(self) -> None: class instantiation. For example, class "Foo" with template arguments [[2, 2], [3, 3]] will have a Python name list ["Foo_2_2", "Foo_3_3"]. """ + class_name = self.py_name_base() + # Handles untemplated classes if not self.template_arg_lists: - if self.name_override: - self.py_names.append(self.name_override) - else: - self.py_names.append(self.name) + self.py_names.append(class_name) return # Table of special characters for removal rm_chars = {"<": None, ">": None, ",": None, " ": None} rm_table = str.maketrans(rm_chars) - # Clean the class name - class_name = self.name - if self.name_override: - class_name = self.name_override - - # Do standard name replacements e.g. "unsigned int" -> "Unsigned" - for name, replacement in self.name_replacements.items(): - class_name = class_name.replace(name, replacement) - - # Remove special characters - class_name = class_name.translate(rm_table) - - # Capitalize the first letter e.g. "foo" -> "Foo" - if len(class_name) > 1: - class_name = class_name[0].capitalize() + class_name[1:] - # Create a string of template args separated by "_" e.g. 2_2 for template_arg_list in self.template_arg_lists: # Example template_arg_list : [2, 2] diff --git a/cppwg/templates/custom.py b/cppwg/templates/custom.py index 868421a..f2e5231 100644 --- a/cppwg/templates/custom.py +++ b/cppwg/templates/custom.py @@ -25,6 +25,14 @@ def get_class_cpp_def_code(self, *args, **kwargs) -> str: return "" + def get_module_pre_code(self) -> str: + """ + Return a string of C++ code to be inserted before the module + definition. + """ + + return "" + def get_module_code(self) -> str: """ Return a string of C++ code to be inserted in the module diff --git a/cppwg/templates/pybind11_default.py b/cppwg/templates/pybind11_default.py index cad4aa7..312862e 100644 --- a/cppwg/templates/pybind11_default.py +++ b/cppwg/templates/pybind11_default.py @@ -8,7 +8,9 @@ # every entry in template_collection is filled the same way (via .substitute). class_virtual_override_header = Template( - "class ${class_py_name}" + CPPWG_CLASS_OVERRIDE_SUFFIX + " : public ${class_py_name}\n" + "class ${class_py_name}" + + CPPWG_CLASS_OVERRIDE_SUFFIX + + " : public ${class_py_name}\n" "{\n" "public:\n" " using ${class_py_name}::${class_base_name};\n" @@ -88,38 +90,64 @@ " PyErr_SetString(PyExc_RuntimeError, ${message_expr});\n" ) -# Skeleton for a class wrapper hpp file. +# Skeleton for a class wrapper hpp file. One hpp is emitted per class (not per +# template instantiation); it forward-declares the register function for every +# instantiation via ${register_declarations}. class_hpp = Template( "${prefix_text}" - "#ifndef ${class_py_name}_hpp__" + CPPWG_EXT + "_wrapper\n" - "#define ${class_py_name}_hpp__" + CPPWG_EXT + "_wrapper\n" + "#ifndef ${class_hpp_name}_hpp__" + CPPWG_EXT + "_wrapper\n" + "#define ${class_hpp_name}_hpp__" + CPPWG_EXT + "_wrapper\n" "\n" "#include \n" "\n" + "${register_declarations}" + "#endif // ${class_hpp_name}_hpp__" + CPPWG_EXT + "_wrapper\n" +) + +# A single register-function forward declaration, one per instantiation, joined +# into ${register_declarations} above. +class_hpp_register_declaration = Template( "void register_${class_py_name}_class(pybind11::module &m);\n" - "#endif // ${class_py_name}_hpp__" + CPPWG_EXT + "_wrapper\n" ) -# Skeleton for a class wrapper cpp file. -class_cpp = Template( +# Preamble for a class wrapper cpp file, emitted once per class. The file-scope +# items that must appear only once when several instantiations share a file live +# here: the includes, the smart-pointer holder declaration, class-level prefix +# code, and the (deduplicated) trampoline return typedefs. It also carries the +# per-instantiation alias typedefs (typedef ;) for every instantiation +# in the file, gathered into ${class_typedefs} ahead of any registration code so +# each block's generator pre-code, trampoline and registration can refer to any +# instantiation by its alias. Each instantiation's registration follows via one +# class_cpp_register block. +class_cpp_header = Template( "${prefix_text}" "#include \n" "#include \n" "${includes}" "\n" - '#include "${class_py_name}.' + CPPWG_EXT + '.hpp"\n' + '#include "${class_hpp_name}.' + CPPWG_EXT + '.hpp"\n' "\n" "namespace py = pybind11;\n" - "typedef ${class_cpp_name} ${class_py_name};\n" "${smart_ptr_handle};\n" "${prefix_code}" - "${generator_pre_code}" + "${class_typedefs}" "${return_typedefs}" +) + +# Registration block for one template instantiation, appended once per +# instantiation after the class_cpp_header preamble. Refers to the class through +# the wrapper alias (class_py_name) so the trampoline, registration function name +# and Python-visible name all match. The alias typedefs for every instantiation +# are emitted once in the class_cpp_header preamble (${class_typedefs}), so the +# generator pre-code, trampoline override class and registration below can all +# refer to the class by that alias without redefining it here. +class_cpp_register = Template( + "${generator_pre_code}" "\n" "${override_class}" "void register_${class_py_name}_class(py::module &m)\n" "{\n" - ' py::class_<${class_py_name}${overrides_string}${ptr_support}${bases}>' + " py::class_<${class_py_name}${overrides_string}${ptr_support}${bases}>" '(m, "${class_py_name}")\n' "${constructors}" "${methods}" @@ -131,25 +159,13 @@ # Skeleton for the struct-enum special case, e.g.: # struct Foo { enum Value { A, B, C }; }; -# The header block is identical to a class cpp file; the registration body wraps -# the single nested enum. Like the class cpp, it refers to the class through the -# wrapper alias (class_py_name, typedef'd to the C++ type) so the registration -# function name matches the hpp declaration and the module's register_..._class -# call, and the Python-visible name is the wrapper name (e.g. for templated -# instantiations or name overrides where it differs from the C++ decl name). -struct_enum_cpp = Template( - "${prefix_text}" - "#include \n" - "#include \n" - "${includes}" - "\n" - '#include "${class_py_name}.' + CPPWG_EXT + '.hpp"\n' - "\n" - "namespace py = pybind11;\n" - "typedef ${class_cpp_name} ${class_py_name};\n" - "${smart_ptr_handle};\n" - "${prefix_code}" +# The registration body wraps the single nested enum. Like class_cpp_register it +# refers to the class through the wrapper alias (class_py_name), whose typedef is +# emitted in the shared class_cpp_header preamble (${class_typedefs}), and follows +# that preamble. +struct_enum_register = Template( "${generator_pre_code}" + "\n" "void register_${class_py_name}_class(py::module &m){\n" ' py::class_<${class_py_name}> myclass(m, "${class_py_name}");\n' ' py::enum_<${class_py_name}::${enum_name}>(myclass, "${enum_name}")\n' @@ -187,8 +203,10 @@ "module_exception_catch": module_exception_catch, "header_collection_hpp": header_collection_hpp, "class_hpp": class_hpp, - "class_cpp": class_cpp, - "struct_enum_cpp": struct_enum_cpp, + "class_hpp_register_declaration": class_hpp_register_declaration, + "class_cpp_header": class_cpp_header, + "class_cpp_register": class_cpp_register, + "struct_enum_register": struct_enum_register, "free_function": free_function, "class_method": class_method, "class_constructor": class_constructor, diff --git a/cppwg/utils/utils.py b/cppwg/utils/utils.py index 224d99d..3bf17d1 100644 --- a/cppwg/utils/utils.py +++ b/cppwg/utils/utils.py @@ -13,6 +13,33 @@ from pygccxml.declarations.class_declaration import class_t +def ensure_trailing_newline(code: str) -> str: + """ + Return `code` guaranteed to end with a newline (unless it is empty). + + Custom generators (subclasses of `cppwg.templates.custom.Custom`) return raw + C++ snippets with no trailing-newline guarantee. When such a snippet is + substituted into a wrapper template immediately ahead of another line, a + missing newline glues the two together and can produce invalid C++ (e.g. a + `#include` directive that no longer starts a line, or a closing `}` swallowed + by a trailing `//` comment). Normalise the snippet here so callers can splice + it safely regardless of how the generator formatted it. + + Parameters + ---------- + code : str + The generator-produced code snippet, possibly empty. + + Returns + ------- + str + The snippet guaranteed to end with a trailing newline, or "" unchanged. + """ + if code and not code.endswith("\n"): + return code + "\n" + return code + + def write_file_if_changed(filepath: str, content: str, overwrite: bool = False) -> bool: """ Write content to filepath unless an identical file already exists. diff --git a/cppwg/writers/class_writer.py b/cppwg/writers/class_writer.py index 67a8539..2328052 100644 --- a/cppwg/writers/class_writer.py +++ b/cppwg/writers/class_writer.py @@ -12,7 +12,7 @@ CPPWG_EXT, CPPWG_HEADER_COLLECTION_FILENAME, ) -from cppwg.utils.utils import write_file_if_changed +from cppwg.utils.utils import ensure_trailing_newline, write_file_if_changed from cppwg.writers.base_writer import CppBaseWrapperWriter from cppwg.writers.constructor_writer import CppConstructorWrapperWriter from cppwg.writers.method_writer import CppMethodWrapperWriter @@ -301,38 +301,83 @@ def bases_block(self, class_decl: "class_t") -> str: return bases - def build_hpp(self, class_py_name: str) -> str: + def build_hpp(self, register_py_names: list[str]) -> str: """ Build the class wrapper hpp file contents. + One hpp is emitted per class, forward-declaring the register function for + every instantiation that is written. + Parameters ---------- - class_py_name : str - The Python name of the class e.g. Foo_2_2 + register_py_names : list[str] + The Python names of the instantiations that have a register function, + e.g. ["Foo_2_2", "Foo_3_3"]. Returns ------- str The hpp wrapper code. """ + decl_template = self.wrapper_templates["class_hpp_register_declaration"] + register_declarations = "".join( + decl_template.substitute(class_py_name=class_py_name) + for class_py_name in register_py_names + ) return self.wrapper_templates["class_hpp"].substitute( prefix_text=self.prefix_block(), - class_py_name=class_py_name, + class_hpp_name=self.class_info.py_name_base(), + register_declarations=register_declarations, ) - def build_class_cpp(self, template_idx: int) -> str: + def build_cpp_header( + self, class_typedefs: str = "", return_typedefs: str = "" + ) -> str: """ - Build the class wrapper cpp file contents for one template instantiation. + Build the shared preamble of the class wrapper cpp file. + + Emitted once per class (not per instantiation): the includes, the smart + pointer holder declaration, class-level prefix code, the per-instantiation + alias typedefs, and the deduplicated trampoline return typedefs. Parameters ---------- - template_idx : int - The index of the template in the class info + class_typedefs : str + The alias typedefs (typedef ;) for every instantiation in + the file, emitted ahead of the registration blocks so any block can + refer to any instantiation by its alias. + return_typedefs : str + The trampoline return typedefs, deduplicated across instantiations. Returns ------- str - The cpp wrapper code. + The cpp preamble. + """ + return self.wrapper_templates["class_cpp_header"].substitute( + prefix_text=self.prefix_block(), + includes=self.includes_block(), + class_hpp_name=self.class_info.py_name_base(), + smart_ptr_handle=self.smart_ptr_handle(), + prefix_code=self.prefix_code(), + class_typedefs=class_typedefs, + return_typedefs=return_typedefs, + ) + + def build_class_register(self, template_idx: int) -> tuple[str, str]: + """ + Build the registration block for one template instantiation. + + Parameters + ---------- + template_idx : int + The index of the template instantiation in the class info. + + Returns + ------- + tuple[str, str] + The registration block and its trampoline return typedefs (the + latter are hoisted into the shared preamble and deduplicated). """ class_cpp_name = self.class_info.cpp_names[template_idx] class_py_name = self.class_info.py_names[template_idx] @@ -340,9 +385,11 @@ def build_class_cpp(self, template_idx: int) -> str: generator = self.class_info.custom_generator_instance # Find and define virtual function "trampoline" overrides - return_typedefs, override_class, methods_needing_override = ( - self.virtual_overrides(template_idx) - ) + ( + return_typedefs, + override_class, + methods_needing_override, + ) = self.virtual_overrides(template_idx) # Add the trampoline override class to the class definition if needed # e.g. py::class_(m, "Foo") @@ -382,32 +429,32 @@ def build_class_cpp(self, template_idx: int) -> str: ) ) - return self.wrapper_templates["class_cpp"].substitute( - prefix_text=self.prefix_block(), - includes=self.includes_block(), - class_py_name=class_py_name, - class_cpp_name=class_cpp_name, - smart_ptr_handle=self.smart_ptr_handle(), - prefix_code=self.prefix_code(), + block = self.wrapper_templates["class_cpp_register"].substitute( generator_pre_code=( generator.get_class_cpp_pre_code(class_py_name) if generator else "" ), - return_typedefs=return_typedefs, + class_py_name=class_py_name, + class_cpp_name=class_cpp_name, override_class=override_class, overrides_string=overrides_string, ptr_support=ptr_support, bases=self.bases_block(class_decl), constructors=constructors, methods=methods, - generator_def_code=( + # Normalise the generator snippet to end with a newline: it is spliced + # into the .def() chain ahead of suffix_code and the closing `;`, so a + # missing newline (or a trailing // comment) could swallow the + # statement terminator. See ensure_trailing_newline. + generator_def_code=ensure_trailing_newline( generator.get_class_cpp_def_code(class_py_name) if generator else "" ), suffix_code=self.suffix_code(), ) + return block, return_typedefs - def build_struct_enum_cpp(self, template_idx: int) -> str: + def build_struct_enum_register(self, template_idx: int) -> str: """ - Build the cpp file contents for the struct-enum special case. + Build the registration block for a struct-enum instantiation. Handles a struct that wraps a single nested enum, for example: @@ -418,12 +465,12 @@ def build_struct_enum_cpp(self, template_idx: int) -> str: Parameters ---------- template_idx : int - The index of the template in the class info + The index of the template instantiation in the class info. Returns ------- str - The cpp wrapper code. + The registration block. """ class_cpp_name = self.class_info.cpp_names[template_idx] class_py_name = self.class_info.py_names[template_idx] @@ -444,23 +491,23 @@ def build_struct_enum_cpp(self, template_idx: int) -> str: for value in enum_decl.values ) - return self.wrapper_templates["struct_enum_cpp"].substitute( - prefix_text=self.prefix_block(), - includes=self.includes_block(), - class_py_name=class_py_name, - class_cpp_name=class_cpp_name, - smart_ptr_handle=self.smart_ptr_handle(), - prefix_code=self.prefix_code(), + return self.wrapper_templates["struct_enum_register"].substitute( generator_pre_code=( generator.get_class_cpp_pre_code(class_py_name) if generator else "" ), + class_py_name=class_py_name, + class_cpp_name=class_cpp_name, enum_name=enum_decl.name, enum_values=enum_values, ) def write(self, work_dir: str) -> None: """ - Write the hpp and cpp wrapper codes to file. + Write the hpp and cpp wrapper code to file. + + All of a class's template instantiations share a single + `{class}.cppwg.hpp` / `.cpp` pair. The cpp holds one shared preamble + followed by one registration block per instantiation. Parameters ---------- @@ -469,30 +516,75 @@ def write(self, work_dir: str) -> None: """ logger = logging.getLogger() - if len(self.class_info.decls) != len(self.class_info.cpp_names): - logger.error("Not enough class decls added to do write.") - raise AssertionError() - - for idx, class_py_name in enumerate(self.class_info.py_names): - class_decl = self.class_info.decls[idx] + # decls, cpp_names and py_names are parallel, one entry per instantiation, + # and the loop below indexes all three by the same position. Validate they + # are the same length up-front so a mismatch fails deterministically here + # rather than as an IndexError (or a mislabelled wrapper) mid-loop. + n_decls = len(self.class_info.decls) + n_cpp = len(self.class_info.cpp_names) + n_py = len(self.class_info.py_names) + if not n_decls == n_cpp == n_py: + message = ( + f"Class {self.class_info.name} has mismatched instantiation lists " + f"({n_decls} decls, {n_cpp} cpp_names, {n_py} py_names); they must " + "be kept in lockstep." + ) + logger.error(message) + raise AssertionError(message) + + register_blocks: list[str] = [] + register_py_names: list[str] = [] + class_typedefs: list[str] = [] + deduped_typedefs: list[str] = [] + seen_typedefs: set[str] = set() + + for idx, class_decl in enumerate(self.class_info.decls): + class_py_name = self.class_info.py_names[idx] + + # The alias typedef (typedef ;) for this instantiation, kept + # for the preamble so every block can refer to it (and every other + # instantiation) by its alias regardless of block order. + alias_typedef = "typedef {cpp_name} {py_name};\n".format( + cpp_name=self.class_info.cpp_names[idx], + py_name=class_py_name, + ) - # Check for struct-enum pattern. For example: - # struct Foo{ - # enum Value{A, B, C}; - # }; + # Check for the struct-enum pattern, e.g.: + # struct Foo { enum Value {A, B, C}; }; if type_traits_classes.is_struct(class_decl): enums = class_decl.enumerations(allow_empty=True) if len(enums) == 1: - self.cpp_string = self.build_struct_enum_cpp(idx) - self.hpp_string = self.build_hpp(class_py_name) - self.write_files(work_dir, class_py_name) + register_blocks.append(self.build_struct_enum_register(idx)) + register_py_names.append(class_py_name) + class_typedefs.append(alias_typedef) continue - self.cpp_string = self.build_class_cpp(idx) - self.hpp_string = self.build_hpp(class_py_name) - self.write_files(work_dir, class_py_name) + block, return_typedefs = self.build_class_register(idx) + register_blocks.append(block) + register_py_names.append(class_py_name) + class_typedefs.append(alias_typedef) + + # Hoist trampoline return typedefs into the shared preamble, + # deduplicated so a type shared by several instantiations (e.g. + # ::std::string) is not redefined. + for line in return_typedefs.splitlines(keepends=True): + if line not in seen_typedefs: + seen_typedefs.add(line) + deduped_typedefs.append(line) + + # Nothing to register (e.g. only structs that are not the single-enum + # pattern) - write no files, matching the previous behaviour. + if not register_blocks: + return + + self.hpp_string = self.build_hpp(register_py_names) + self.cpp_string = self.build_cpp_header( + "".join(class_typedefs), "".join(deduped_typedefs) + ) + self.cpp_string += "\n" + "\n".join(register_blocks) + self.write_files(work_dir, self.class_info.py_name_base()) - def write_files(self, work_dir: str, class_py_name: str) -> None: + def write_files(self, work_dir: str, file_stem: str) -> None: """ Write the hpp and cpp wrapper code to file. @@ -500,11 +592,12 @@ def write_files(self, work_dir: str, class_py_name: str) -> None: ---------- work_dir : str The directory to write the files to - class_py_name : str - The Python name of the class e.g. Foo_2_2 + file_stem : str + The wrapper file stem shared by all of the class's + instantiations, e.g. Foo (for Foo.cppwg.hpp / Foo.cppwg.cpp). """ - hpp_filepath = os.path.join(work_dir, f"{class_py_name}.{CPPWG_EXT}.hpp") - cpp_filepath = os.path.join(work_dir, f"{class_py_name}.{CPPWG_EXT}.cpp") + hpp_filepath = os.path.join(work_dir, f"{file_stem}.{CPPWG_EXT}.hpp") + cpp_filepath = os.path.join(work_dir, f"{file_stem}.{CPPWG_EXT}.cpp") write_file_if_changed(hpp_filepath, self.hpp_string, self.overwrite) write_file_if_changed(cpp_filepath, self.cpp_string, self.overwrite) diff --git a/cppwg/writers/module_writer.py b/cppwg/writers/module_writer.py index c79e81c..013aa2d 100644 --- a/cppwg/writers/module_writer.py +++ b/cppwg/writers/module_writer.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING from cppwg.utils.constants import CPPWG_EXT, CPPWG_HEADER_COLLECTION_FILENAME -from cppwg.utils.utils import write_file_if_changed +from cppwg.utils.utils import ensure_trailing_newline, write_file_if_changed from cppwg.writers.class_writer import CppClassWrapperWriter from cppwg.writers.free_function_writer import CppFreeFunctionWrapperWriter @@ -173,12 +173,12 @@ def build_module_context(self) -> dict[str, str]: else: includes = exception_block - # Includes for class wrappers in the module - # Example: #include "Foo_2_2.cppwg.hpp" + # Includes for class wrappers in the module. All of a class's template + # instantiations share one wrapper hpp (named after the class), so this + # is one include per class, e.g. #include "Foo.cppwg.hpp". class_includes = "".join( - f'#include "{py_name}.{CPPWG_EXT}.hpp"\n' + f'#include "{class_info.py_name_base()}.{CPPWG_EXT}.hpp"\n' for class_info in non_excluded_classes - for py_name in class_info.py_names ) # Import any modules that register externally-wrapped base classes, so @@ -212,7 +212,13 @@ def build_module_context(self) -> dict[str, str]: return { "prefix_text": prefix_block, "includes": includes, - "module_pre_code": (generator.get_module_pre_code() if generator else ""), + # Generator snippets carry no trailing-newline guarantee. module_pre_code + # is followed by the class #include lines and module_code by the module's + # closing brace, so normalise both to end with a newline (see + # ensure_trailing_newline) to avoid producing invalid C++. + "module_pre_code": ensure_trailing_newline( + generator.get_module_pre_code() if generator else "" + ), "class_includes": class_includes, "full_module_name": self.full_module_name, "imports": imports, @@ -221,7 +227,9 @@ def build_module_context(self) -> dict[str, str]: "exception_translator": self.generate_exception_translator(), "free_functions": free_functions, "register_calls": register_calls, - "module_code": generator.get_module_code() if generator else "", + "module_code": ensure_trailing_newline( + generator.get_module_code() if generator else "" + ), } def write_module_wrapper(self) -> None: @@ -265,12 +273,28 @@ def write_class_wrappers(self) -> None: """Write wrappers for classes in the module.""" logger = logging.getLogger() + seen_file_stems: dict[str, str] = {} for class_info in self.module_info.class_collection: # Skip excluded classes if class_info.excluded: logger.info(f"Skipping class {class_info.name}") continue + # Each class writes one wrapper file pair named after the class. Two + # classes sharing that name would overwrite each other's files (and + # collide on the register_..._class symbols), producing a broken + # output set that only fails later at compile/link. Fail fast instead. + file_stem = class_info.py_name_base() + if file_stem in seen_file_stems: + message = ( + f"Wrapper file name '{file_stem}.{CPPWG_EXT}.*' is used by both " + f"class {seen_file_stems[file_stem]} and class " + f"{class_info.name}. Give one of them a distinct name_override." + ) + logger.error(message) + raise ValueError(message) + seen_file_stems[file_stem] = class_info.name + logger.info(f"Generating wrappers for class {class_info.name}") class_writer = CppClassWrapperWriter( diff --git a/examples/cells/dynamic/wrappers/all/AbstractMesh_2_2.cppwg.cpp b/examples/cells/dynamic/wrappers/all/AbstractMesh.cppwg.cpp similarity index 54% rename from examples/cells/dynamic/wrappers/all/AbstractMesh_2_2.cppwg.cpp rename to examples/cells/dynamic/wrappers/all/AbstractMesh.cppwg.cpp index ed81b5f..e5b275e 100644 --- a/examples/cells/dynamic/wrappers/all/AbstractMesh_2_2.cppwg.cpp +++ b/examples/cells/dynamic/wrappers/all/AbstractMesh.cppwg.cpp @@ -5,11 +5,13 @@ #include #include "AbstractMesh.hpp" -#include "AbstractMesh_2_2.cppwg.hpp" +#include "AbstractMesh.cppwg.hpp" namespace py = pybind11; -typedef AbstractMesh<2, 2> AbstractMesh_2_2; PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); +typedef AbstractMesh<2, 2> AbstractMesh_2_2; +typedef AbstractMesh<3, 3> AbstractMesh_3_3; + class AbstractMesh_2_2_Overrides : public AbstractMesh_2_2 { @@ -43,3 +45,37 @@ void register_AbstractMesh_2_2_class(py::module &m) " ", py::arg("factor")) ; } + + +class AbstractMesh_3_3_Overrides : public AbstractMesh_3_3 +{ +public: + using AbstractMesh_3_3::AbstractMesh; + void Scale(double const factor) override + { + PYBIND11_OVERRIDE_PURE( + void, + AbstractMesh_3_3, + Scale, + factor); + } +}; + +void register_AbstractMesh_3_3_class(py::module &m) +{ + py::class_>(m, "AbstractMesh_3_3") + .def(py::init<>()) + .def("GetIndex", + (unsigned int(AbstractMesh_3_3::*)() const) &AbstractMesh_3_3::GetIndex, + " ") + .def("SetIndex", + (void(AbstractMesh_3_3::*)(unsigned int)) &AbstractMesh_3_3::SetIndex, + " ", py::arg("index")) + .def("AddNode", + (void(AbstractMesh_3_3::*)(::Node<3>)) &AbstractMesh_3_3::AddNode, + " ", py::arg("node")) + .def("Scale", + (void(AbstractMesh_3_3::*)(double const)) &AbstractMesh_3_3::Scale, + " ", py::arg("factor")) + ; +} diff --git a/examples/cells/dynamic/wrappers/all/AbstractMesh.cppwg.hpp b/examples/cells/dynamic/wrappers/all/AbstractMesh.cppwg.hpp new file mode 100644 index 0000000..986fc0a --- /dev/null +++ b/examples/cells/dynamic/wrappers/all/AbstractMesh.cppwg.hpp @@ -0,0 +1,10 @@ +// This file is auto-generated by cppwg; manual changes will be overwritten. + +#ifndef AbstractMesh_hpp__cppwg_wrapper +#define AbstractMesh_hpp__cppwg_wrapper + +#include + +void register_AbstractMesh_2_2_class(pybind11::module &m); +void register_AbstractMesh_3_3_class(pybind11::module &m); +#endif // AbstractMesh_hpp__cppwg_wrapper diff --git a/examples/cells/dynamic/wrappers/all/AbstractMesh_2_2.cppwg.hpp b/examples/cells/dynamic/wrappers/all/AbstractMesh_2_2.cppwg.hpp deleted file mode 100644 index c1503c6..0000000 --- a/examples/cells/dynamic/wrappers/all/AbstractMesh_2_2.cppwg.hpp +++ /dev/null @@ -1,9 +0,0 @@ -// This file is auto-generated by cppwg; manual changes will be overwritten. - -#ifndef AbstractMesh_2_2_hpp__cppwg_wrapper -#define AbstractMesh_2_2_hpp__cppwg_wrapper - -#include - -void register_AbstractMesh_2_2_class(pybind11::module &m); -#endif // AbstractMesh_2_2_hpp__cppwg_wrapper diff --git a/examples/cells/dynamic/wrappers/all/AbstractMesh_3_3.cppwg.cpp b/examples/cells/dynamic/wrappers/all/AbstractMesh_3_3.cppwg.cpp deleted file mode 100644 index c56583b..0000000 --- a/examples/cells/dynamic/wrappers/all/AbstractMesh_3_3.cppwg.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// This file is auto-generated by cppwg; manual changes will be overwritten. - -#include -#include -#include -#include "AbstractMesh.hpp" - -#include "AbstractMesh_3_3.cppwg.hpp" - -namespace py = pybind11; -typedef AbstractMesh<3, 3> AbstractMesh_3_3; -PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); - -class AbstractMesh_3_3_Overrides : public AbstractMesh_3_3 -{ -public: - using AbstractMesh_3_3::AbstractMesh; - void Scale(double const factor) override - { - PYBIND11_OVERRIDE_PURE( - void, - AbstractMesh_3_3, - Scale, - factor); - } -}; - -void register_AbstractMesh_3_3_class(py::module &m) -{ - py::class_>(m, "AbstractMesh_3_3") - .def(py::init<>()) - .def("GetIndex", - (unsigned int(AbstractMesh_3_3::*)() const) &AbstractMesh_3_3::GetIndex, - " ") - .def("SetIndex", - (void(AbstractMesh_3_3::*)(unsigned int)) &AbstractMesh_3_3::SetIndex, - " ", py::arg("index")) - .def("AddNode", - (void(AbstractMesh_3_3::*)(::Node<3>)) &AbstractMesh_3_3::AddNode, - " ", py::arg("node")) - .def("Scale", - (void(AbstractMesh_3_3::*)(double const)) &AbstractMesh_3_3::Scale, - " ", py::arg("factor")) - ; -} diff --git a/examples/cells/dynamic/wrappers/all/AbstractMesh_3_3.cppwg.hpp b/examples/cells/dynamic/wrappers/all/AbstractMesh_3_3.cppwg.hpp deleted file mode 100644 index 1b26ef7..0000000 --- a/examples/cells/dynamic/wrappers/all/AbstractMesh_3_3.cppwg.hpp +++ /dev/null @@ -1,9 +0,0 @@ -// This file is auto-generated by cppwg; manual changes will be overwritten. - -#ifndef AbstractMesh_3_3_hpp__cppwg_wrapper -#define AbstractMesh_3_3_hpp__cppwg_wrapper - -#include - -void register_AbstractMesh_3_3_class(pybind11::module &m); -#endif // AbstractMesh_3_3_hpp__cppwg_wrapper diff --git a/examples/cells/dynamic/wrappers/all/Cell.cppwg.cpp b/examples/cells/dynamic/wrappers/all/Cell.cppwg.cpp index f9ba7bb..0a5b28d 100644 --- a/examples/cells/dynamic/wrappers/all/Cell.cppwg.cpp +++ b/examples/cells/dynamic/wrappers/all/Cell.cppwg.cpp @@ -8,8 +8,9 @@ #include "Cell.cppwg.hpp" namespace py = pybind11; -typedef Cell Cell; PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); +typedef Cell Cell; + void register_Cell_class(py::module &m) { diff --git a/examples/cells/dynamic/wrappers/all/Corner_2.cppwg.cpp b/examples/cells/dynamic/wrappers/all/Corner.cppwg.cpp similarity index 97% rename from examples/cells/dynamic/wrappers/all/Corner_2.cppwg.cpp rename to examples/cells/dynamic/wrappers/all/Corner.cppwg.cpp index 6324ac6..eb30826 100644 --- a/examples/cells/dynamic/wrappers/all/Corner_2.cppwg.cpp +++ b/examples/cells/dynamic/wrappers/all/Corner.cppwg.cpp @@ -5,13 +5,14 @@ #include #include "Corner.hpp" -#include "Corner_2.cppwg.hpp" +#include "Corner.cppwg.hpp" namespace py = pybind11; -typedef Corner<2> Corner_2; PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); +typedef Corner<2> Corner_2; typedef unsigned int unsignedint; + class Corner_2_Overrides : public Corner_2 { public: diff --git a/examples/cells/dynamic/wrappers/all/Corner_2.cppwg.hpp b/examples/cells/dynamic/wrappers/all/Corner.cppwg.hpp similarity index 59% rename from examples/cells/dynamic/wrappers/all/Corner_2.cppwg.hpp rename to examples/cells/dynamic/wrappers/all/Corner.cppwg.hpp index 169a082..63bd85c 100644 --- a/examples/cells/dynamic/wrappers/all/Corner_2.cppwg.hpp +++ b/examples/cells/dynamic/wrappers/all/Corner.cppwg.hpp @@ -1,9 +1,9 @@ // This file is auto-generated by cppwg; manual changes will be overwritten. -#ifndef Corner_2_hpp__cppwg_wrapper -#define Corner_2_hpp__cppwg_wrapper +#ifndef Corner_hpp__cppwg_wrapper +#define Corner_hpp__cppwg_wrapper #include void register_Corner_2_class(pybind11::module &m); -#endif // Corner_2_hpp__cppwg_wrapper +#endif // Corner_hpp__cppwg_wrapper diff --git a/examples/cells/dynamic/wrappers/all/Facet_2.cppwg.cpp b/examples/cells/dynamic/wrappers/all/Facet.cppwg.cpp similarity index 97% rename from examples/cells/dynamic/wrappers/all/Facet_2.cppwg.cpp rename to examples/cells/dynamic/wrappers/all/Facet.cppwg.cpp index d4bd58e..b2d4257 100644 --- a/examples/cells/dynamic/wrappers/all/Facet_2.cppwg.cpp +++ b/examples/cells/dynamic/wrappers/all/Facet.cppwg.cpp @@ -5,13 +5,14 @@ #include #include "Facet.hpp" -#include "Facet_2.cppwg.hpp" +#include "Facet.cppwg.hpp" namespace py = pybind11; -typedef Facet<2> Facet_2; PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); +typedef Facet<2> Facet_2; typedef unsigned int unsignedint; + class Facet_2_Overrides : public Facet_2 { public: diff --git a/examples/cells/dynamic/wrappers/all/Facet_2.cppwg.hpp b/examples/cells/dynamic/wrappers/all/Facet.cppwg.hpp similarity index 60% rename from examples/cells/dynamic/wrappers/all/Facet_2.cppwg.hpp rename to examples/cells/dynamic/wrappers/all/Facet.cppwg.hpp index 111656e..1c6d72e 100644 --- a/examples/cells/dynamic/wrappers/all/Facet_2.cppwg.hpp +++ b/examples/cells/dynamic/wrappers/all/Facet.cppwg.hpp @@ -1,9 +1,9 @@ // This file is auto-generated by cppwg; manual changes will be overwritten. -#ifndef Facet_2_hpp__cppwg_wrapper -#define Facet_2_hpp__cppwg_wrapper +#ifndef Facet_hpp__cppwg_wrapper +#define Facet_hpp__cppwg_wrapper #include void register_Facet_2_class(pybind11::module &m); -#endif // Facet_2_hpp__cppwg_wrapper +#endif // Facet_hpp__cppwg_wrapper diff --git a/examples/cells/dynamic/wrappers/all/MacroMesh_2_2.cppwg.cpp b/examples/cells/dynamic/wrappers/all/MacroMesh.cppwg.cpp similarity index 61% rename from examples/cells/dynamic/wrappers/all/MacroMesh_2_2.cppwg.cpp rename to examples/cells/dynamic/wrappers/all/MacroMesh.cppwg.cpp index ab66d33..5e23264 100644 --- a/examples/cells/dynamic/wrappers/all/MacroMesh_2_2.cppwg.cpp +++ b/examples/cells/dynamic/wrappers/all/MacroMesh.cppwg.cpp @@ -5,11 +5,13 @@ #include #include "MacroMesh.hpp" -#include "MacroMesh_2_2.cppwg.hpp" +#include "MacroMesh.cppwg.hpp" namespace py = pybind11; -typedef MacroMesh<2, 2> MacroMesh_2_2; PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); +typedef MacroMesh<2, 2> MacroMesh_2_2; +typedef MacroMesh<3, 3> MacroMesh_3_3; + void register_MacroMesh_2_2_class(py::module &m) { @@ -20,3 +22,14 @@ void register_MacroMesh_2_2_class(py::module &m) " ") ; } + + +void register_MacroMesh_3_3_class(py::module &m) +{ + py::class_>(m, "MacroMesh_3_3") + .def(py::init<>()) + .def("GetDimension", + (unsigned int(MacroMesh_3_3::*)() const) &MacroMesh_3_3::GetDimension, + " ") + ; +} diff --git a/examples/cells/dynamic/wrappers/all/MacroMesh.cppwg.hpp b/examples/cells/dynamic/wrappers/all/MacroMesh.cppwg.hpp new file mode 100644 index 0000000..eae11f4 --- /dev/null +++ b/examples/cells/dynamic/wrappers/all/MacroMesh.cppwg.hpp @@ -0,0 +1,10 @@ +// This file is auto-generated by cppwg; manual changes will be overwritten. + +#ifndef MacroMesh_hpp__cppwg_wrapper +#define MacroMesh_hpp__cppwg_wrapper + +#include + +void register_MacroMesh_2_2_class(pybind11::module &m); +void register_MacroMesh_3_3_class(pybind11::module &m); +#endif // MacroMesh_hpp__cppwg_wrapper diff --git a/examples/cells/dynamic/wrappers/all/MacroMesh_2_2.cppwg.hpp b/examples/cells/dynamic/wrappers/all/MacroMesh_2_2.cppwg.hpp deleted file mode 100644 index 3e67411..0000000 --- a/examples/cells/dynamic/wrappers/all/MacroMesh_2_2.cppwg.hpp +++ /dev/null @@ -1,9 +0,0 @@ -// This file is auto-generated by cppwg; manual changes will be overwritten. - -#ifndef MacroMesh_2_2_hpp__cppwg_wrapper -#define MacroMesh_2_2_hpp__cppwg_wrapper - -#include - -void register_MacroMesh_2_2_class(pybind11::module &m); -#endif // MacroMesh_2_2_hpp__cppwg_wrapper diff --git a/examples/cells/dynamic/wrappers/all/MacroMesh_3_3.cppwg.cpp b/examples/cells/dynamic/wrappers/all/MacroMesh_3_3.cppwg.cpp deleted file mode 100644 index 3220bf6..0000000 --- a/examples/cells/dynamic/wrappers/all/MacroMesh_3_3.cppwg.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// This file is auto-generated by cppwg; manual changes will be overwritten. - -#include -#include -#include -#include "MacroMesh.hpp" - -#include "MacroMesh_3_3.cppwg.hpp" - -namespace py = pybind11; -typedef MacroMesh<3, 3> MacroMesh_3_3; -PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); - -void register_MacroMesh_3_3_class(py::module &m) -{ - py::class_>(m, "MacroMesh_3_3") - .def(py::init<>()) - .def("GetDimension", - (unsigned int(MacroMesh_3_3::*)() const) &MacroMesh_3_3::GetDimension, - " ") - ; -} diff --git a/examples/cells/dynamic/wrappers/all/MacroMesh_3_3.cppwg.hpp b/examples/cells/dynamic/wrappers/all/MacroMesh_3_3.cppwg.hpp deleted file mode 100644 index bee7adc..0000000 --- a/examples/cells/dynamic/wrappers/all/MacroMesh_3_3.cppwg.hpp +++ /dev/null @@ -1,9 +0,0 @@ -// This file is auto-generated by cppwg; manual changes will be overwritten. - -#ifndef MacroMesh_3_3_hpp__cppwg_wrapper -#define MacroMesh_3_3_hpp__cppwg_wrapper - -#include - -void register_MacroMesh_3_3_class(pybind11::module &m); -#endif // MacroMesh_3_3_hpp__cppwg_wrapper diff --git a/examples/cells/dynamic/wrappers/all/MeshFactory_PottsMesh_2.cppwg.cpp b/examples/cells/dynamic/wrappers/all/MeshFactory.cppwg.cpp similarity index 60% rename from examples/cells/dynamic/wrappers/all/MeshFactory_PottsMesh_2.cppwg.cpp rename to examples/cells/dynamic/wrappers/all/MeshFactory.cppwg.cpp index 3147611..5171e2d 100644 --- a/examples/cells/dynamic/wrappers/all/MeshFactory_PottsMesh_2.cppwg.cpp +++ b/examples/cells/dynamic/wrappers/all/MeshFactory.cppwg.cpp @@ -6,11 +6,13 @@ #include #include "MeshFactory.hpp" -#include "MeshFactory_PottsMesh_2.cppwg.hpp" +#include "MeshFactory.cppwg.hpp" namespace py = pybind11; -typedef MeshFactory> MeshFactory_PottsMesh_2; PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); +typedef MeshFactory> MeshFactory_PottsMesh_2; +typedef MeshFactory> MeshFactory_PottsMesh_3; + void register_MeshFactory_PottsMesh_2_class(py::module &m) { @@ -21,3 +23,14 @@ void register_MeshFactory_PottsMesh_2_class(py::module &m) " ") ; } + + +void register_MeshFactory_PottsMesh_3_class(py::module &m) +{ + py::class_>(m, "MeshFactory_PottsMesh_3") + .def(py::init<>()) + .def("generateMesh", + (::std::shared_ptr>(MeshFactory_PottsMesh_3::*)()) &MeshFactory_PottsMesh_3::generateMesh, + " ") + ; +} diff --git a/examples/cells/dynamic/wrappers/all/MeshFactory.cppwg.hpp b/examples/cells/dynamic/wrappers/all/MeshFactory.cppwg.hpp new file mode 100644 index 0000000..1ec8792 --- /dev/null +++ b/examples/cells/dynamic/wrappers/all/MeshFactory.cppwg.hpp @@ -0,0 +1,10 @@ +// This file is auto-generated by cppwg; manual changes will be overwritten. + +#ifndef MeshFactory_hpp__cppwg_wrapper +#define MeshFactory_hpp__cppwg_wrapper + +#include + +void register_MeshFactory_PottsMesh_2_class(pybind11::module &m); +void register_MeshFactory_PottsMesh_3_class(pybind11::module &m); +#endif // MeshFactory_hpp__cppwg_wrapper diff --git a/examples/cells/dynamic/wrappers/all/MeshFactory_PottsMesh_2.cppwg.hpp b/examples/cells/dynamic/wrappers/all/MeshFactory_PottsMesh_2.cppwg.hpp deleted file mode 100644 index 28dc4ad..0000000 --- a/examples/cells/dynamic/wrappers/all/MeshFactory_PottsMesh_2.cppwg.hpp +++ /dev/null @@ -1,9 +0,0 @@ -// This file is auto-generated by cppwg; manual changes will be overwritten. - -#ifndef MeshFactory_PottsMesh_2_hpp__cppwg_wrapper -#define MeshFactory_PottsMesh_2_hpp__cppwg_wrapper - -#include - -void register_MeshFactory_PottsMesh_2_class(pybind11::module &m); -#endif // MeshFactory_PottsMesh_2_hpp__cppwg_wrapper diff --git a/examples/cells/dynamic/wrappers/all/MeshFactory_PottsMesh_3.cppwg.cpp b/examples/cells/dynamic/wrappers/all/MeshFactory_PottsMesh_3.cppwg.cpp deleted file mode 100644 index 2a8b1dc..0000000 --- a/examples/cells/dynamic/wrappers/all/MeshFactory_PottsMesh_3.cppwg.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// This file is auto-generated by cppwg; manual changes will be overwritten. - -#include -#include -#include "PottsMesh.hpp" -#include -#include "MeshFactory.hpp" - -#include "MeshFactory_PottsMesh_3.cppwg.hpp" - -namespace py = pybind11; -typedef MeshFactory> MeshFactory_PottsMesh_3; -PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); - -void register_MeshFactory_PottsMesh_3_class(py::module &m) -{ - py::class_>(m, "MeshFactory_PottsMesh_3") - .def(py::init<>()) - .def("generateMesh", - (::std::shared_ptr>(MeshFactory_PottsMesh_3::*)()) &MeshFactory_PottsMesh_3::generateMesh, - " ") - ; -} diff --git a/examples/cells/dynamic/wrappers/all/MeshFactory_PottsMesh_3.cppwg.hpp b/examples/cells/dynamic/wrappers/all/MeshFactory_PottsMesh_3.cppwg.hpp deleted file mode 100644 index eb7de7c..0000000 --- a/examples/cells/dynamic/wrappers/all/MeshFactory_PottsMesh_3.cppwg.hpp +++ /dev/null @@ -1,9 +0,0 @@ -// This file is auto-generated by cppwg; manual changes will be overwritten. - -#ifndef MeshFactory_PottsMesh_3_hpp__cppwg_wrapper -#define MeshFactory_PottsMesh_3_hpp__cppwg_wrapper - -#include - -void register_MeshFactory_PottsMesh_3_class(pybind11::module &m); -#endif // MeshFactory_PottsMesh_3_hpp__cppwg_wrapper diff --git a/examples/cells/dynamic/wrappers/all/Node_2.cppwg.cpp b/examples/cells/dynamic/wrappers/all/Node.cppwg.cpp similarity index 57% rename from examples/cells/dynamic/wrappers/all/Node_2.cppwg.cpp rename to examples/cells/dynamic/wrappers/all/Node.cppwg.cpp index 3db7b00..77b7268 100644 --- a/examples/cells/dynamic/wrappers/all/Node_2.cppwg.cpp +++ b/examples/cells/dynamic/wrappers/all/Node.cppwg.cpp @@ -6,11 +6,13 @@ #include #include "Node.hpp" -#include "Node_2.cppwg.hpp" +#include "Node.cppwg.hpp" namespace py = pybind11; -typedef Node<2> Node_2; PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); +typedef Node<2> Node_2; +typedef Node<3> Node_3; + void register_Node_2_class(py::module &m) { @@ -29,3 +31,22 @@ void register_Node_2_class(py::module &m) " ", py::arg("rDisplacement")) ; } + + +void register_Node_3_class(py::module &m) +{ + py::class_>(m, "Node_3") + .def(py::init<>()) + .def(py::init<::std::vector>(), py::arg("coords")) + .def(py::init<::boost::numeric::ublas::c_vector>(), py::arg("coords")) + .def("GetIndex", + (unsigned int(Node_3::*)() const) &Node_3::GetIndex, + " ") + .def("GetLocation", + (::boost::numeric::ublas::c_vector(Node_3::*)()) &Node_3::GetLocation, + " ") + .def("Translate", + (void(Node_3::*)(::boost::numeric::ublas::c_vector const &)) &Node_3::Translate, + " ", py::arg("rDisplacement")) + ; +} diff --git a/examples/cells/dynamic/wrappers/all/Node_2.cppwg.hpp b/examples/cells/dynamic/wrappers/all/Node.cppwg.hpp similarity index 52% rename from examples/cells/dynamic/wrappers/all/Node_2.cppwg.hpp rename to examples/cells/dynamic/wrappers/all/Node.cppwg.hpp index 35b1aeb..15dfdfb 100644 --- a/examples/cells/dynamic/wrappers/all/Node_2.cppwg.hpp +++ b/examples/cells/dynamic/wrappers/all/Node.cppwg.hpp @@ -1,9 +1,10 @@ // This file is auto-generated by cppwg; manual changes will be overwritten. -#ifndef Node_2_hpp__cppwg_wrapper -#define Node_2_hpp__cppwg_wrapper +#ifndef Node_hpp__cppwg_wrapper +#define Node_hpp__cppwg_wrapper #include void register_Node_2_class(pybind11::module &m); -#endif // Node_2_hpp__cppwg_wrapper +void register_Node_3_class(pybind11::module &m); +#endif // Node_hpp__cppwg_wrapper diff --git a/examples/cells/dynamic/wrappers/all/Node_3.cppwg.cpp b/examples/cells/dynamic/wrappers/all/Node_3.cppwg.cpp deleted file mode 100644 index d6149b9..0000000 --- a/examples/cells/dynamic/wrappers/all/Node_3.cppwg.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// This file is auto-generated by cppwg; manual changes will be overwritten. - -#include -#include -#include "PybindUblasTypeCaster.hpp" -#include -#include "Node.hpp" - -#include "Node_3.cppwg.hpp" - -namespace py = pybind11; -typedef Node<3> Node_3; -PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); - -void register_Node_3_class(py::module &m) -{ - py::class_>(m, "Node_3") - .def(py::init<>()) - .def(py::init<::std::vector>(), py::arg("coords")) - .def(py::init<::boost::numeric::ublas::c_vector>(), py::arg("coords")) - .def("GetIndex", - (unsigned int(Node_3::*)() const) &Node_3::GetIndex, - " ") - .def("GetLocation", - (::boost::numeric::ublas::c_vector(Node_3::*)()) &Node_3::GetLocation, - " ") - .def("Translate", - (void(Node_3::*)(::boost::numeric::ublas::c_vector const &)) &Node_3::Translate, - " ", py::arg("rDisplacement")) - ; -} diff --git a/examples/cells/dynamic/wrappers/all/Node_3.cppwg.hpp b/examples/cells/dynamic/wrappers/all/Node_3.cppwg.hpp deleted file mode 100644 index 0c2935d..0000000 --- a/examples/cells/dynamic/wrappers/all/Node_3.cppwg.hpp +++ /dev/null @@ -1,9 +0,0 @@ -// This file is auto-generated by cppwg; manual changes will be overwritten. - -#ifndef Node_3_hpp__cppwg_wrapper -#define Node_3_hpp__cppwg_wrapper - -#include - -void register_Node_3_class(pybind11::module &m); -#endif // Node_3_hpp__cppwg_wrapper diff --git a/examples/cells/dynamic/wrappers/all/PetscUtils.cppwg.cpp b/examples/cells/dynamic/wrappers/all/PetscUtils.cppwg.cpp index 5f0e9bf..c6f7e94 100644 --- a/examples/cells/dynamic/wrappers/all/PetscUtils.cppwg.cpp +++ b/examples/cells/dynamic/wrappers/all/PetscUtils.cppwg.cpp @@ -9,8 +9,9 @@ #include "PetscUtils.cppwg.hpp" namespace py = pybind11; -typedef PetscUtils PetscUtils; PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); +typedef PetscUtils PetscUtils; + void register_PetscUtils_class(py::module &m) { diff --git a/examples/cells/dynamic/wrappers/all/PottsMesh_2.cppwg.cpp b/examples/cells/dynamic/wrappers/all/PottsMesh.cppwg.cpp similarity index 57% rename from examples/cells/dynamic/wrappers/all/PottsMesh_2.cppwg.cpp rename to examples/cells/dynamic/wrappers/all/PottsMesh.cppwg.cpp index 21a2ede..43d7756 100644 --- a/examples/cells/dynamic/wrappers/all/PottsMesh_2.cppwg.cpp +++ b/examples/cells/dynamic/wrappers/all/PottsMesh.cppwg.cpp @@ -5,11 +5,13 @@ #include #include "PottsMesh.hpp" -#include "PottsMesh_2.cppwg.hpp" +#include "PottsMesh.cppwg.hpp" namespace py = pybind11; -typedef PottsMesh<2> PottsMesh_2; PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); +typedef PottsMesh<2> PottsMesh_2; +typedef PottsMesh<3> PottsMesh_3; + class PottsMesh_2_Overrides : public PottsMesh_2 { @@ -34,3 +36,28 @@ void register_PottsMesh_2_class(py::module &m) " ", py::arg("factor")) ; } + + +class PottsMesh_3_Overrides : public PottsMesh_3 +{ +public: + using PottsMesh_3::PottsMesh; + void Scale(double const factor) override + { + PYBIND11_OVERRIDE( + void, + PottsMesh_3, + Scale, + factor); + } +}; + +void register_PottsMesh_3_class(py::module &m) +{ + py::class_, AbstractMesh<3, 3>>(m, "PottsMesh_3") + .def(py::init<>()) + .def("Scale", + (void(PottsMesh_3::*)(double const)) &PottsMesh_3::Scale, + " ", py::arg("factor")) + ; +} diff --git a/examples/cells/dynamic/wrappers/all/PottsMesh.cppwg.hpp b/examples/cells/dynamic/wrappers/all/PottsMesh.cppwg.hpp new file mode 100644 index 0000000..4f1cc7f --- /dev/null +++ b/examples/cells/dynamic/wrappers/all/PottsMesh.cppwg.hpp @@ -0,0 +1,10 @@ +// This file is auto-generated by cppwg; manual changes will be overwritten. + +#ifndef PottsMesh_hpp__cppwg_wrapper +#define PottsMesh_hpp__cppwg_wrapper + +#include + +void register_PottsMesh_2_class(pybind11::module &m); +void register_PottsMesh_3_class(pybind11::module &m); +#endif // PottsMesh_hpp__cppwg_wrapper diff --git a/examples/cells/dynamic/wrappers/all/PottsMesh_2.cppwg.hpp b/examples/cells/dynamic/wrappers/all/PottsMesh_2.cppwg.hpp deleted file mode 100644 index 30615bd..0000000 --- a/examples/cells/dynamic/wrappers/all/PottsMesh_2.cppwg.hpp +++ /dev/null @@ -1,9 +0,0 @@ -// This file is auto-generated by cppwg; manual changes will be overwritten. - -#ifndef PottsMesh_2_hpp__cppwg_wrapper -#define PottsMesh_2_hpp__cppwg_wrapper - -#include - -void register_PottsMesh_2_class(pybind11::module &m); -#endif // PottsMesh_2_hpp__cppwg_wrapper diff --git a/examples/cells/dynamic/wrappers/all/PottsMesh_3.cppwg.cpp b/examples/cells/dynamic/wrappers/all/PottsMesh_3.cppwg.cpp deleted file mode 100644 index 408c8bb..0000000 --- a/examples/cells/dynamic/wrappers/all/PottsMesh_3.cppwg.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// This file is auto-generated by cppwg; manual changes will be overwritten. - -#include -#include -#include -#include "PottsMesh.hpp" - -#include "PottsMesh_3.cppwg.hpp" - -namespace py = pybind11; -typedef PottsMesh<3> PottsMesh_3; -PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); - -class PottsMesh_3_Overrides : public PottsMesh_3 -{ -public: - using PottsMesh_3::PottsMesh; - void Scale(double const factor) override - { - PYBIND11_OVERRIDE( - void, - PottsMesh_3, - Scale, - factor); - } -}; - -void register_PottsMesh_3_class(py::module &m) -{ - py::class_, AbstractMesh<3, 3>>(m, "PottsMesh_3") - .def(py::init<>()) - .def("Scale", - (void(PottsMesh_3::*)(double const)) &PottsMesh_3::Scale, - " ", py::arg("factor")) - ; -} diff --git a/examples/cells/dynamic/wrappers/all/PottsMesh_3.cppwg.hpp b/examples/cells/dynamic/wrappers/all/PottsMesh_3.cppwg.hpp deleted file mode 100644 index 51ee08f..0000000 --- a/examples/cells/dynamic/wrappers/all/PottsMesh_3.cppwg.hpp +++ /dev/null @@ -1,9 +0,0 @@ -// This file is auto-generated by cppwg; manual changes will be overwritten. - -#ifndef PottsMesh_3_hpp__cppwg_wrapper -#define PottsMesh_3_hpp__cppwg_wrapper - -#include - -void register_PottsMesh_3_class(pybind11::module &m); -#endif // PottsMesh_3_hpp__cppwg_wrapper diff --git a/examples/cells/dynamic/wrappers/all/Scene_2.cppwg.cpp b/examples/cells/dynamic/wrappers/all/Scene.cppwg.cpp similarity index 61% rename from examples/cells/dynamic/wrappers/all/Scene_2.cppwg.cpp rename to examples/cells/dynamic/wrappers/all/Scene.cppwg.cpp index d03a68f..66cd5d8 100644 --- a/examples/cells/dynamic/wrappers/all/Scene_2.cppwg.cpp +++ b/examples/cells/dynamic/wrappers/all/Scene.cppwg.cpp @@ -6,11 +6,13 @@ #include #include "Scene.hpp" -#include "Scene_2.cppwg.hpp" +#include "Scene.cppwg.hpp" namespace py = pybind11; -typedef Scene<2> Scene_2; PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); +typedef Scene<2> Scene_2; +typedef Scene<3> Scene_3; + void register_Scene_2_class(py::module &m) { @@ -24,3 +26,17 @@ void register_Scene_2_class(py::module &m) " ") ; } + + +void register_Scene_3_class(py::module &m) +{ + py::class_>(m, "Scene_3") + .def(py::init<>()) + .def("GetRenderer", + (::vtkSmartPointer(Scene_3::*)()) &Scene_3::GetRenderer, + " ") + .def_static("ThrowException", + (void(*)()) &Scene_3::ThrowException, + " ") + ; +} diff --git a/examples/cells/dynamic/wrappers/all/Scene_2.cppwg.hpp b/examples/cells/dynamic/wrappers/all/Scene.cppwg.hpp similarity index 51% rename from examples/cells/dynamic/wrappers/all/Scene_2.cppwg.hpp rename to examples/cells/dynamic/wrappers/all/Scene.cppwg.hpp index bfca3fc..4051624 100644 --- a/examples/cells/dynamic/wrappers/all/Scene_2.cppwg.hpp +++ b/examples/cells/dynamic/wrappers/all/Scene.cppwg.hpp @@ -1,9 +1,10 @@ // This file is auto-generated by cppwg; manual changes will be overwritten. -#ifndef Scene_2_hpp__cppwg_wrapper -#define Scene_2_hpp__cppwg_wrapper +#ifndef Scene_hpp__cppwg_wrapper +#define Scene_hpp__cppwg_wrapper #include void register_Scene_2_class(pybind11::module &m); -#endif // Scene_2_hpp__cppwg_wrapper +void register_Scene_3_class(pybind11::module &m); +#endif // Scene_hpp__cppwg_wrapper diff --git a/examples/cells/dynamic/wrappers/all/Scene_3.cppwg.cpp b/examples/cells/dynamic/wrappers/all/Scene_3.cppwg.cpp deleted file mode 100644 index 6a8c433..0000000 --- a/examples/cells/dynamic/wrappers/all/Scene_3.cppwg.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// This file is auto-generated by cppwg; manual changes will be overwritten. - -#include -#include -#include "PybindVTKTypeCaster.h" -#include -#include "Scene.hpp" - -#include "Scene_3.cppwg.hpp" - -namespace py = pybind11; -typedef Scene<3> Scene_3; -PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); - -void register_Scene_3_class(py::module &m) -{ - py::class_>(m, "Scene_3") - .def(py::init<>()) - .def("GetRenderer", - (::vtkSmartPointer(Scene_3::*)()) &Scene_3::GetRenderer, - " ") - .def_static("ThrowException", - (void(*)()) &Scene_3::ThrowException, - " ") - ; -} diff --git a/examples/cells/dynamic/wrappers/all/Scene_3.cppwg.hpp b/examples/cells/dynamic/wrappers/all/Scene_3.cppwg.hpp deleted file mode 100644 index b9c477c..0000000 --- a/examples/cells/dynamic/wrappers/all/Scene_3.cppwg.hpp +++ /dev/null @@ -1,9 +0,0 @@ -// This file is auto-generated by cppwg; manual changes will be overwritten. - -#ifndef Scene_3_hpp__cppwg_wrapper -#define Scene_3_hpp__cppwg_wrapper - -#include - -void register_Scene_3_class(pybind11::module &m); -#endif // Scene_3_hpp__cppwg_wrapper diff --git a/examples/cells/dynamic/wrappers/all/_pycells_all.main.cppwg.cpp b/examples/cells/dynamic/wrappers/all/_pycells_all.main.cppwg.cpp index 5ae7d8f..b325c7a 100644 --- a/examples/cells/dynamic/wrappers/all/_pycells_all.main.cppwg.cpp +++ b/examples/cells/dynamic/wrappers/all/_pycells_all.main.cppwg.cpp @@ -9,21 +9,15 @@ #pragma GCC visibility pop #endif #include "Cell.cppwg.hpp" -#include "Corner_2.cppwg.hpp" -#include "Facet_2.cppwg.hpp" -#include "MacroMesh_2_2.cppwg.hpp" -#include "MacroMesh_3_3.cppwg.hpp" -#include "Node_2.cppwg.hpp" -#include "Node_3.cppwg.hpp" -#include "AbstractMesh_2_2.cppwg.hpp" -#include "AbstractMesh_3_3.cppwg.hpp" +#include "Corner.cppwg.hpp" +#include "Facet.cppwg.hpp" +#include "MacroMesh.cppwg.hpp" +#include "Node.cppwg.hpp" +#include "AbstractMesh.cppwg.hpp" #include "PetscUtils.cppwg.hpp" -#include "PottsMesh_2.cppwg.hpp" -#include "PottsMesh_3.cppwg.hpp" -#include "MeshFactory_PottsMesh_2.cppwg.hpp" -#include "MeshFactory_PottsMesh_3.cppwg.hpp" -#include "Scene_2.cppwg.hpp" -#include "Scene_3.cppwg.hpp" +#include "PottsMesh.cppwg.hpp" +#include "MeshFactory.cppwg.hpp" +#include "Scene.cppwg.hpp" namespace py = pybind11; diff --git a/examples/shapes/wrapper/composites/Square.cppwg.cpp b/examples/shapes/wrapper/composites/Square.cppwg.cpp index cbdc630..b657ca5 100644 --- a/examples/shapes/wrapper/composites/Square.cppwg.cpp +++ b/examples/shapes/wrapper/composites/Square.cppwg.cpp @@ -8,8 +8,9 @@ #include "Square.cppwg.hpp" namespace py = pybind11; -typedef Square Square; PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); +typedef Square Square; + void register_Square_class(py::module &m) { diff --git a/examples/shapes/wrapper/geometry/Point_2.cppwg.cpp b/examples/shapes/wrapper/geometry/Point.cppwg.cpp similarity index 55% rename from examples/shapes/wrapper/geometry/Point_2.cppwg.cpp rename to examples/shapes/wrapper/geometry/Point.cppwg.cpp index 190020d..4a1f9a7 100644 --- a/examples/shapes/wrapper/geometry/Point_2.cppwg.cpp +++ b/examples/shapes/wrapper/geometry/Point.cppwg.cpp @@ -5,11 +5,13 @@ #include #include "wrapper_header_collection.cppwg.hpp" -#include "Point_2.cppwg.hpp" +#include "Point.cppwg.hpp" namespace py = pybind11; -typedef Point<2> Point_2; PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); +typedef Point<2> Point_2; +typedef Point<3> Point_3; + void register_Point_2_class(py::module &m) { @@ -33,3 +35,27 @@ void register_Point_2_class(py::module &m) " ", py::arg("rLocation")) ; } + + +void register_Point_3_class(py::module &m) +{ + py::class_>(m, "Point_3") + .def(py::init<>()) + .def(py::init(), py::arg("x"), py::arg("y"), py::arg("z") = (3 - 3)) + .def("GetLocation", + (::std::array(Point_3::*)() const) &Point_3::GetLocation, + " ") + .def("rGetLocation", + (::std::array const &(Point_3::*)() const) &Point_3::rGetLocation, + " ", py::return_value_policy::reference_internal) + .def("GetIndex", + (unsigned int(Point_3::*)() const) &Point_3::GetIndex, + " ") + .def("SetIndex", + (void(Point_3::*)(unsigned int)) &Point_3::SetIndex, + " ", py::arg("index")) + .def("SetLocation", + (void(Point_3::*)(::std::array const &)) &Point_3::SetLocation, + " ", py::arg("rLocation")) + ; +} diff --git a/examples/shapes/wrapper/geometry/Point_2.cppwg.hpp b/examples/shapes/wrapper/geometry/Point.cppwg.hpp similarity index 53% rename from examples/shapes/wrapper/geometry/Point_2.cppwg.hpp rename to examples/shapes/wrapper/geometry/Point.cppwg.hpp index 7b5e0ca..4bc0af5 100644 --- a/examples/shapes/wrapper/geometry/Point_2.cppwg.hpp +++ b/examples/shapes/wrapper/geometry/Point.cppwg.hpp @@ -1,10 +1,11 @@ // This file is automatically generated by cppwg. // Do not modify this file directly. -#ifndef Point_2_hpp__cppwg_wrapper -#define Point_2_hpp__cppwg_wrapper +#ifndef Point_hpp__cppwg_wrapper +#define Point_hpp__cppwg_wrapper #include void register_Point_2_class(pybind11::module &m); -#endif // Point_2_hpp__cppwg_wrapper +void register_Point_3_class(pybind11::module &m); +#endif // Point_hpp__cppwg_wrapper diff --git a/examples/shapes/wrapper/geometry/Point_3.cppwg.cpp b/examples/shapes/wrapper/geometry/Point_3.cppwg.cpp deleted file mode 100644 index 24c8bf7..0000000 --- a/examples/shapes/wrapper/geometry/Point_3.cppwg.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// This file is automatically generated by cppwg. -// Do not modify this file directly. - -#include -#include -#include "wrapper_header_collection.cppwg.hpp" - -#include "Point_3.cppwg.hpp" - -namespace py = pybind11; -typedef Point<3> Point_3; -PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); - -void register_Point_3_class(py::module &m) -{ - py::class_>(m, "Point_3") - .def(py::init<>()) - .def(py::init(), py::arg("x"), py::arg("y"), py::arg("z") = (3 - 3)) - .def("GetLocation", - (::std::array(Point_3::*)() const) &Point_3::GetLocation, - " ") - .def("rGetLocation", - (::std::array const &(Point_3::*)() const) &Point_3::rGetLocation, - " ", py::return_value_policy::reference_internal) - .def("GetIndex", - (unsigned int(Point_3::*)() const) &Point_3::GetIndex, - " ") - .def("SetIndex", - (void(Point_3::*)(unsigned int)) &Point_3::SetIndex, - " ", py::arg("index")) - .def("SetLocation", - (void(Point_3::*)(::std::array const &)) &Point_3::SetLocation, - " ", py::arg("rLocation")) - ; -} diff --git a/examples/shapes/wrapper/geometry/Point_3.cppwg.hpp b/examples/shapes/wrapper/geometry/Point_3.cppwg.hpp deleted file mode 100644 index 4acc0ec..0000000 --- a/examples/shapes/wrapper/geometry/Point_3.cppwg.hpp +++ /dev/null @@ -1,10 +0,0 @@ -// This file is automatically generated by cppwg. -// Do not modify this file directly. - -#ifndef Point_3_hpp__cppwg_wrapper -#define Point_3_hpp__cppwg_wrapper - -#include - -void register_Point_3_class(pybind11::module &m); -#endif // Point_3_hpp__cppwg_wrapper diff --git a/examples/shapes/wrapper/geometry/_pyshapes_geometry.main.cppwg.cpp b/examples/shapes/wrapper/geometry/_pyshapes_geometry.main.cppwg.cpp index cc6aaba..487f31f 100644 --- a/examples/shapes/wrapper/geometry/_pyshapes_geometry.main.cppwg.cpp +++ b/examples/shapes/wrapper/geometry/_pyshapes_geometry.main.cppwg.cpp @@ -10,8 +10,7 @@ #pragma GCC visibility pop #endif #include "wrapper_header_collection.cppwg.hpp" -#include "Point_2.cppwg.hpp" -#include "Point_3.cppwg.hpp" +#include "Point.cppwg.hpp" namespace py = pybind11; diff --git a/examples/shapes/wrapper/primitives/Cuboid.cppwg.cpp b/examples/shapes/wrapper/primitives/Cuboid.cppwg.cpp index 8927818..a1b37ad 100644 --- a/examples/shapes/wrapper/primitives/Cuboid.cppwg.cpp +++ b/examples/shapes/wrapper/primitives/Cuboid.cppwg.cpp @@ -8,8 +8,9 @@ #include "Cuboid.cppwg.hpp" namespace py = pybind11; -typedef Cuboid Cuboid; PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); +typedef Cuboid Cuboid; + void register_Cuboid_class(py::module &m) { diff --git a/examples/shapes/wrapper/primitives/Rectangle.cppwg.cpp b/examples/shapes/wrapper/primitives/Rectangle.cppwg.cpp index 2cc8a68..e773936 100644 --- a/examples/shapes/wrapper/primitives/Rectangle.cppwg.cpp +++ b/examples/shapes/wrapper/primitives/Rectangle.cppwg.cpp @@ -8,8 +8,9 @@ #include "Rectangle.cppwg.hpp" namespace py = pybind11; -typedef Rectangle Rectangle; PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); +typedef Rectangle Rectangle; + void register_Rectangle_class(py::module &m) { diff --git a/examples/shapes/wrapper/primitives/Shape_2.cppwg.cpp b/examples/shapes/wrapper/primitives/Shape.cppwg.cpp similarity index 55% rename from examples/shapes/wrapper/primitives/Shape_2.cppwg.cpp rename to examples/shapes/wrapper/primitives/Shape.cppwg.cpp index 680e76d..be89755 100644 --- a/examples/shapes/wrapper/primitives/Shape_2.cppwg.cpp +++ b/examples/shapes/wrapper/primitives/Shape.cppwg.cpp @@ -5,11 +5,13 @@ #include #include "wrapper_header_collection.cppwg.hpp" -#include "Shape_2.cppwg.hpp" +#include "Shape.cppwg.hpp" namespace py = pybind11; -typedef Shape<2> Shape_2; PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); +typedef Shape<2> Shape_2; +typedef Shape<3> Shape_3; + void register_Shape_2_class(py::module &m) { @@ -32,3 +34,26 @@ void register_Shape_2_class(py::module &m) " ", py::arg("point") = std::make_shared>()) ; } + + +void register_Shape_3_class(py::module &m) +{ + py::class_>(m, "Shape_3") + .def(py::init<>()) + .def("GetIndex", + (unsigned int(Shape_3::*)() const) &Shape_3::GetIndex, + " ") + .def("rGetVertices", + (::std::vector>> const &(Shape_3::*)() const) &Shape_3::rGetVertices, + " ", py::return_value_policy::reference_internal) + .def("SetIndex", + (void(Shape_3::*)(unsigned int)) &Shape_3::SetIndex, + " ", py::arg("index")) + .def("SetVertices", + (void(Shape_3::*)(::std::vector>> const &)) &Shape_3::SetVertices, + " ", py::arg("rVertices")) + .def("AddVertex", + (void(Shape_3::*)(::std::shared_ptr>)) &Shape_3::AddVertex, + " ", py::arg("point") = std::make_shared>()) + ; +} diff --git a/examples/shapes/wrapper/primitives/Shape_2.cppwg.hpp b/examples/shapes/wrapper/primitives/Shape.cppwg.hpp similarity index 53% rename from examples/shapes/wrapper/primitives/Shape_2.cppwg.hpp rename to examples/shapes/wrapper/primitives/Shape.cppwg.hpp index 0898be1..91ccece 100644 --- a/examples/shapes/wrapper/primitives/Shape_2.cppwg.hpp +++ b/examples/shapes/wrapper/primitives/Shape.cppwg.hpp @@ -1,10 +1,11 @@ // This file is automatically generated by cppwg. // Do not modify this file directly. -#ifndef Shape_2_hpp__cppwg_wrapper -#define Shape_2_hpp__cppwg_wrapper +#ifndef Shape_hpp__cppwg_wrapper +#define Shape_hpp__cppwg_wrapper #include void register_Shape_2_class(pybind11::module &m); -#endif // Shape_2_hpp__cppwg_wrapper +void register_Shape_3_class(pybind11::module &m); +#endif // Shape_hpp__cppwg_wrapper diff --git a/examples/shapes/wrapper/primitives/Shape_3.cppwg.cpp b/examples/shapes/wrapper/primitives/Shape_3.cppwg.cpp deleted file mode 100644 index b775a6e..0000000 --- a/examples/shapes/wrapper/primitives/Shape_3.cppwg.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// This file is automatically generated by cppwg. -// Do not modify this file directly. - -#include -#include -#include "wrapper_header_collection.cppwg.hpp" - -#include "Shape_3.cppwg.hpp" - -namespace py = pybind11; -typedef Shape<3> Shape_3; -PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); - -void register_Shape_3_class(py::module &m) -{ - py::class_>(m, "Shape_3") - .def(py::init<>()) - .def("GetIndex", - (unsigned int(Shape_3::*)() const) &Shape_3::GetIndex, - " ") - .def("rGetVertices", - (::std::vector>> const &(Shape_3::*)() const) &Shape_3::rGetVertices, - " ", py::return_value_policy::reference_internal) - .def("SetIndex", - (void(Shape_3::*)(unsigned int)) &Shape_3::SetIndex, - " ", py::arg("index")) - .def("SetVertices", - (void(Shape_3::*)(::std::vector>> const &)) &Shape_3::SetVertices, - " ", py::arg("rVertices")) - .def("AddVertex", - (void(Shape_3::*)(::std::shared_ptr>)) &Shape_3::AddVertex, - " ", py::arg("point") = std::make_shared>()) - ; -} diff --git a/examples/shapes/wrapper/primitives/Shape_3.cppwg.hpp b/examples/shapes/wrapper/primitives/Shape_3.cppwg.hpp deleted file mode 100644 index a307241..0000000 --- a/examples/shapes/wrapper/primitives/Shape_3.cppwg.hpp +++ /dev/null @@ -1,10 +0,0 @@ -// This file is automatically generated by cppwg. -// Do not modify this file directly. - -#ifndef Shape_3_hpp__cppwg_wrapper -#define Shape_3_hpp__cppwg_wrapper - -#include - -void register_Shape_3_class(pybind11::module &m); -#endif // Shape_3_hpp__cppwg_wrapper diff --git a/examples/shapes/wrapper/primitives/_pyshapes_primitives.main.cppwg.cpp b/examples/shapes/wrapper/primitives/_pyshapes_primitives.main.cppwg.cpp index da42eb7..b77a7d7 100644 --- a/examples/shapes/wrapper/primitives/_pyshapes_primitives.main.cppwg.cpp +++ b/examples/shapes/wrapper/primitives/_pyshapes_primitives.main.cppwg.cpp @@ -10,8 +10,7 @@ #pragma GCC visibility pop #endif #include "wrapper_header_collection.cppwg.hpp" -#include "Shape_2.cppwg.hpp" -#include "Shape_3.cppwg.hpp" +#include "Shape.cppwg.hpp" #include "Cuboid.cppwg.hpp" #include "Rectangle.cppwg.hpp" diff --git a/pyproject.toml b/pyproject.toml index 8869f93..0835fa5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,7 +60,7 @@ zip-safe = false include = ["cppwg*"] [tool.black] -target-version = ["py310", "py311", "py312", "py313"] +target-version = ["py310"] extend-exclude = """ ( ^/cppwg/templates/ diff --git a/tests/test_class_writer.py b/tests/test_class_writer.py index 6e12fb7..b2706c5 100644 --- a/tests/test_class_writer.py +++ b/tests/test_class_writer.py @@ -35,17 +35,32 @@ def enumerations(self, allow_empty=False): class _FakeClassInfo: """Minimal CppClassInfo stand-in for exercising the writer directly.""" - def __init__(self, name, decl, attrs, source_file, cpp_names=None, py_names=None): + def __init__( + self, + name, + decl, + attrs, + source_file, + cpp_names=None, + py_names=None, + name_base=None, + decls=None, + generator=None, + ): self.name = name self.cpp_names = cpp_names if cpp_names is not None else [name] self.py_names = py_names if py_names is not None else [name] - self.decls = [decl] + self.decls = decls if decls is not None else [decl] self.source_file = source_file self.prefix_code = [] self.suffix_code = [] - self.custom_generator_instance = None + self.custom_generator_instance = generator + self._name_base = name_base or name self._attrs = attrs + def py_name_base(self): + return self._name_base + def hierarchy_attribute(self, key): return self._attrs.get(key) @@ -79,18 +94,22 @@ def test_struct_enum_wrapper_common_include(): source_file="Color.hpp", ) - output = _make_writer(class_info).build_struct_enum_cpp(0) - - expected = ( - "#include \n" - "#include \n" - '#include "wrapper_header_collection.cppwg.hpp"\n' - "\n" - '#include "Color.cppwg.hpp"\n' + writer = _make_writer(class_info) + header = writer.build_cpp_header("typedef Color Color;\n") + block = writer.build_struct_enum_register(0) + + # The shared preamble carries the includes, the holder (exactly once) and the + # instantiation's alias typedef. + assert '#include "wrapper_header_collection.cppwg.hpp"\n' in header + assert header.count("PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr);") == 1 + assert "typedef Color Color;\n" in header + + # The per-instantiation registration block wraps the enum values; the alias + # typedef lives in the preamble, not the block. It opens with the (empty) + # generator pre-code slot followed by a blank line, mirroring the normal + # class_cpp_register block. + expected_block = ( "\n" - "namespace py = pybind11;\n" - "typedef Color Color;\n" - "PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr);\n" "void register_Color_class(py::module &m){\n" ' py::class_ myclass(m, "Color");\n' ' py::enum_(myclass, "Value")\n' @@ -100,7 +119,7 @@ def test_struct_enum_wrapper_common_include(): " .export_values();\n" "}\n" ) - assert output == expected + assert block == expected_block def test_struct_enum_wrapper_uses_wrapper_alias_not_cpp_decl_name(): @@ -122,21 +141,22 @@ def test_struct_enum_wrapper_uses_wrapper_alias_not_cpp_decl_name(): source_file="Color.hpp", cpp_names=["Color"], py_names=["MyColor"], + name_base="MyColor", ) writer = _make_writer(class_info) - output = writer.build_struct_enum_cpp(0) + header = writer.build_cpp_header("typedef Color MyColor;\n") + block = writer.build_struct_enum_register(0) - expected = ( - "#include \n" - "#include \n" - '#include "wrapper_header_collection.cppwg.hpp"\n' - "\n" - '#include "MyColor.cppwg.hpp"\n' + # The wrapper file and its include are named after the Python alias, and the + # preamble defines the alias (C++ decl "Color" -> Python "MyColor"). + assert '#include "MyColor.cppwg.hpp"\n' in header + assert "typedef Color MyColor;\n" in header + + # The registration refers to the class by the alias throughout, opening with + # the (empty) generator pre-code slot and a blank line. + expected_block = ( "\n" - "namespace py = pybind11;\n" - "typedef Color MyColor;\n" - "PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr);\n" "void register_MyColor_class(py::module &m){\n" ' py::class_ myclass(m, "MyColor");\n' ' py::enum_(myclass, "Value")\n' @@ -145,11 +165,11 @@ def test_struct_enum_wrapper_uses_wrapper_alias_not_cpp_decl_name(): " .export_values();\n" "}\n" ) - assert output == expected + assert block == expected_block # The cpp definition and the hpp declaration must be for the same symbol. - assert "void register_MyColor_class(" in output - assert "void register_MyColor_class(" in writer.build_hpp("MyColor") + assert "void register_MyColor_class(" in block + assert "void register_MyColor_class(" in writer.build_hpp(["MyColor"]) def test_struct_enum_wrapper_source_includes_and_prefix(): @@ -171,9 +191,11 @@ class source file) and the empty smart-pointer handle. source_file="Color.hpp", ) - output = _make_writer(class_info).build_struct_enum_cpp(0) + header = _make_writer(class_info).build_cpp_header("") - expected = ( + # Non-common-include-file path: prefix text, then the explicit source + # includes and the class source file; the empty holder renders as ";". + expected_header = ( "// auto\n" "#include \n" "#include \n" @@ -183,16 +205,110 @@ class source file) and the empty smart-pointer handle. '#include "Color.cppwg.hpp"\n' "\n" "namespace py = pybind11;\n" - "typedef Color Color;\n" ";\n" - "void register_Color_class(py::module &m){\n" - ' py::class_ myclass(m, "Color");\n' - ' py::enum_(myclass, "Value")\n' - ' .value("RED", Color::Value::RED)\n' - " .export_values();\n" - "}\n" ) - assert output == expected + assert header == expected_header + + +def test_combined_wrapper_shares_one_preamble_for_all_instantiations(): + """All instantiations of a class share one file: one preamble, N register fns. + + Pins issue #30 Phase 1 - the includes, namespace and holder appear once, the + hpp declares a register function for every instantiation, and each + instantiation contributes its own alias typedef and register function. + """ + enum2 = _FakeEnum("Value", [("A", 0)]) + enum3 = _FakeEnum("Value", [("A", 0)]) + decl2 = _FakeStructDecl("Foo", "/src/Foo.hpp", enum2) + decl3 = _FakeStructDecl("Foo", "/src/Foo.hpp", enum3) + class_info = _FakeClassInfo( + "Foo", + decl2, + attrs={"common_include_file": True, "smart_ptr_type": "std::shared_ptr"}, + source_file="Foo.hpp", + cpp_names=["Foo<2>", "Foo<3>"], + py_names=["Foo_2", "Foo_3"], + name_base="Foo", + decls=[decl2, decl3], + ) + writer = _make_writer(class_info) + + # The single hpp declares a register function for every instantiation. + hpp = writer.build_hpp(["Foo_2", "Foo_3"]) + assert "void register_Foo_2_class(" in hpp + assert "void register_Foo_3_class(" in hpp + assert hpp.count("#define Foo_hpp__cppwg_wrapper") == 1 + + # The combined cpp is one shared preamble (carrying every instantiation's + # alias typedef) + one register block per instantiation. This mirrors how + # write() assembles the preamble from the gathered alias typedefs. + class_typedefs = "typedef Foo<2> Foo_2;\ntypedef Foo<3> Foo_3;\n" + cpp = ( + writer.build_cpp_header(class_typedefs) + + "\n" + + "\n".join(writer.build_struct_enum_register(i) for i in range(2)) + ) + assert cpp.count("namespace py = pybind11;") == 1 + assert cpp.count("PYBIND11_DECLARE_HOLDER_TYPE") == 1 + assert cpp.count('#include "Foo.cppwg.hpp"') == 1 + assert "typedef Foo<2> Foo_2;" in cpp + assert "typedef Foo<3> Foo_3;" in cpp + assert "void register_Foo_2_class(" in cpp + assert "void register_Foo_3_class(" in cpp + + # Every alias typedef precedes all registration code, so any block can refer + # to any instantiation by its alias. + last_typedef = max( + cpp.index("typedef Foo<2> Foo_2;"), cpp.index("typedef Foo<3> Foo_3;") + ) + first_register = min( + cpp.index("void register_Foo_2_class("), + cpp.index("void register_Foo_3_class("), + ) + assert last_typedef < first_register + + +class _FakeGenerator: + """Custom generator whose pre-code refers to the class by its wrapper alias. + + Mirrors real generators (e.g. CellsGenerator's) that emit an override class + inheriting the alias in their pre-code. + """ + + def get_class_cpp_pre_code(self, class_py_name): + return f"class {class_py_name}_Overrides : public {class_py_name} {{}};\n" + + def get_class_cpp_def_code(self, class_py_name): + return "" + + +def test_generator_pre_code_follows_alias_typedef(): + """A custom generator's pre-code is emitted after the alias typedef. + + Regression test for issue #30: the generator's pre-code refers to the class + by its wrapper alias, so the `typedef ;` (emitted in the shared + preamble) must precede it. Emitting the pre-code first left the alias + undeclared and failed to compile (seen on pychaste's CellsGenerator). + """ + enum = _FakeEnum("Value", [("A", 0)]) + decl = _FakeStructDecl("Foo", "/src/Foo.hpp", enum) + class_info = _FakeClassInfo( + "Foo", + decl, + attrs={"common_include_file": True}, + source_file="Foo.hpp", + cpp_names=["Foo<2>"], + py_names=["Foo_2"], + generator=_FakeGenerator(), + ) + writer = _make_writer(class_info) + + # write() hoists the alias typedef into the preamble; the generator pre-code + # (which uses the alias) stays in the block that follows. + cpp = writer.build_cpp_header("typedef Foo<2> Foo_2;\n") + cpp += "\n" + writer.build_struct_enum_register(0) + + assert cpp.index("typedef Foo<2> Foo_2;") < cpp.index("class Foo_2_Overrides") def test_includes_block_skips_non_string_source_include(): diff --git a/tests/test_utils.py b/tests/test_utils.py index 2c5c3ee..1a2a875 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -5,6 +5,7 @@ import pytest from cppwg.utils.utils import ( + ensure_trailing_newline, find_template_instantiations_in_source, find_template_instantiations_in_source_file, find_template_params_in_source, @@ -17,6 +18,22 @@ ) +@pytest.mark.parametrize( + "code, expected", + [ + ("", ""), # empty is left untouched (no spurious blank line) + ("class X{};", "class X{};\n"), # unterminated snippet gets a newline + ('#include "foo.h"', '#include "foo.h"\n'), + ("already\n", "already\n"), # already terminated is unchanged + ("two\nlines\n", "two\nlines\n"), + ("// trailing comment", "// trailing comment\n"), + ], +) +def test_ensure_trailing_newline(code, expected): + """A non-empty snippet is guaranteed to end with a trailing newline.""" + assert ensure_trailing_newline(code) == expected + + @pytest.mark.parametrize( "arg, expected", [