Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,31 @@ r = Rectangle(4, 5)
directories to your build's include paths, exactly as before. Free functions
are not covered — a free function using a caster type still needs the header
added manually. See `examples/cells/dynamic/config.yaml`.
- A wrapped signature may use another **project type** whose definition the
class's own header only forward-declares — e.g. a `MeshFactory<MESH>` whose
`generateMesh()` returns a `PottsMesh` it never `#include`s. The wrapper then
needs that type's header (unless you use `common_include_file`, which pulls in
everything). Rather than listing it by hand under `source_includes`, set
`auto_includes: True` (at package, module or class level; it inherits down the
info tree, and is off by default):

```yaml
modules:
- name: mymod
classes:
- name: MeshFactory
auto_includes: True # resolves PottsMesh.hpp from the signature
```

cppwg then scans each such class's wrapped method/constructor signatures, and
for every **project type** it finds (a class defined under the module
`source_locations`) adds that class's header to the wrapper. Only project types
are resolved — library types (`std::`, boost, PETSc, VTK, …) are left alone —
and a type name that is defined in more than one header is treated as ambiguous
and left for a manual `source_includes`. It is a no-op with
`common_include_file`, and does not cover types that appear only in
hand-written `custom_generator` code (cppwg cannot see those). See the
`MeshFactory` class in `examples/cells/dynamic/config.yaml`.
- A wrapped class can inherit from a base class wrapped in a **different
module**, as long as that base is registered somewhere that gets imported
first. Opt in per module with `imports`, which lists the Python modules to
Expand Down
6 changes: 6 additions & 0 deletions cppwg/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,12 @@ def generate(self) -> None:
# by those steps; sorting earlier would use incomplete inheritance info.
self.package_info.sort_classes()

# For classes that opt into auto_includes, resolve the headers of the
# project types their wrapped signatures use, so those headers are added
# to the class wrappers automatically. Runs after the wrapped set is final
# so the resolved headers reflect exactly what will be wrapped.
self.package_info.resolve_auto_includes()

# Re-write the header collection so it reflects the final wrapped set:
# base-class-discovered classes are added and pruned instantiations are
# removed (the first write, before parsing, could not know either). The
Expand Down
14 changes: 14 additions & 0 deletions cppwg/info/base_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ class BaseInfo(ABC):
Exclude any method, constructor or free function with an argument of one
of these types. Patterns match a type as a whole token (so `Node` does
not match `AbstractNode`).
auto_includes : bool | None
Automatically add `#include`s for the project types a class's wrapped
method/constructor signatures use, when the class's own header only
forward-declares them (so the wrapper still compiles without listing them
by hand under `source_includes`). Only project types - classes defined
under the module `source_locations` - are resolved; library types are
left alone. None (the default) means inherit from further up the info
tree, where it is treated as off (opt-in); set it to True or False at any
level (package, module or class). Has no effect with
`common_include_file` (the common header already includes everything).
calldef_excludes : list[str]
Deprecated: use arg_type_excludes and/or return_type_excludes. Kept for
backwards compatibility; treated as both arg_type_excludes and
Expand Down Expand Up @@ -114,6 +124,9 @@ def __init__(self, name: str, info_config: dict[str, Any] | None = None) -> None
self.calldef_excludes: list[str] = []
self.constructor_arg_type_excludes: list[str] = []
self.constructor_signature_excludes: list[list[str]] = []
# Tri-state (None inherits): automatically add includes for the project
# types used in a class's wrapped signatures. Off unless set.
self.auto_includes: bool | None = None
# Tri-state: None means inherit from further up the info tree, so that a
# package/module-level setting propagates to classes (hierarchy_attribute
# stops at the first non-None value it finds ascending the tree).
Expand Down Expand Up @@ -157,6 +170,7 @@ def __init__(self, name: str, info_config: dict[str, Any] | None = None) -> None
if info_config:
for key in [
"arg_type_excludes",
"auto_includes",
"calldef_excludes",
"constructor_arg_type_excludes",
"constructor_signature_excludes",
Expand Down
5 changes: 5 additions & 0 deletions cppwg/info/class_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ def __init__(self, name: str, class_config: dict[str, Any] | None = None):
self.cpp_names: list[str] = []
self.py_names: list[str] = []

# Headers auto-resolved for the project types this class's wrapped
# signatures use (populated by PackageInfo.resolve_auto_includes when the
# auto_includes option is enabled; empty otherwise).
self.auto_include_headers: list[str] = []

# Cache for template parameter names read from the source header, so the
# header is not read twice during discovery (once to decide whether the
# class is templated, once to record its parameter names).
Expand Down
Loading
Loading