From 2e1506c1896f90456e5b3092ba7c7200c1b1e2e1 Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Sun, 6 Aug 2023 13:43:52 +0100 Subject: [PATCH] Add support for using declarations to introduce templated members Add support for using declarations to introduce templated member methods and for inheriting templated constructors, such as: struct Base { // templated constructor template Base(const T &t, const char *s) {} // templated member method template void template_method(const T &t, const char *s) {} }; %template(Base) Base::Base; %template(template_method) Base::template_method; struct Derived : Base { using Base::Base; using Base::template_method; }; Previously the templated methods and constructors were ignored and not introduced into the Derived class. --- CHANGES.current | 22 ++++++++ Examples/test-suite/cpp11_using_constructor.i | 51 +++++++++++++------ ...pp11_director_using_constructor_runme.java | 34 ++++++++++++- .../java/cpp11_using_constructor_runme.java | 38 ++++++++++++-- .../cpp11_director_using_constructor_runme.py | 24 ++++++++- .../python/cpp11_using_constructor_runme.py | 24 ++++++++- Source/Modules/allocate.cxx | 36 ++++++++++--- 7 files changed, 199 insertions(+), 30 deletions(-) diff --git a/CHANGES.current b/CHANGES.current index 08a03faae..2afdc0ede 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -7,6 +7,28 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/ Version 4.2.0 (in progress) =========================== +2023-08-06: wsfulton + Add support for using declarations to introduce templated member + methods and for inheriting templated constructors, such as: + + struct Base { + // templated constructor + template Base(const T &t, const char *s) {} + // templated member method + template void template_method(const T &t, const char *s) {} + }; + + %template(Base) Base::Base; + %template(template_method) Base::template_method; + + struct Derived : Base { + using Base::Base; + using Base::template_method; + }; + + Previously the templated methods and constructors were ignored and + not introduced into the Derived class. + 2023-08-04: wsfulton Fix using declarations for inheritance hierarchies more than two deep and the using declarations are overloaded. Using declarations diff --git a/Examples/test-suite/cpp11_using_constructor.i b/Examples/test-suite/cpp11_using_constructor.i index 1c1ca2c42..b28be61d7 100644 --- a/Examples/test-suite/cpp11_using_constructor.i +++ b/Examples/test-suite/cpp11_using_constructor.i @@ -510,35 +510,56 @@ struct TemplPublicDerived6 : TemplPublicBase6 { %template(TemplPublicDerived6Int) TemplPublicDerived6; -// Templated constructors +// Templated constructors (public) %inline %{ struct TemplateConstructor1Base { virtual ~TemplateConstructor1Base() {} +public: // No implicit constructor - template TemplateConstructor1Base(const T &t, const char *s) {} - virtual void meth() {} + template TemplateConstructor1Base(T t, const char *s) {} + template void template_method(T t, const char *s) {} + virtual void normal_method() {} }; %} %template(TemplateConstructor1Base) TemplateConstructor1Base::TemplateConstructor1Base; +%template(TemplateConstructor1Base) TemplateConstructor1Base::TemplateConstructor1Base; +%template(TemplateConstructor1Base) TemplateConstructor1Base::TemplateConstructor1Base; +%template(template_method) TemplateConstructor1Base::template_method; +%template(template_method) TemplateConstructor1Base::template_method; %inline %{ struct TemplateConstructor1Derived : TemplateConstructor1Base { + using TemplateConstructor1Base::normal_method; + // Note: The two using declarations below automatically introduce the templated names without an explicit %template(), see allocate.cxx using TemplateConstructor1Base::TemplateConstructor1Base; - using TemplateConstructor1Base::meth; + using TemplateConstructor1Base::template_method; }; %} -// TODO: Missing constructors, below probably ought to work using instantiation as follows: -//%template(TemplateConstructor1Derived) TemplateConstructor1Derived::TemplateConstructor1Derived; - -%{ -void tester() { - TemplateConstructor1Derived tc = TemplateConstructor1Derived(123, "hi"); - tc.meth(); - // Note not valid c++: - // TemplateConstructor1Derived tc2 = TemplateConstructor1Derived::TemplateConstructor1Derived(123, "hi"); -} +// Templated constructors (protected) +%inline %{ +struct TemplateConstructor2Base { + virtual ~TemplateConstructor2Base() {} +protected: + // No implicit constructor + template TemplateConstructor2Base(T t, const char *s) {} + template void template_method(T t, const char *s) {} + virtual void normal_method() {} +}; %} -// Note that templated methods also not working with using declarations for inheriting from base templated methods +%template(TemplateConstructor2Base) TemplateConstructor2Base::TemplateConstructor2Base; +%template(TemplateConstructor2Base) TemplateConstructor2Base::TemplateConstructor2Base; +%template(TemplateConstructor2Base) TemplateConstructor2Base::TemplateConstructor2Base; +%template(template_method) TemplateConstructor2Base::template_method; +%template(template_method) TemplateConstructor2Base::template_method; + +%inline %{ +struct TemplateConstructor2Derived : TemplateConstructor2Base { + using TemplateConstructor2Base::normal_method; + using TemplateConstructor2Base::TemplateConstructor2Base; // introduces protected constructors + using TemplateConstructor2Base::template_method; // introduces public templated methods + TemplateConstructor2Derived() : TemplateConstructor2Derived(0, "") {} // provide one public constructor for testing +}; +%} diff --git a/Examples/test-suite/java/cpp11_director_using_constructor_runme.java b/Examples/test-suite/java/cpp11_director_using_constructor_runme.java index 1c03a6902..e1508a975 100644 --- a/Examples/test-suite/java/cpp11_director_using_constructor_runme.java +++ b/Examples/test-suite/java/cpp11_director_using_constructor_runme.java @@ -114,7 +114,7 @@ public class cpp11_director_using_constructor_runme { DeepProtectedBase3 dbp3 = new DeepProtectedBase3(11, 22, 33); // Missing base - // new HiddenDerived1(); + new HiddenDerived1(); // Templates and public base constructors (derive from non-template) new TemplatePublicDerived1Int(0, "hi").meth(); @@ -135,6 +135,38 @@ public class cpp11_director_using_constructor_runme { new TemplPublicDerived5Int().meth(); new TemplPublicDerived6Int(0, "hi").meth(); new TemplPublicDerived6Int().meth(); + + // Templated constructors (public) + TemplateConstructor1Base tcb = new TemplateConstructor1Base(0, "hi"); + tcb = new TemplateConstructor1Base("hi", "hi"); + tcb = new TemplateConstructor1Base(11.1, "hi"); + tcb.normal_method(); + tcb.template_method(0, "hi"); + tcb.template_method("hey", "ho"); + + TemplateConstructor1Derived tcd1 = new TemplateConstructor1Derived(0, "hi"); + tcd1 = new TemplateConstructor1Derived("hi", "hi"); + tcd1 = new TemplateConstructor1Derived(11.1, "hi"); + // Not the best test as these are also in the base class, hence use also introspection below + tcd1.normal_method(); + tcd1.template_method(0, "hi"); + tcd1.template_method("hey", "ho"); + + // Templated methods + // Introspection to make sure these are actually generated in the derived class + try { + TemplateConstructor1Derived.class.getDeclaredMethod("normal_method", (java.lang.Class[])null); + TemplateConstructor1Derived.class.getDeclaredMethod("template_method", new java.lang.Class[]{String.class, String.class}); + TemplateConstructor1Derived.class.getDeclaredMethod("template_method", new java.lang.Class[]{int.class, String.class}); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + + // Templated constructors (protected) + TemplateConstructor2Derived tcd2 = new TemplateConstructor2Derived(); + tcd2.normal_method(); + tcd2.template_method(0, "hi"); + tcd2.template_method("hey", "ho"); } // diff --git a/Examples/test-suite/java/cpp11_using_constructor_runme.java b/Examples/test-suite/java/cpp11_using_constructor_runme.java index 10ebc9356..fca19f19b 100644 --- a/Examples/test-suite/java/cpp11_using_constructor_runme.java +++ b/Examples/test-suite/java/cpp11_using_constructor_runme.java @@ -1,5 +1,6 @@ import cpp11_using_constructor.*; +import java.lang.reflect.*; public class cpp11_using_constructor_runme { @@ -112,7 +113,7 @@ public class cpp11_using_constructor_runme { DeepProtectedBase3 dbp3 = new DeepProtectedBase3(11, 22, 33); // Missing base - // new HiddenDerived1(); + new HiddenDerived1(); // Templates and public base constructors (derive from non-template) new TemplatePublicDerived1Int(0, "hi").meth(); @@ -134,9 +135,36 @@ public class cpp11_using_constructor_runme { new TemplPublicDerived6Int(0, "hi").meth(); new TemplPublicDerived6Int().meth(); - // Templated constructors - new TemplateConstructor1Base(0, "hi").meth(); - // TODO: missing constructor... - // new TemplateConstructor1Derived(0, "hi").meth(); + // Templated constructors (public) + TemplateConstructor1Base tcb = new TemplateConstructor1Base(0, "hi"); + tcb = new TemplateConstructor1Base("hi", "hi"); + tcb = new TemplateConstructor1Base(11.1, "hi"); + tcb.normal_method(); + tcb.template_method(0, "hi"); + tcb.template_method("hey", "ho"); + + TemplateConstructor1Derived tcd1 = new TemplateConstructor1Derived(0, "hi"); + tcd1 = new TemplateConstructor1Derived("hi", "hi"); + tcd1 = new TemplateConstructor1Derived(11.1, "hi"); + // Not the best test as these are also in the base class, hence use also introspection below + tcd1.normal_method(); + tcd1.template_method(0, "hi"); + tcd1.template_method("hey", "ho"); + + // Templated methods + // Introspection to make sure these are actually generated in the derived class + try { + TemplateConstructor1Derived.class.getDeclaredMethod("normal_method", (java.lang.Class[])null); + TemplateConstructor1Derived.class.getDeclaredMethod("template_method", new java.lang.Class[]{String.class, String.class}); + TemplateConstructor1Derived.class.getDeclaredMethod("template_method", new java.lang.Class[]{int.class, String.class}); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + + // Templated constructors (protected) + TemplateConstructor2Derived tcd2 = new TemplateConstructor2Derived(); + tcd2.normal_method(); + tcd2.template_method(0, "hi"); + tcd2.template_method("hey", "ho"); } } diff --git a/Examples/test-suite/python/cpp11_director_using_constructor_runme.py b/Examples/test-suite/python/cpp11_director_using_constructor_runme.py index 0b83209c9..84238a281 100644 --- a/Examples/test-suite/python/cpp11_director_using_constructor_runme.py +++ b/Examples/test-suite/python/cpp11_director_using_constructor_runme.py @@ -100,7 +100,7 @@ db3 = DeepBase3(11, 22, 33) dbp3 = DeepProtectedBase3(11, 22, 33) # Missing base -# HiddenDerived1() +HiddenDerived1() # Templates and public base constructors (derive from non-template) TemplatePublicDerived1Int(0, "hi").meth() @@ -121,3 +121,25 @@ TemplPublicDerived4Int().meth() TemplPublicDerived5Int().meth() TemplPublicDerived6Int(0, "hi").meth() TemplPublicDerived6Int().meth() + +# Templated constructors (public) +tcb = TemplateConstructor1Base(0, "hi") +tcb = TemplateConstructor1Base("hi", "hi") +tcb = TemplateConstructor1Base(11.1, "hi") +tcb.normal_method() +tcb.template_method(0, "hi") +tcb.template_method("hey", "ho") + +tcd1 = TemplateConstructor1Derived(0, "hi") +tcd1 = TemplateConstructor1Derived("hi", "hi") +tcd1 = TemplateConstructor1Derived(11.1, "hi") +# Not the best test as these are also in the base class, (should use introspection to check) +tcd1.normal_method() +tcd1.template_method(0, "hi") +tcd1.template_method("hey", "ho") + +# Templated constructors (protected) +tcd2 = TemplateConstructor2Derived() +tcd2.normal_method() +tcd2.template_method(0, "hi") +tcd2.template_method("hey", "ho") diff --git a/Examples/test-suite/python/cpp11_using_constructor_runme.py b/Examples/test-suite/python/cpp11_using_constructor_runme.py index ec3a3240b..06f8f9af2 100644 --- a/Examples/test-suite/python/cpp11_using_constructor_runme.py +++ b/Examples/test-suite/python/cpp11_using_constructor_runme.py @@ -100,7 +100,7 @@ db3 = DeepBase3(11, 22, 33) dbp3 = DeepProtectedBase3(11, 22, 33) # Missing base -# HiddenDerived1() +HiddenDerived1() # Templates and public base constructors (derive from non-template) TemplatePublicDerived1Int(0, "hi").meth() @@ -121,3 +121,25 @@ TemplPublicDerived4Int().meth() TemplPublicDerived5Int().meth() TemplPublicDerived6Int(0, "hi").meth() TemplPublicDerived6Int().meth() + +# Templated constructors (public) +tcb = TemplateConstructor1Base(0, "hi") +tcb = TemplateConstructor1Base("hi", "hi") +tcb = TemplateConstructor1Base(11.1, "hi") +tcb.normal_method() +tcb.template_method(0, "hi") +tcb.template_method("hey", "ho") + +tcd1 = TemplateConstructor1Derived(0, "hi") +tcd1 = TemplateConstructor1Derived("hi", "hi") +tcd1 = TemplateConstructor1Derived(11.1, "hi") +# Not the best test as these are also in the base class, (should use introspection to check) +tcd1.normal_method() +tcd1.template_method(0, "hi") +tcd1.template_method("hey", "ho") + +# Templated constructors (protected) +tcd2 = TemplateConstructor2Derived() +tcd2.normal_method() +tcd2.template_method(0, "hi") +tcd2.template_method("hey", "ho") diff --git a/Source/Modules/allocate.cxx b/Source/Modules/allocate.cxx index f1013a022..004036c26 100644 --- a/Source/Modules/allocate.cxx +++ b/Source/Modules/allocate.cxx @@ -952,20 +952,42 @@ Allocate(): } else if (Equal(nodeType(ns), "constructor") && !GetFlag(n, "usingctor")) { Swig_warning(WARN_PARSE_USING_CONSTRUCTOR, Getfile(n), Getline(n), "Using declaration '%s' for inheriting constructors uses base '%s' which is not an immediate base of '%s'.\n", SwigType_namestr(Getattr(n, "uname")), SwigType_namestr(Getattr(ns, "name")), SwigType_namestr(Getattr(parentNode(n), "name"))); } else { - String *ntype = nodeType(ns); - if (Equal(ntype, "cdecl") || Equal(ntype, "constructor")) { + if (inclass && !GetFlag(n, "feature:ignore") && Getattr(n, "sym:name")) { { - /* A normal C declaration or constructor declaration - * Now add a new class member top the parse tree (copied from the base class member pointed to by the using declaration) */ - if ((inclass) && (!GetFlag(n, "feature:ignore")) && (Getattr(n, "sym:name"))) { + String *ntype = nodeType(ns); + if (Equal(ntype, "cdecl") || Equal(ntype, "constructor") || Equal(ntype, "template")) { + /* Add a new class member to the parse tree (copy it from the base class member pointed to by the using declaration in node n) */ Node *c = ns; Node *unodes = 0, *last_unodes = 0; int ccount = 0; while (c) { - if (Equal(nodeType(c), ntype)) { + String *cnodetype = nodeType(c); + if (Equal(cnodetype, "cdecl")) { add_member_for_using_declaration(c, n, ccount, unodes, last_unodes); - } else if (Equal(nodeType(c), "using")) { + } else if (Equal(cnodetype, "constructor")) { + add_member_for_using_declaration(c, n, ccount, unodes, last_unodes); + } else if (Equal(cnodetype, "template")) { + // A templated member (in a non-template class or in a template class that where the member has a separate template declaration) + // Find the template instantiations in the using declaration (base class) + for (Node *member = ns; member; member = nextSibling(member)) { + /* Constructors have already been handled, only add member functions + * This adds an implicit template instantiation and is a bit unusual as SWIG requires explicit %template for other template instantiations. + * However, of note, is that there is no valid C++ syntax for a template instantiation to introduce a name via a using declaration... + * + * struct Base { template void template_method(T, T) {} }; + * struct Derived : Base { using Base::template_method; }; + * %template() Base::template_method; // SWIG template instantiation + * template void Base::template_method(int, int); // C++ template instantiation + * template void Derived::template_method(int, int); // Not valid C++ + */ + if (Getattr(member, "template") == ns && checkAttribute(ns, "templatetype", "cdecl")) { + if (!GetFlag(member, "feature:ignore") && !Getattr(member, "error")) { + add_member_for_using_declaration(member, n, ccount, unodes, last_unodes); + } + } + } + } else if (Equal(cnodetype, "using")) { for (Node *member = firstChild(c); member; member = nextSibling(member)) { add_member_for_using_declaration(member, n, ccount, unodes, last_unodes); }