diff --git a/README.md b/README.md index cc0c250..39562a0 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Add a package description to `examples/shapes/wrapper/package_info.yaml`: name: pyshapes modules: - name: math_funcs - free_functions: cppwg_ALL + free_functions: CPPWG_ALL ``` Generate the wrappers with: diff --git a/cppwg/__main__.py b/cppwg/__main__.py index 7271ae0..8f16262 100644 --- a/cppwg/__main__.py +++ b/cppwg/__main__.py @@ -112,7 +112,7 @@ def main() -> None: logging.basicConfig( format="%(levelname)s %(message)s", - handlers=[logging.FileHandler("cppwg.log"), logging.StreamHandler()], + handlers=[logging.StreamHandler()], ) logger = logging.getLogger() diff --git a/cppwg/generators.py b/cppwg/generators.py index 3b539a8..b8e04aa 100644 --- a/cppwg/generators.py +++ b/cppwg/generators.py @@ -94,7 +94,7 @@ def __init__( r"castxml version \d+\.\d+\.\d+", castxml_version ).group(0) logger.info(castxml_version) - logger.info(f"pygccxml version {pygccxml.version}") + logger.info(f"pygccxml version {pygccxml.__version__}") # Sanitize castxml_cflags self.castxml_cflags: str = "" @@ -278,13 +278,17 @@ def add_class_decls(self) -> None: for class_info in module_info.class_info_collection: class_info.decls: List["class_t"] = [] # noqa: F821 - for full_name in class_info.full_names: - decl_name = full_name.replace(" ", "") # e.g. Foo<2,2> + for class_cpp_name in class_info.cpp_names: + decl_name = class_cpp_name.replace(" ", "") # e.g. Foo<2,2> try: class_decl = self.source_ns.class_(decl_name) except pygccxml.declarations.runtime_errors.declaration_not_found_t: + logging.warning( + f"Could not find declaration for {decl_name}: trying partial match." + ) + if "=" in class_info.template_signature: # Try to find the class without default template args # e.g. for template class Foo {}; @@ -300,10 +304,10 @@ def add_class_decls(self) -> None: decl_name = ",".join(decl_name.split(",")[0:pos]) + " >" class_decl = self.source_ns.class_(decl_name) + logging.info(f"Found {decl_name}") + else: - logging.error( - f"Could not find class declaration for {decl_name}" - ) + logging.error(f"Could not find declaration for {decl_name}") class_info.decls.append(class_decl) diff --git a/cppwg/input/class_info.py b/cppwg/input/class_info.py index 8622c29..af4c566 100644 --- a/cppwg/input/class_info.py +++ b/cppwg/input/class_info.py @@ -11,9 +11,9 @@ class CppClassInfo(CppTypeInfo): Attributes ---------- - full_names : List[str] + cpp_names : List[str] The C++ names of the class e.g. ["Foo<2,2>", "Foo<3,3>"] - short_names : List[str] + py_names : List[str] The Python names of the class e.g. ["Foo2_2", "Foo3_3"] """ @@ -21,10 +21,10 @@ def __init__(self, name: str, class_config: Optional[Dict[str, Any]] = None): super(CppClassInfo, self).__init__(name, class_config) - self.full_names: List[str] = None - self.short_names: List[str] = None + self.cpp_names: List[str] = None + self.py_names: List[str] = None - def update_short_names(self) -> None: + def update_py_names(self) -> None: """ Set the Python names for the class, accounting for template args. @@ -32,18 +32,18 @@ def update_short_names(self) -> None: collapses template arguments, separating them by underscores and removes special characters. The return type is a list, as a class can have multiple names if it is templated. For example, a class "Foo" with - template arguments [[2, 2], [3, 3]] will have a short name list + template arguments [[2, 2], [3, 3]] will have a python name list ["Foo2_2", "Foo3_3"]. """ # Handles untemplated classes if self.template_arg_lists is None: if self.name_override: - self.short_names = [self.name_override] + self.py_names = [self.name_override] else: - self.short_names = [self.name] + self.py_names = [self.name] return - self.short_names = [] + self.py_names = [] # Table of special characters for removal rm_chars = {"<": None, ">": None, ",": None, " ": None} @@ -89,36 +89,36 @@ def update_short_names(self) -> None: if idx < len(template_arg_list) - 1: template_string += "_" - self.short_names.append(type_name + template_string) + self.py_names.append(type_name + template_string) - def update_full_names(self) -> None: + def update_cpp_names(self) -> None: """ Set the C++ names for the class, accounting for template args. Set the name of the class as it should appear in C++. The return type is a list, as a class can have multiple names if it is templated. For example, a class "Foo" with - template arguments [[2, 2], [3, 3]] will have a full name list + template arguments [[2, 2], [3, 3]] will have a C++ name list ["Foo<2,2 >", "Foo<3,3 >"]. """ # Handles untemplated classes if self.template_arg_lists is None: - self.full_names = [self.name] + self.cpp_names = [self.name] return - self.full_names = [] + self.cpp_names = [] for template_arg_list in self.template_arg_lists: # Create template string from arg list e.g. [2, 2] -> "<2,2 >" template_string = ",".join([str(arg) for arg in template_arg_list]) template_string = "<" + template_string + " >" # Join full name e.g. "Foo<2,2 >" - self.full_names.append(self.name + template_string) + self.cpp_names.append(self.name + template_string) def update_names(self) -> None: - """Update the full and short names for the class.""" - self.update_full_names() - self.update_short_names() + """Update the C++ and Python names for the class.""" + self.update_cpp_names() + self.update_py_names() @property def parent(self) -> "ModuleInfo": # noqa: F821 diff --git a/cppwg/input/cpp_type_info.py b/cppwg/input/cpp_type_info.py index d5d7578..3260d85 100644 --- a/cppwg/input/cpp_type_info.py +++ b/cppwg/input/cpp_type_info.py @@ -21,8 +21,10 @@ class CppTypeInfo(BaseInfo): The name override specified in config e.g. "CustomFoo" -> "Foo" template_signature : str The template signature of the type e.g. "" + template_params : List[str] + List of template parameters e.g. ["DIM_A", "DIM_B"] template_arg_lists : List[List[Any]] - List of template replacement arguments for the type e.g. [[2, 2], [3, 3]] + List of template replacement arguments e.g. [[2, 2], [3, 3]] decls : pygccxml.declarations.declaration_t The pygccxml declarations associated with this type, one per template arg if templated """ @@ -36,32 +38,10 @@ def __init__(self, name: str, type_config: Optional[Dict[str, Any]] = None): self.source_file: Optional[str] = None self.name_override: Optional[str] = None self.template_signature: Optional[str] = None + self.template_params: Optional[List[str]] = None self.template_arg_lists: Optional[List[List[Any]]] = None self.decls: Optional[List["declaration_t"]] = None # noqa: F821 if type_config: for key, value in type_config.items(): setattr(self, key, value) - - # TODO: This method is not used, remove it? - def needs_header_file_instantiation(self): - """Check if this class needs to be instantiated in the header file.""" - return ( - (self.template_arg_lists is not None) - and (not self.include_file_only) - and (self.needs_instantiation) - ) - - # TODO: This method is not used, remove it? - def needs_header_file_typdef(self): - """ - Check if this type need to be typdef'd with a nicer name. - - The typedefs are declared in the header file. All template classes need this. - """ - return (self.template_arg_lists is not None) and (not self.include_file_only) - - # TODO: This method is not used, remove it? - def needs_auto_wrapper_generation(self): - """Check if this class needs a wrapper to be autogenerated.""" - return not self.include_file_only diff --git a/cppwg/input/info_helper.py b/cppwg/input/info_helper.py index 9b81d5a..b2883d8 100644 --- a/cppwg/input/info_helper.py +++ b/cppwg/input/info_helper.py @@ -134,4 +134,16 @@ def extract_templates_from_source(self, feature_info: BaseInfo) -> None: feature_info.template_signature = template_substitution[ "signature" ] + + # Extract ["DIM_A", "DIM_B"] from "" + template_params = [] + for tp in template_substitution["signature"].split(","): + template_params.append( + tp.replace("<", "") + .replace(">", "") + .split(" ")[1] + .split("=")[0] + .strip() + ) + feature_info.template_params = template_params break diff --git a/cppwg/input/module_info.py b/cppwg/input/module_info.py index 0479315..ac2c792 100644 --- a/cppwg/input/module_info.py +++ b/cppwg/input/module_info.py @@ -3,8 +3,6 @@ import os from typing import Any, Dict, List, Optional -from pygccxml.declarations import declaration_t - from cppwg.input.base_info import BaseInfo @@ -54,7 +52,7 @@ def parent(self) -> "PackageInfo": # noqa: F821 """Returns the parent package info object.""" return self.package_info - def is_decl_in_source_path(self, decl: declaration_t) -> bool: + def is_decl_in_source_path(self, decl: "declaration_t") -> bool: # noqa: F821 """ Check if the declaration is associated with a file in the specified source paths. diff --git a/cppwg/input/package_info.py b/cppwg/input/package_info.py index 0a6c497..fdbe78c 100644 --- a/cppwg/input/package_info.py +++ b/cppwg/input/package_info.py @@ -25,6 +25,8 @@ class PackageInfo(BaseInfo): A list of source file names to include common_include_file : bool Use a common include file for all source files + exclude_default_args : bool + Exclude default arguments from method wrappers. """ def __init__( @@ -57,6 +59,7 @@ def __init__( self.source_hpp_patterns: List[str] = ["*.hpp"] self.source_hpp_files: List[str] = [] self.common_include_file: bool = False + self.exclude_default_args: bool = False if package_config: for key, value in package_config.items(): diff --git a/cppwg/parsers/package_info_parser.py b/cppwg/parsers/package_info_parser.py index 06cd6ca..552c404 100644 --- a/cppwg/parsers/package_info_parser.py +++ b/cppwg/parsers/package_info_parser.py @@ -133,6 +133,7 @@ def parse(self) -> PackageInfo: package_config: Dict[str, Any] = { "name": "cppwg_package", "common_include_file": True, + "exclude_default_args": False, "source_hpp_patterns": ["*.hpp"], } package_config.update(global_config) @@ -140,7 +141,10 @@ def parse(self) -> PackageInfo: for key in package_config.keys(): if key in self.raw_package_info: package_config[key] = self.raw_package_info[key] + + # Replace boolean strings with booleans utils.substitute_bool_for_string(package_config, "common_include_file") + utils.substitute_bool_for_string(package_config, "exclude_default_args") # Create the PackageInfo object from the package config dict self.package_info = PackageInfo( diff --git a/cppwg/templates/pybind11_default.py b/cppwg/templates/pybind11_default.py index b4dfd26..010fe67 100644 --- a/cppwg/templates/pybind11_default.py +++ b/cppwg/templates/pybind11_default.py @@ -4,10 +4,10 @@ #include #include {includes} -#include "{class_short_name}.%s.hpp" +#include "{class_py_name}.%s.hpp" namespace py = pybind11; -typedef {class_full_name} {class_short_name}; +typedef {class_cpp_name} {class_py_name}; {smart_ptr_handle}; """ % CPPWG_EXT @@ -16,43 +16,43 @@ #include {includes} //#include "PythonObjectConverters.hpp" -#include "{class_short_name}.%s.hpp" +#include "{class_py_name}.%s.hpp" namespace py = pybind11; //PYBIND11_CVECTOR_TYPECASTER2(); //PYBIND11_CVECTOR_TYPECASTER3(); -typedef {class_full_name} {class_short_name}; +typedef {class_cpp_name} {class_py_name}; {smart_ptr_handle}; """ % CPPWG_EXT class_hpp_header = """\ -#ifndef {class_short_name}_hpp__%s_wrapper -#define {class_short_name}_hpp__%s_wrapper +#ifndef {class_py_name}_hpp__%s_wrapper +#define {class_py_name}_hpp__%s_wrapper #include -void register_{class_short_name}_class(pybind11::module &m); -#endif // {class_short_name}_hpp__%s_wrapper +void register_{class_py_name}_class(pybind11::module &m); +#endif // {class_py_name}_hpp__%s_wrapper """ % tuple([CPPWG_EXT]*3) class_virtual_override_header = """\ -class {class_short_name}%s : public {class_short_name}{{ +class {class_py_name}%s : public {class_py_name}{{ public: - using {class_short_name}::{class_base_name}; + using {class_py_name}::{class_base_name}; """ % CPPWG_CLASS_OVERRIDE_SUFFIX class_virtual_override_footer = "}\n" class_definition = """\ -void register_{short_name}_class(py::module &m){{ -py::class_<{short_name} {overrides_string} {ptr_support} {bases} >(m, "{short_name}") +void register_{class_py_name}_class(py::module &m){{ +py::class_<{class_py_name} {overrides_string} {ptr_support} {bases} >(m, "{class_py_name}") """ method_virtual_override = """\ {return_type} {method_name}({arg_string}){const_adorn} override {{ PYBIND11_OVERRIDE{overload_adorn}( {tidy_method_name}, - {short_class_name}, + {class_py_name}, {method_name}, {args_string}); }} @@ -67,7 +67,7 @@ class {class_short_name}%s : public {class_short_name}{{ class_method = """\ .def{def_adorn}( "{method_name}", - ({return_type}({self_ptr})({arg_signature}){const_adorn}) &{class_short_name}::{method_name}, + ({return_type}({self_ptr})({arg_signature}){const_adorn}) &{class_py_name}::{method_name}, {method_docs} {default_args} {call_policy}) """ diff --git a/cppwg/writers/base_writer.py b/cppwg/writers/base_writer.py index 336d314..ca2f3cd 100644 --- a/cppwg/writers/base_writer.py +++ b/cppwg/writers/base_writer.py @@ -53,15 +53,3 @@ def tidy_name(self, name: str) -> str: name = name.replace(key, value) return name - - # TODO: This method is currently a placeholder. Consider implementing or removing. - def default_arg_exclusion_criteria(self) -> bool: - """ - Check if default arguments should be excluded from the wrapper code. - - Returns - ------- - bool - True if the default arguments should be excluded - """ - return False diff --git a/cppwg/writers/class_writer.py b/cppwg/writers/class_writer.py index 677f44b..0f1fbba 100644 --- a/cppwg/writers/class_writer.py +++ b/cppwg/writers/class_writer.py @@ -30,8 +30,6 @@ class CppClassWrapperWriter(CppBaseWrapperWriter): A list of decls for all classes in the module has_shared_ptr : bool Whether the class uses shared pointers - is_abstract : bool - Whether the class is abstract hpp_string : str The hpp wrapper code cpp_string : str @@ -50,43 +48,42 @@ def __init__( self.class_info: "CppClassInfo" = class_info # noqa: F821 - if len(self.class_info.full_names) != len(self.class_info.short_names): - logger.error("Full and short name lists should be the same length") + if len(self.class_info.cpp_names) != len(self.class_info.py_names): + logger.error("C++ and Python class name lists should be the same length") raise AssertionError() self.module_class_decls: List["class_t"] = module_class_decls # noqa: F821 self.has_shared_ptr: bool = True - self.is_abstract: bool = False # TODO: Consider removing unused attribute self.hpp_string: str = "" self.cpp_string: str = "" - def add_hpp(self, class_short_name: str) -> None: + def add_hpp(self, class_py_name: str) -> None: """ Fill the class hpp string for a single class using the wrapper template. Parameters ---------- - class_short_name: str - The short name of the class e.g. Foo2_2 + class_py_name: str + The Python name of the class e.g. Foo2_2 """ - class_hpp_dict = {"class_short_name": class_short_name} + class_hpp_dict = {"class_py_name": class_py_name} self.hpp_string += self.wrapper_templates["class_hpp_header"].format( **class_hpp_dict ) - def add_cpp_header(self, class_full_name: str, class_short_name: str) -> None: + def add_cpp_header(self, class_cpp_name: str, class_py_name: str) -> None: """ Add the 'top' of the class wrapper cpp file for a single class. Parameters ---------- - class_full_name : str - The full name of the class e.g. Foo<2,2> - class_short_name : str - The short name of the class e.g. Foo2_2 + class_cpp_name : str + The C++ name of the class e.g. Foo<2,2> + class_py_name : str + The Python name of the class e.g. Foo2_2 """ # Add the includes for this class includes = "" @@ -127,8 +124,8 @@ def add_cpp_header(self, class_full_name: str, class_short_name: str) -> None: # Fill in the cpp header template header_dict = { "includes": includes, - "class_short_name": class_short_name, - "class_full_name": class_full_name, + "class_py_name": class_py_name, + "class_cpp_name": class_cpp_name, "smart_ptr_handle": smart_ptr_handle, } @@ -143,11 +140,11 @@ def add_cpp_header(self, class_full_name: str, class_short_name: str) -> None: # Run any custom generators to add additional prefix code if self.class_info.custom_generator: self.cpp_string += self.class_info.custom_generator.get_class_cpp_pre_code( - class_short_name + class_py_name ) def add_virtual_overrides( - self, class_decl: "class_t", short_class_name: str # noqa: F821 + self, template_idx: int ) -> List["member_function_t"]: # noqa: F821 """ Add virtual "trampoline" overrides for the class. @@ -157,10 +154,8 @@ def add_virtual_overrides( Parameters ---------- - class_decl : class_t - The class declaration - short_class_name : str - The short name of the class e.g. Foo2_2 + template_idx : int + The index of the template in the class info Returns ------- @@ -170,35 +165,36 @@ def add_virtual_overrides( return_types: List[str] = [] # e.g. ["void", "unsigned int", "::Bar<2> *"] # Collect all virtual methods and their return types + class_decl = self.class_info.decls[template_idx] + for member_function in class_decl.member_functions(allow_empty=True): is_pure_virtual = member_function.virtuality == "pure virtual" is_virtual = member_function.virtuality == "virtual" if is_pure_virtual or is_virtual: methods_needing_override.append(member_function) return_types.append(member_function.return_type.decl_string) - if is_pure_virtual: - self.is_abstract = True # Add typedefs for return types with special characters # e.g. typedef ::Bar<2> * _Bar_lt_2_gt_Ptr; for return_type in return_types: if return_type != self.tidy_name(return_type): - typedef_template = "typedef {full_name} {tidy_name};\n" + typedef_template = "typedef {class_cpp_name} {tidy_name};\n" typedef_dict = { - "full_name": return_type, + "class_cpp_name": return_type, "tidy_name": self.tidy_name(return_type), } self.cpp_string += typedef_template.format(**typedef_dict) self.cpp_string += "\n" # Override virtual methods + class_py_name = self.class_info.py_names[template_idx] if methods_needing_override: # Add virtual override class, e.g.: # class Foo_Overrides : public Foo { # public: # using Foo::Foo; override_header_dict = { - "class_short_name": short_class_name, + "class_py_name": class_py_name, "class_base_name": self.class_info.name, } @@ -217,10 +213,9 @@ def add_virtual_overrides( for method in methods_needing_override: method_writer = CppMethodWrapperWriter( self.class_info, + template_idx, method, - class_decl, self.wrapper_templates, - short_class_name, ) self.cpp_string += method_writer.generate_virtual_override_wrapper() @@ -239,24 +234,23 @@ def write(self, work_dir: str) -> None: """ logger = logging.getLogger() - if len(self.class_info.decls) != len(self.class_info.full_names): + 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, full_name in enumerate(self.class_info.full_names): - short_name = self.class_info.short_names[idx] + for idx, class_cpp_name in enumerate(self.class_info.cpp_names): + class_py_name = self.class_info.py_names[idx] class_decl = self.class_info.decls[idx] self.hpp_string = "" self.cpp_string = "" # Add the cpp file header - self.add_cpp_header(full_name, short_name) + self.add_cpp_header(class_cpp_name, class_py_name) # Check for struct-enum pattern. For example: # struct Foo{ # enum Value{A, B, C}; # }; - # TODO: Consider moving some parts into templates if declarations.is_struct(class_decl): enums = class_decl.enumerations(allow_empty=True) @@ -276,15 +270,15 @@ def write(self, work_dir: str) -> None: self.cpp_string += " .export_values();\n}\n" # Set up the hpp - self.add_hpp(short_name) + self.add_hpp(class_py_name) # Write the struct cpp and hpp files - self.write_files(work_dir, short_name) + self.write_files(work_dir, class_py_name) continue # Find and define virtual function "trampoline" overrides methods_needing_override: List["member_function_t"] = ( # noqa: F821 - self.add_virtual_overrides(class_decl, short_name) + self.add_virtual_overrides(idx) ) # Add the virtual "trampoline" overrides from "Foo_Overrides" to @@ -292,14 +286,14 @@ def write(self, work_dir: str) -> None: # e.g. py::class_(m, "Foo") overrides_string = "" if methods_needing_override: - overrides_string = f", {short_name}{CPPWG_CLASS_OVERRIDE_SUFFIX}" + overrides_string = f", {class_py_name}{CPPWG_CLASS_OVERRIDE_SUFFIX}" # Add smart pointer support to the wrapper class definition if needed # e.g. py::class_ >(m, "Foo") smart_ptr_type: str = self.class_info.hierarchy_attribute("smart_ptr_type") ptr_support = "" if self.has_shared_ptr and smart_ptr_type: - ptr_support = f", {smart_ptr_type}<{short_name} > " + ptr_support = f", {smart_ptr_type}<{class_py_name} > " # Add base classes to the wrapper class definition if needed # e.g. py::class_(m, "Foo") @@ -316,7 +310,7 @@ def write(self, work_dir: str) -> None: # Add the class registration class_definition_dict = { - "short_name": short_name, + "class_py_name": class_py_name, "overrides_string": overrides_string, "ptr_support": ptr_support, "bases": bases, @@ -331,10 +325,9 @@ def write(self, work_dir: str) -> None: ): constructor_writer = CppConstructorWrapperWriter( self.class_info, + idx, constructor, - class_decl, self.wrapper_templates, - short_name, ) self.cpp_string += constructor_writer.generate_wrapper() @@ -350,29 +343,30 @@ def write(self, work_dir: str) -> None: method_writer = CppMethodWrapperWriter( self.class_info, + idx, member_function, - class_decl, self.wrapper_templates, - short_name, ) self.cpp_string += method_writer.generate_wrapper() # Run any custom generators to add additional class code if self.class_info.custom_generator: self.cpp_string += ( - self.class_info.custom_generator.get_class_cpp_def_code(short_name) + self.class_info.custom_generator.get_class_cpp_def_code( + class_py_name + ) ) # Close the class definition self.cpp_string += " ;\n}\n" # Set up the hpp - self.add_hpp(short_name) + self.add_hpp(class_py_name) # Write the class cpp and hpp files - self.write_files(work_dir, short_name) + self.write_files(work_dir, class_py_name) - def write_files(self, work_dir: str, class_short_name: str) -> None: + def write_files(self, work_dir: str, class_py_name: str) -> None: """ Write the hpp and cpp wrapper code to file. @@ -380,11 +374,11 @@ def write_files(self, work_dir: str, class_short_name: str) -> None: ---------- work_dir : str The directory to write the files to - class_short_name : str - The short name of the class e.g. Foo2_2 + class_py_name : str + The Python name of the class e.g. Foo2_2 """ - hpp_filepath = os.path.join(work_dir, f"{class_short_name}.{CPPWG_EXT}.hpp") - cpp_filepath = os.path.join(work_dir, f"{class_short_name}.{CPPWG_EXT}.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") with open(hpp_filepath, "w") as hpp_file: hpp_file.write(self.hpp_string) diff --git a/cppwg/writers/constructor_writer.py b/cppwg/writers/constructor_writer.py index a0a2a54..dde6bd8 100644 --- a/cppwg/writers/constructor_writer.py +++ b/cppwg/writers/constructor_writer.py @@ -1,12 +1,10 @@ """Wrapper code writer for C++ class constructors.""" -from typing import Dict, Optional +import re +from typing import Dict from pygccxml import declarations -from pygccxml.declarations.calldef_members import constructor_t -from pygccxml.declarations.class_declaration import class_t -from cppwg.input.class_info import CppClassInfo from cppwg.writers.base_writer import CppBaseWrapperWriter @@ -18,36 +16,47 @@ class CppConstructorWrapperWriter(CppBaseWrapperWriter): ---------- class_info : ClassInfo The class information for the class containing the constructor - ctor_decl : constructor_t + template_idx: int + The index of the template in class_info + ctor_decl : pygccxml.declarations.constructor_t The pygccxml declaration object for the constructor - class_decl : class_t + class_decl : pygccxml.declarations.class_t The class declaration for the class containing the constructor wrapper_templates : Dict[str, str] String templates with placeholders for generating wrapper code - class_short_name : Optional[str] - The short name of the class e.g. 'Foo2_2' + class_py_name : Optional[str] + The Python name of the class e.g. 'Foo2_2' + template_params: Optional[List[str]] + The template params for the class e.g. ['DIM_A', 'DIM_B'] + template_args: Optional[List[str]] + The template args for the class e.g. ['2', '2'] """ def __init__( self, - class_info: CppClassInfo, - ctor_decl: constructor_t, - class_decl: class_t, + class_info: "CppClassInfo", # noqa: F821 + template_idx: int, + ctor_decl: "constructor_t", # noqa: F821 wrapper_templates: Dict[str, str], - class_short_name: Optional[str] = None, ) -> None: super(CppConstructorWrapperWriter, self).__init__(wrapper_templates) - self.class_info: CppClassInfo = class_info - self.ctor_decl: constructor_t = ctor_decl - self.class_decl: class_t = class_decl + self.class_info: "CppClassInfo" = class_info # noqa: F821 + self.ctor_decl: "constructor_t" = ctor_decl # noqa: F821 + self.class_decl: "class_t" = class_info.decls[template_idx] # noqa: F821 - self.class_short_name = class_short_name - if self.class_short_name is None: - self.class_short_name = self.class_decl.name + self.class_py_name = class_info.py_names[template_idx] + if self.class_py_name is None: + self.class_py_name = self.class_decl.name - def exclusion_criteria(self) -> bool: + self.template_params = class_info.template_params + + self.template_args = None + if class_info.template_arg_lists: + self.template_args = class_info.template_arg_lists[template_idx] + + def exclude(self) -> bool: """ Check if the constructor should be excluded from the wrapper code. @@ -130,7 +139,7 @@ def generate_wrapper(self) -> str: The constructor wrapper code. """ # Skip excluded constructors - if self.exclusion_criteria(): + if self.exclude(): return "" # Get the arg signature e.g. "int, bool" @@ -141,16 +150,35 @@ def generate_wrapper(self) -> str: wrapper_string += " >()" - # Default args e.g. py::arg("i") = 1 - default_args = "" - if not self.default_arg_exclusion_criteria(): - for arg in self.ctor_decl.arguments: - default_args += f', py::arg("{arg.name}")' + # Keyword args with default values e.g. py::arg("i") = 1 + keyword_args = "" + for arg in self.ctor_decl.arguments: + keyword_args += f', py::arg("{arg.name}")' - if arg.default_value is not None: - # TODO: Fix in default args (see method_writer) - default_args += f" = {arg.default_value}" - - wrapper_string += default_args + ")\n" + if not ( + arg.default_value is None + or self.class_info.hierarchy_attribute("exclude_default_args") + ): + default_value = str(arg.default_value) + + # Check for template params in default value + if self.template_params: + for param, val in zip(self.template_params, self.template_args): + if param in default_value: + # Replace e.g. Foo::DIM_A -> 2 + default_value = re.sub( + f"\\b{self.class_info.name}::{param}\\b", + str(val), + default_value, + ) + + # Replace e.g. -> <2> + default_value = re.sub( + f"\\b{param}\\b", f"{val}", default_value + ) + + keyword_args += f" = {default_value}" + + wrapper_string += keyword_args + ")\n" return wrapper_string diff --git a/cppwg/writers/free_function_writer.py b/cppwg/writers/free_function_writer.py index 690c0aa..c3f29ac 100644 --- a/cppwg/writers/free_function_writer.py +++ b/cppwg/writers/free_function_writer.py @@ -38,7 +38,7 @@ def generate_wrapper(self) -> str: The updated C++ wrapper code string """ # Skip this free function if it uses any excluded arg types or return types - if self.exclusion_criteria(): + if self.exclude(): return "" # Pybind11 def type e.g. "_static" for def_static() @@ -48,7 +48,7 @@ def generate_wrapper(self) -> str: # e.g. without default values: ', py::arg("foo"), py::arg("bar")' # e.g. with default values: ', py::arg("foo") = 1, py::arg("bar") = 2' default_args = "" - if not self.default_arg_exclusion_criteria(): + if not self.free_function_info.hierarchy_attribute("exclude_default_args"): for argument in self.free_function_info.decls[0].arguments: default_args += f', py::arg("{argument.name}")' if argument.default_value is not None: @@ -65,7 +65,7 @@ def generate_wrapper(self) -> str: return wrapper_string - def exclusion_criteria(self) -> bool: + def exclude(self) -> bool: """ Check if the function should be excluded from the wrapper code. diff --git a/cppwg/writers/header_collection_writer.py b/cppwg/writers/header_collection_writer.py index 0be1660..ca9df41 100644 --- a/cppwg/writers/header_collection_writer.py +++ b/cppwg/writers/header_collection_writer.py @@ -129,15 +129,15 @@ def write(self) -> None: if not class_info.template_arg_lists: continue - # Class full names eg. ["Foo<2,2>", "Foo<3,3>"] - full_names = [name.replace(" ", "") for name in class_info.full_names] + # C++ class names eg. ["Foo<2,2>", "Foo<3,3>"] + cpp_names = [name.replace(" ", "") for name in class_info.cpp_names] - # Class short names eg. ["Foo2_2", "Foo3_3"] - short_names = [name.replace(" ", "") for name in class_info.short_names] + # Python class names eg. ["Foo2_2", "Foo3_3"] + py_names = [name.replace(" ", "") for name in class_info.py_names] - for full_name, short_name in zip(full_names, short_names): - template_instantiations += f"template class {full_name};\n" - template_typedefs += f"typedef {full_name} {short_name};\n" + for cpp_name, py_name in zip(cpp_names, py_names): + template_instantiations += f"template class {cpp_name};\n" + template_typedefs += f"typedef {cpp_name} {py_name};\n" self.hpp_collection_string += "\n// Instantiate Template Classes\n" self.hpp_collection_string += template_instantiations diff --git a/cppwg/writers/method_writer.py b/cppwg/writers/method_writer.py index d61e189..3d4d621 100644 --- a/cppwg/writers/method_writer.py +++ b/cppwg/writers/method_writer.py @@ -1,12 +1,10 @@ """Wrapper code writer for C++ methods.""" -from typing import Dict, Optional +import re +from typing import Dict from pygccxml import declarations -from pygccxml.declarations.calldef_members import member_function_t -from pygccxml.declarations.class_declaration import class_t -from cppwg.input.class_info import CppClassInfo from cppwg.writers.base_writer import CppBaseWrapperWriter @@ -18,36 +16,47 @@ class CppMethodWrapperWriter(CppBaseWrapperWriter): ---------- class_info : ClassInfo The class information for the class containing the method - method_decl : member_function_t + template_idx: int + The index of the template in class_info + method_decl : [pygccxml.declarations.member_function_t] The pygccxml declaration object for the method - class_decl : class_t + class_decl : [pygccxml.declarations.class_t] The class declaration for the class containing the method wrapper_templates : Dict[str, str] String templates with placeholders for generating wrapper code - class_short_name : Optional[str] - The short name of the class e.g. 'Foo2_2' + class_py_name : Optional[str] + The Python name of the class e.g. 'Foo2_2' + template_params: Optional[List[str]] + The template params for the class e.g. ['DIM_A', 'DIM_B'] + template_args: Optional[List[str]] + The template args for the class e.g. ['2', '2'] """ def __init__( self, - class_info: CppClassInfo, - method_decl: member_function_t, - class_decl: class_t, + class_info: "CppClassInfo", # noqa: F821 + template_idx: int, + method_decl: "member_function_t", # noqa: F821 wrapper_templates: Dict[str, str], - class_short_name: Optional[str] = None, ) -> None: super(CppMethodWrapperWriter, self).__init__(wrapper_templates) - self.class_info: CppClassInfo = class_info - self.method_decl: member_function_t = method_decl - self.class_decl: class_t = class_decl + self.class_info: "CppClassInfo" = class_info # noqa: F821 + self.method_decl: "member_function_t" = method_decl # noqa: F821 + self.class_decl: "class_t" = class_info.decls[template_idx] # noqa: F821 - self.class_short_name: str = class_short_name - if self.class_short_name is None: - self.class_short_name = self.class_decl.name + self.class_py_name = class_info.py_names[template_idx] + if self.class_py_name is None: + self.class_py_name = self.class_decl.name - def exclusion_criteria(self) -> bool: + self.template_params = class_info.template_params + + self.template_args = None + if class_info.template_arg_lists: + self.template_args = class_info.template_arg_lists[template_idx] + + def exclude(self) -> bool: """ Check if the method should be excluded from the wrapper code. @@ -109,7 +118,7 @@ def generate_wrapper(self) -> str: The method wrapper code. """ # Skip excluded methods - if self.exclusion_criteria(): + if self.exclude(): return "" # Pybind11 def type e.g. "_static" for def_static() @@ -122,7 +131,7 @@ def generate_wrapper(self) -> str: self_ptr = "*" else: # e.g. Foo2_2::* - self_ptr = self.class_short_name + "::*" + self_ptr = self.class_py_name + "::*" # Const-ness const_adorn = "" @@ -133,28 +142,34 @@ def generate_wrapper(self) -> str: arg_types = [t.decl_string for t in self.method_decl.argument_types] arg_signature = ", ".join(arg_types) - # Default args e.g. py::arg("d") = 1.0 - default_args = "" - if not self.default_arg_exclusion_criteria(): - for arg, arg_type in zip( - self.method_decl.arguments, self.method_decl.argument_types - ): - default_args += f', py::arg("{arg.name}")' - - if arg.default_value is not None: - default_value = str(arg.default_value) - - # Hack for missing template in default args - # e.g. Foo<2>::bar(Bar<2> const & b = Bar()) - # TODO: Make more robust - arg_type_str = str(arg_type).replace(" ", "") - if "" in default_value: - if "<2>" in arg_type_str: - default_value = default_value.replace("", "<2>") - elif "<3>" in arg_type_str: - default_value = default_value.replace("", "<3>") + # Keyword args with default values e.g. py::arg("i") = 1 + keyword_args = "" + for arg in self.method_decl.arguments: + keyword_args += f', py::arg("{arg.name}")' - default_args += f" = {default_value}" + if not ( + arg.default_value is None + or self.class_info.hierarchy_attribute("exclude_default_args") + ): + default_value = str(arg.default_value) + + # Check for template params in default value + if self.template_params: + for param, val in zip(self.template_params, self.template_args): + if param in default_value: + # Replace e.g. Foo::DIM_A -> 2 + default_value = re.sub( + f"\\b{self.class_info.name}::{param}\\b", + str(val), + default_value, + ) + + # Replace e.g. -> <2> + default_value = re.sub( + f"\\b{param}\\b", f"{val}", default_value + ) + + keyword_args += f" = {default_value}" # Call policy, e.g. "py::return_value_policy::reference" call_policy = "" @@ -175,9 +190,9 @@ def generate_wrapper(self) -> str: "self_ptr": self_ptr, "arg_signature": arg_signature, "const_adorn": const_adorn, - "class_short_name": self.class_short_name, + "class_py_name": self.class_py_name, "method_docs": '" "', - "default_args": default_args, + "default_args": keyword_args, "call_policy": call_policy, } class_method_template = self.wrapper_templates["class_method"] @@ -243,7 +258,7 @@ def generate_virtual_override_wrapper(self) -> str: "const_adorn": const_adorn, "overload_adorn": overload_adorn, "tidy_method_name": self.tidy_name(return_string), - "short_class_name": self.class_short_name, + "class_py_name": self.class_py_name, "args_string": arg_name_string, } wrapper_string = self.wrapper_templates["method_virtual_override"].format( diff --git a/cppwg/writers/module_writer.py b/cppwg/writers/module_writer.py index a4abc9f..65e2189 100644 --- a/cppwg/writers/module_writer.py +++ b/cppwg/writers/module_writer.py @@ -42,9 +42,7 @@ def __init__( self.module_info: "ModuleInfo" = module_info # noqa: F821 self.wrapper_templates: Dict[str, str] = wrapper_templates self.wrapper_root: str = wrapper_root - self.package_license: str = ( - package_license # TODO: use this in the generated wrappers - ) + self.package_license: str = package_license # For convenience, store a list of declarations of all # classes to be wrapped in the module @@ -83,9 +81,9 @@ def write_module_wrapper(self) -> None: # Add includes for class wrappers in the module for class_info in self.module_info.class_info_collection: - for short_name in class_info.short_names: + for py_name in class_info.py_names: # Example: #include "Foo2_2.cppwg.hpp" - cpp_string += f'#include "{short_name}.{CPPWG_EXT}.hpp"\n' + cpp_string += f'#include "{py_name}.{CPPWG_EXT}.hpp"\n' # Format module name as _packagename_modulename full_module_name = ( @@ -106,9 +104,9 @@ def write_module_wrapper(self) -> None: # Add classes for class_info in self.module_info.class_info_collection: - for short_name in class_info.short_names: + for py_name in class_info.py_names: # Example: register_Foo2_2_class(m);" - cpp_string += f" register_{short_name}_class(m);\n" + cpp_string += f" register_{py_name}_class(m);\n" # Add code from the module's custom generator if self.module_info.custom_generator: diff --git a/examples/shapes/src/geometry/Point.cpp b/examples/shapes/src/geometry/Point.cpp index aa87aaf..87630e1 100644 --- a/examples/shapes/src/geometry/Point.cpp +++ b/examples/shapes/src/geometry/Point.cpp @@ -1,66 +1,63 @@ #include "Point.hpp" -template -Point::Point() : - mIndex(0), - mLocation() +template +Point::Point() + : Point(0.0, 0.0, 0.0) { - mLocation[0] = 0.0; - mLocation[1] = 0.0; - if(DIM==3) - { - mLocation[2] = 0.0; - } } -template -Point::Point(double x, double y, double z) : - mIndex(0), - mLocation() +template +Point::Point(double x, double y, double z) + : mIndex(0), mLocation() { mLocation[0] = x; mLocation[1] = y; - if(DIM==3) + if (DIM == 3) { mLocation[2] = z; } } -template +template Point::~Point() { - } -template +template std::array Point::GetLocation() const { return mLocation; } -template -const std::array& Point::rGetLocation() const +template +const std::array &Point::rGetLocation() const { return mLocation; } -template +template unsigned Point::GetIndex() const { return mIndex; } -template +template void Point::SetIndex(unsigned index) { mIndex = index; } -template -void Point::SetLocation(const std::array& rLocation) +template +void Point::SetLocation(const std::array &rLocation) { mLocation = rLocation; } +template +void Point::ExcludedMethod() +{ + // Excluded method +} + template class Point<2>; template class Point<3>; diff --git a/examples/shapes/src/geometry/Point.hpp b/examples/shapes/src/geometry/Point.hpp index 018731e..fe93805 100644 --- a/examples/shapes/src/geometry/Point.hpp +++ b/examples/shapes/src/geometry/Point.hpp @@ -6,11 +6,10 @@ /** * A point in DIM dimensional space */ -template +template class Point { private: - /** * Point index */ @@ -22,16 +21,15 @@ class Point std::array mLocation; public: - /** * Default Constructor */ Point(); /** - * Constructor + * Constructor with coordinates */ - Point(double x, double y, double z=0.0); + Point(double x, double y, double z = (DIM - DIM)); /** * Destructor @@ -46,7 +44,7 @@ class Point /** * Return the const location */ - const std::array& rGetLocation() const; + const std::array &rGetLocation() const; /** * Return the index @@ -61,7 +59,12 @@ class Point /** * Set the location */ - void SetLocation(const std::array& rLocation); + void SetLocation(const std::array &rLocation); + + /** + * Placeholder method to exclude from wrapping + */ + void ExcludedMethod(); }; -#endif // _POINT_HPP +#endif // _POINT_HPP diff --git a/examples/shapes/src/math_funcs/SimpleMathFunctions.hpp b/examples/shapes/src/math_funcs/SimpleMathFunctions.hpp index 8451bd9..bae076d 100644 --- a/examples/shapes/src/math_funcs/SimpleMathFunctions.hpp +++ b/examples/shapes/src/math_funcs/SimpleMathFunctions.hpp @@ -1,5 +1,5 @@ -#ifndef _SIMPLEMATHFUNCTIONS_HPP -#define _SIMPLEMATHFUNCTIONS_HPP +#ifndef _SIMPLE_MATH_FUNCTIONS_HPP +#define _SIMPLE_MATH_FUNCTIONS_HPP /** * Add the two input numbers and return the result @@ -12,4 +12,4 @@ inline double add(double i = 1.0, double j = 2.0) return i + j; } -#endif // _SIMPLEMATHFUNCTIONS_HPP +#endif // _SIMPLE_MATH_FUNCTIONS_HPP diff --git a/examples/shapes/src/primitives/Cuboid.hpp b/examples/shapes/src/primitives/Cuboid.hpp index 6b3d4b9..3d0a409 100644 --- a/examples/shapes/src/primitives/Cuboid.hpp +++ b/examples/shapes/src/primitives/Cuboid.hpp @@ -1,5 +1,5 @@ -#ifndef _Cuboid_HPP -#define _Cuboid_HPP +#ifndef _CUBOID_HPP +#define _CUBOID_HPP #include "Shape.hpp" @@ -23,4 +23,4 @@ class Cuboid : public Shape<3> }; -#endif // _Cuboid_HPP +#endif // _CUBOID_HPP diff --git a/examples/shapes/src/primitives/Rectangle.hpp b/examples/shapes/src/primitives/Rectangle.hpp index 692877a..a990a90 100644 --- a/examples/shapes/src/primitives/Rectangle.hpp +++ b/examples/shapes/src/primitives/Rectangle.hpp @@ -1,5 +1,5 @@ -#ifndef _Rectangle_HPP -#define _Rectangle_HPP +#ifndef _RECTANGLE_HPP +#define _RECTANGLE_HPP #include "Shape.hpp" @@ -23,4 +23,4 @@ class Rectangle : public Shape<2> }; -#endif // _Rectangle_HPP +#endif // _RECTANGLE_HPP diff --git a/examples/shapes/src/primitives/Shape.cpp b/examples/shapes/src/primitives/Shape.cpp index 626ecb6..4ee1a8a 100644 --- a/examples/shapes/src/primitives/Shape.cpp +++ b/examples/shapes/src/primitives/Shape.cpp @@ -1,42 +1,47 @@ +#include + #include "Shape.hpp" -template -Shape::Shape() : - mIndex(0), - mVertices() +template +Shape::Shape() : mIndex(0), + mVertices() { - } -template +template Shape::~Shape() { - } -template +template unsigned Shape::GetIndex() const { return mIndex; } -template -const std::vector > >& Shape::rGetVertices() const +template +const std::vector>> &Shape::rGetVertices() const { return mVertices; } -template +template void Shape::SetIndex(unsigned index) { mIndex = index; } -template -void Shape::SetVertices(const std::vector > >& rVertices) +template +void Shape::SetVertices(const std::vector>> &rVertices) { mVertices = rVertices; } +template +void Shape::AddVertex(std::shared_ptr> point) +{ + this->mVertices.push_back(point); +} + template class Shape<2>; template class Shape<3>; diff --git a/examples/shapes/src/primitives/Shape.hpp b/examples/shapes/src/primitives/Shape.hpp index 798686b..19538c7 100644 --- a/examples/shapes/src/primitives/Shape.hpp +++ b/examples/shapes/src/primitives/Shape.hpp @@ -8,11 +8,10 @@ /** * A DIM dimensional shape */ -template +template class Shape { protected: - /** * The shape index */ @@ -21,10 +20,9 @@ class Shape /** * The shape vertices */ - std::vector > > mVertices; + std::vector>> mVertices; public: - /** * Default Constructor */ @@ -43,7 +41,7 @@ class Shape /** * Return the shape vertices */ - const std::vector > >& rGetVertices() const; + const std::vector>> &rGetVertices() const; /** * Set the shape index @@ -53,8 +51,12 @@ class Shape /** * Set the shape vertices */ - void SetVertices(const std::vector > >& rVertices); -}; + void SetVertices(const std::vector>> &rVertices); + /** + * Add a single vertex to the shape + */ + void AddVertex(std::shared_ptr> point = std::make_shared>()); +}; -#endif // _SHAPE_HPP +#endif // _SHAPE_HPP diff --git a/examples/shapes/src/python/test/test_classes.py b/examples/shapes/src/python/test/test_classes.py index a173760..3f9a134 100644 --- a/examples/shapes/src/python/test/test_classes.py +++ b/examples/shapes/src/python/test/test_classes.py @@ -9,6 +9,9 @@ class TestClasses(unittest.TestCase): def testGeometry(self): + p0 = pyshapes.geometry.Point2() + self.assertTrue(p0.GetLocation() == [0.0, 0.0]) + p1 = pyshapes.geometry.Point2(0.0, 0.0) p2 = pyshapes.geometry.Point2(1.0, 0.0) p3 = pyshapes.geometry.Point2(0.0, 1.0) diff --git a/examples/shapes/wrapper/geometry/Point2.cppwg.cpp b/examples/shapes/wrapper/geometry/Point2.cppwg.cpp index cf20feb..6ca7333 100644 --- a/examples/shapes/wrapper/geometry/Point2.cppwg.cpp +++ b/examples/shapes/wrapper/geometry/Point2.cppwg.cpp @@ -11,7 +11,7 @@ PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); void register_Point2_class(py::module &m){ py::class_ >(m, "Point2") .def(py::init< >()) - .def(py::init(), py::arg("x"), py::arg("y"), py::arg("z") = 0.) + .def(py::init(), py::arg("x"), py::arg("y"), py::arg("z") = (2 - 2)) .def( "GetLocation", (::std::array(Point2::*)() const ) &Point2::GetLocation, @@ -19,7 +19,7 @@ py::class_ >(m, "Point2") .def( "rGetLocation", (::std::array const &(Point2::*)() const ) &Point2::rGetLocation, - " " ) + " " , py::return_value_policy::reference_internal) .def( "GetIndex", (unsigned int(Point2::*)() const ) &Point2::GetIndex, diff --git a/examples/shapes/wrapper/geometry/Point3.cppwg.cpp b/examples/shapes/wrapper/geometry/Point3.cppwg.cpp index af5fd03..b489ecb 100644 --- a/examples/shapes/wrapper/geometry/Point3.cppwg.cpp +++ b/examples/shapes/wrapper/geometry/Point3.cppwg.cpp @@ -11,7 +11,7 @@ PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr); void register_Point3_class(py::module &m){ py::class_ >(m, "Point3") .def(py::init< >()) - .def(py::init(), py::arg("x"), py::arg("y"), py::arg("z") = 0.) + .def(py::init(), py::arg("x"), py::arg("y"), py::arg("z") = (3 - 3)) .def( "GetLocation", (::std::array(Point3::*)() const ) &Point3::GetLocation, @@ -19,7 +19,7 @@ py::class_ >(m, "Point3") .def( "rGetLocation", (::std::array const &(Point3::*)() const ) &Point3::rGetLocation, - " " ) + " " , py::return_value_policy::reference_internal) .def( "GetIndex", (unsigned int(Point3::*)() const ) &Point3::GetIndex, diff --git a/examples/shapes/wrapper/package_info.yaml b/examples/shapes/wrapper/package_info.yaml index 43711d2..51efc20 100644 --- a/examples/shapes/wrapper/package_info.yaml +++ b/examples/shapes/wrapper/package_info.yaml @@ -1,5 +1,30 @@ -name: pyshapes # Unique name prepended to all modules +name: pyshapes # Package name: prepended to all modules. + +# Smart pointer type for PYBIND11_DECLARE_HOLDER_TYPE in all wrappers. smart_ptr_type: std::shared_ptr +# Default value of pybind11::return_value_policy for pointers. +pointer_call_policy: reference +# Default value of pybind11::return_value_policy for references. +reference_call_policy: reference_internal + +# Set False to not include the common include file (all headers) in all wrappers. +common_include_file: True +# Headers to include in all wrappers. +source_includes: + - + +# Exclude default arguments from wrapped methods. +exclude_default_args: False + +# Exclude any methods that have these arg types. +# arg_type_excludes: +# - double + +# Exclude any constructors that have these arg types. +# constructor_arg_type_excludes: +# - double + +# Signature/replacement settings for explicit template instantiations. template_substitutions: - signature: replacement: [[2], [3]] @@ -7,20 +32,41 @@ template_substitutions: replacement: [[2, 2], [3, 3]] modules: - - name: math_funcs # Name of the module - source_locations: # Only use decls with hpp files found in these directories. Leave blank for 'cppwg_ALL' - classes: # Classes to include. Blank means none, cppwg_ALL means any found - free_functions: cppwg_ALL # Free functions to include. Blank means none, cppwg_ALL means any found + - name: math_funcs # Module name + # Restrict to headers from these directories. Blank means unrestricted. + source_locations: + # List of classes to wrap. Blank means none, CPPWG_ALL means discover all. + classes: + # List of free functions to wrap. Blank means none, CPPWG_ALL means discover all. + free_functions: CPPWG_ALL + - name: geometry source_locations: classes: - name: Point + # Name of class source file. Not required if class name matches file name. + source_file: Point.hpp + # Additional headers to include in this class wrapper. + source_includes: + - + # List of methods that should not be wrapped. + excluded_methods: + - ExcludedMethod + + # Path to a custom script for generating wrappers for this class. + # CPPWG_SOURCEROOT points to the supplied source root directory. + # custom_generator: "CPPWG_SOURCEROOT/point_generator.py" + + # Custom C++ code to place at the top of the class wrapper. + # prefix_code: + - name: primitives source_locations: classes: - name: Shape - name: Cuboid - name: Rectangle + - name: mesh source_locations: classes: diff --git a/examples/shapes/wrapper/primitives/Shape2.cppwg.cpp b/examples/shapes/wrapper/primitives/Shape2.cppwg.cpp index 76d5474..4bb1651 100644 --- a/examples/shapes/wrapper/primitives/Shape2.cppwg.cpp +++ b/examples/shapes/wrapper/primitives/Shape2.cppwg.cpp @@ -18,7 +18,7 @@ py::class_ >(m, "Shape2") .def( "rGetVertices", (::std::vector>> const &(Shape2::*)() const ) &Shape2::rGetVertices, - " " ) + " " , py::return_value_policy::reference_internal) .def( "SetIndex", (void(Shape2::*)(unsigned int)) &Shape2::SetIndex, @@ -27,5 +27,9 @@ py::class_ >(m, "Shape2") "SetVertices", (void(Shape2::*)(::std::vector>> const &)) &Shape2::SetVertices, " " , py::arg("rVertices") ) + .def( + "AddVertex", + (void(Shape2::*)(::std::shared_ptr>)) &Shape2::AddVertex, + " " , py::arg("point") = std::make_shared>() ) ; } diff --git a/examples/shapes/wrapper/primitives/Shape3.cppwg.cpp b/examples/shapes/wrapper/primitives/Shape3.cppwg.cpp index b3e81cf..a43674f 100644 --- a/examples/shapes/wrapper/primitives/Shape3.cppwg.cpp +++ b/examples/shapes/wrapper/primitives/Shape3.cppwg.cpp @@ -18,7 +18,7 @@ py::class_ >(m, "Shape3") .def( "rGetVertices", (::std::vector>> const &(Shape3::*)() const ) &Shape3::rGetVertices, - " " ) + " " , py::return_value_policy::reference_internal) .def( "SetIndex", (void(Shape3::*)(unsigned int)) &Shape3::SetIndex, @@ -27,5 +27,9 @@ py::class_ >(m, "Shape3") "SetVertices", (void(Shape3::*)(::std::vector>> const &)) &Shape3::SetVertices, " " , py::arg("rVertices") ) + .def( + "AddVertex", + (void(Shape3::*)(::std::shared_ptr>)) &Shape3::AddVertex, + " " , py::arg("point") = std::make_shared>() ) ; }