diff --git a/robotpy_build/autowrap/context.py b/robotpy_build/autowrap/context.py index 63f13c87..c42079fc 100644 --- a/robotpy_build/autowrap/context.py +++ b/robotpy_build/autowrap/context.py @@ -287,6 +287,12 @@ class BaseClassData: Render data for each base that a class inherits """ + #: Just the class name + cls_name: str + + #: This ends with :: + namespace_: str + #: C++ name, including all known components full_cpp_name: str # was x_qualname diff --git a/robotpy_build/autowrap/cxxparser.py b/robotpy_build/autowrap/cxxparser.py index ad27ca4c..3603a797 100644 --- a/robotpy_build/autowrap/cxxparser.py +++ b/robotpy_build/autowrap/cxxparser.py @@ -191,7 +191,9 @@ def _count_and_unwrap( assert False -def _fmt_base_name(typename: PQName) -> typing.Tuple[str, str, str, typing.List[str]]: +def _fmt_base_name( + typename: PQName, +) -> typing.Tuple[str, str, str, str, str, typing.List[str]]: all_parts = [] nameonly_parts = [] @@ -209,18 +211,22 @@ def _fmt_base_name(typename: PQName) -> typing.Tuple[str, str, str, typing.List[ if last_segment.specialization: most_parts = all_parts[:-1] + ns_parts = all_parts[:-1] all_parts.append(last_segment.format()) most_parts.append(last_segment.name) tparam_list = [arg.format() for arg in last_segment.specialization.args] else: + ns_parts = all_parts[:] all_parts.append(last_segment.name) most_parts = all_parts tparam_list = [] return ( + last_segment.name, "::".join(most_parts), "::".join(all_parts), "::".join(nameonly_parts), + "::".join(ns_parts), tparam_list, ) @@ -287,7 +293,6 @@ class ClassStateData(typing.NamedTuple): defer_protected_fields: typing.List[Field] # Needed for trampoline - cls_cpp_identifier: str template_argument_list: str base_template_params: str base_template_args: str @@ -751,7 +756,6 @@ def on_class_start(self, state: AWClassBlockState) -> typing.Optional[bool]: defer_private_virtual_methods=[], defer_protected_fields=[], # Trampoline data - cls_cpp_identifier=cls_cpp_identifier, template_argument_list=template_argument_list, base_template_args=base_template_args_s, base_template_params=base_template_params_s, @@ -824,15 +828,20 @@ def _process_class_bases( if base.access == "private": continue - cpp_name, cpp_name_w_templates, dep_cpp_name, tparam_list = _fmt_base_name( - base.typename - ) + ( + cpp_name, + full_cpp_name, + cpp_name_w_templates, + dep_cpp_name, + base_ns, + tparam_list, + ) = _fmt_base_name(base.typename) if ignored_bases.pop(cpp_name_w_templates, None): continue # Sometimes, we can't guess all the information about the base, so the # user needs to specify it explicitly. - user_bqual = class_data.base_qualnames.get(cpp_name) + user_bqual = class_data.base_qualnames.get(full_cpp_name) if user_bqual: cpp_name_w_templates = user_bqual # TODO: sometimes need to add this to pybase_params, but @@ -840,12 +849,17 @@ def _process_class_bases( # obscure, going to omit it for now. tp = user_bqual.find("<") if tp == -1: - cpp_name = user_bqual + full_cpp_name = user_bqual template_params = "" else: - cpp_name = user_bqual[:tp] + full_cpp_name = user_bqual[:tp] template_params = user_bqual[tp + 1 : -1] - dep_cpp_name = cpp_name + dep_cpp_name = full_cpp_name + ns_idx = full_cpp_name.rfind("::") + if ns_idx == -1: + base_ns = "" + else: + base_ns = full_cpp_name[:ns_idx] else: # TODO: we don't handle nested child classes with templates here # ... but that has to be rather obscure? @@ -858,15 +872,21 @@ def _process_class_bases( # If no explicit namespace specified, we assume base classes # live in the same namespace as the class if len(base.typename.segments) == 1: - cpp_name = f"{cls_namespace}::{cpp_name}" + base_ns = cls_namespace + full_cpp_name = f"{cls_namespace}::{full_cpp_name}" cpp_name_w_templates = f"{cls_namespace}::{cpp_name_w_templates}" dep_cpp_name = f"{cls_namespace}::{dep_cpp_name}" - base_identifier = cpp_name.translate(_qualname_trans) + base_identifier = full_cpp_name.translate(_qualname_trans) + + if base_ns: + base_ns = f"{base_ns}::" bases.append( BaseClassData( - full_cpp_name=cpp_name, + cls_name=cpp_name, + namespace_=base_ns, + full_cpp_name=full_cpp_name, full_cpp_name_w_templates=cpp_name_w_templates, full_cpp_name_identifier=base_identifier, dep_cpp_name=dep_cpp_name, @@ -1242,8 +1262,8 @@ def on_class_end(self, state: AWClassBlockState) -> None: if cdata.template_argument_list: tmpl = f", {cdata.template_argument_list}" - trampoline_cfg = f"rpygen::PyTrampolineCfg_{cdata.cls_cpp_identifier}<{cdata.template_argument_list}>" - tname = f"rpygen::PyTrampoline_{cdata.cls_cpp_identifier}" + trampoline_cfg = f"{ctx.namespace}::PyTrampolineCfg_{ctx.cpp_name}<{cdata.template_argument_list}>" + tname = f"{ctx.namespace}::PyTrampoline_{ctx.cpp_name}" tvar = f"{ctx.cpp_name}_Trampoline" ctx.trampoline = TrampolineData( diff --git a/robotpy_build/autowrap/render_cls_rpy_include.py b/robotpy_build/autowrap/render_cls_rpy_include.py index 57e15716..b6694684 100644 --- a/robotpy_build/autowrap/render_cls_rpy_include.py +++ b/robotpy_build/autowrap/render_cls_rpy_include.py @@ -101,10 +101,8 @@ def _render_cls_trampoline( for base in cls.bases: r.writeln(f"#include ") - r.writeln("\nnamespace rpygen {") - if cls.namespace: - r.writeln(f"\nusing namespace {cls.namespace};") + r.writeln(f"\nnamespace {cls.namespace.strip('::')} {{") if hctx.using_declarations: r.writeln() @@ -122,16 +120,16 @@ def _render_cls_trampoline( # r.writeln( - f"\ntemplate <{postcomma(template_parameter_list)}typename CfgBase = EmptyTrampolineCfg>" + f"\ntemplate <{postcomma(template_parameter_list)}typename CfgBase = rpygen::EmptyTrampolineCfg>" ) if cls.bases: - r.writeln(f"struct PyTrampolineCfg_{cls.full_cpp_name_identifier} :") + r.writeln(f"struct PyTrampolineCfg_{cls.cpp_name} :") with r.indent(): for base in cls.bases: r.writeln( - f"PyTrampolineCfg_{base.full_cpp_name_identifier}<{postcomma(base.template_params)}" + f"{base.namespace_}PyTrampolineCfg_{base.cls_name}<{postcomma(base.template_params)}" ) r.writeln("CfgBase") @@ -139,7 +137,7 @@ def _render_cls_trampoline( for base in cls.bases: r.writeln(">") else: - r.writeln(f"struct PyTrampolineCfg_{cls.full_cpp_name_identifier} : CfgBase") + r.writeln(f"struct PyTrampolineCfg_{cls.cpp_name} : CfgBase") r.writeln("{") @@ -164,11 +162,11 @@ def _render_cls_trampoline( r.writeln( f"template " ) - r.writeln(f"using PyTrampolineBase_{cls.full_cpp_name_identifier} =") + r.writeln(f"using PyTrampolineBase_{cls.cpp_name} =") for base in cls.bases: r.rel_indent(2) - r.writeln(f"PyTrampoline_{base.full_cpp_name_identifier}<") + r.writeln(f"{base.namespace_}PyTrampoline_{base.cls_name}<") with r.indent(): r.writeln("PyTrampolineBase") @@ -184,8 +182,8 @@ def _render_cls_trampoline( ; template - struct PyTrampoline_{ cls.full_cpp_name_identifier } : PyTrampolineBase_{ cls.full_cpp_name_identifier } {{ - using PyTrampolineBase_{ cls.full_cpp_name_identifier }::PyTrampolineBase_{ cls.full_cpp_name_identifier }; + struct PyTrampoline_{ cls.cpp_name } : PyTrampolineBase_{ cls.cpp_name } {{ + using PyTrampolineBase_{ cls.cpp_name }::PyTrampolineBase_{ cls.cpp_name }; """ ) @@ -194,7 +192,7 @@ def _render_cls_trampoline( r.write_trim( f""" template - struct PyTrampoline_{ cls.full_cpp_name_identifier } : PyTrampolineBase {{ + struct PyTrampoline_{ cls.cpp_name } : PyTrampolineBase {{ using PyTrampolineBase::PyTrampolineBase; """ ) @@ -234,11 +232,11 @@ def _render_cls_trampoline( with r.indent(): all_decls = ", ".join(p.decl for p in fn.all_params) all_names = ", ".join(p.arg_name for p in fn.all_params) - r.writeln(f"PyTrampoline_{cls.full_cpp_name_identifier}({all_decls}) :") + r.writeln(f"PyTrampoline_{cls.cpp_name}({all_decls}) :") if cls.bases: r.writeln( - f" PyTrampolineBase_{cls.full_cpp_name_identifier}({all_names})" + f" PyTrampolineBase_{cls.cpp_name}({all_names})" ) else: r.writeln(f" PyTrampolineBase({all_names})") @@ -284,7 +282,10 @@ def _render_cls_trampoline( r.writeln() r.write_trim(trampoline.inline_code) - r.writeln("};\n\n}; // namespace rpygen") + r.writeln("};\n\n") + + if cls.namespace: + r.writeln(f"}}; // namespace {cls.namespace}") def _render_cls_trampoline_virtual_method( diff --git a/tests/cpp/pyproject.toml.tmpl b/tests/cpp/pyproject.toml.tmpl index 5d911165..f22d1a3b 100644 --- a/tests/cpp/pyproject.toml.tmpl +++ b/tests/cpp/pyproject.toml.tmpl @@ -61,6 +61,7 @@ generate = [ { lifetime = "lifetime.h" }, { nested = "nested.h" }, { ns_class = "ns_class.h" }, + { ns_hidden = "ns_hidden.h" }, { operators = "operators.h" }, { overloads = "overloads.h" }, { parameters = "parameters.h" }, diff --git a/tests/cpp/rpytest/ft/include/ns_hidden.h b/tests/cpp/rpytest/ft/include/ns_hidden.h new file mode 100644 index 00000000..75c8b4f6 --- /dev/null +++ b/tests/cpp/rpytest/ft/include/ns_hidden.h @@ -0,0 +1,31 @@ +#pragma once + +namespace n { + enum class E { + Item, + }; +}; + +namespace o { + struct O { + O() = default; + virtual ~O() = default; + }; + + class AnotherC; +}; + +namespace n::h { + class C : public o::O { + public: + // E is resolved here because it's in the parent namespace but our + // trampoline was originally in a different namespace and failed + virtual E fn() { return E::Item; } + }; +}; + +struct o::AnotherC { + AnotherC() = default; + virtual ~AnotherC() = default; + virtual int fn() { return 1; } +};