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 <typename T> Base(const T &t, const char *s) {}
    // templated member method
    template <typename T> void template_method(const T &t, const char *s) {}
  };

  %template(Base) Base::Base<int>;
  %template(template_method) Base::template_method<double>;

  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.
This commit is contained in:
William S Fulton 2023-08-06 13:43:52 +01:00
parent 93732bb195
commit 2e1506c189
7 changed files with 199 additions and 30 deletions

View File

@ -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) 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 <typename T> Base(const T &t, const char *s) {}
// templated member method
template <typename T> void template_method(const T &t, const char *s) {}
};
%template(Base) Base::Base<int>;
%template(template_method) Base::template_method<double>;
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 2023-08-04: wsfulton
Fix using declarations for inheritance hierarchies more than Fix using declarations for inheritance hierarchies more than
two deep and the using declarations are overloaded. Using declarations two deep and the using declarations are overloaded. Using declarations

View File

@ -510,35 +510,56 @@ struct TemplPublicDerived6 : TemplPublicBase6<T> {
%template(TemplPublicDerived6Int) TemplPublicDerived6<int>; %template(TemplPublicDerived6Int) TemplPublicDerived6<int>;
// Templated constructors // Templated constructors (public)
%inline %{ %inline %{
struct TemplateConstructor1Base { struct TemplateConstructor1Base {
virtual ~TemplateConstructor1Base() {} virtual ~TemplateConstructor1Base() {}
public:
// No implicit constructor // No implicit constructor
template <typename T> TemplateConstructor1Base(const T &t, const char *s) {} template <typename T> TemplateConstructor1Base(T t, const char *s) {}
virtual void meth() {} template <typename T> void template_method(T t, const char *s) {}
virtual void normal_method() {}
}; };
%} %}
%template(TemplateConstructor1Base) TemplateConstructor1Base::TemplateConstructor1Base<int>; %template(TemplateConstructor1Base) TemplateConstructor1Base::TemplateConstructor1Base<int>;
%template(TemplateConstructor1Base) TemplateConstructor1Base::TemplateConstructor1Base<const char *>;
%template(TemplateConstructor1Base) TemplateConstructor1Base::TemplateConstructor1Base<double>;
%template(template_method) TemplateConstructor1Base::template_method<int>;
%template(template_method) TemplateConstructor1Base::template_method<const char *>;
%inline %{ %inline %{
struct TemplateConstructor1Derived : TemplateConstructor1Base { 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::TemplateConstructor1Base;
using TemplateConstructor1Base::meth; using TemplateConstructor1Base::template_method;
}; };
%} %}
// TODO: Missing constructors, below probably ought to work using instantiation as follows: // Templated constructors (protected)
//%template(TemplateConstructor1Derived) TemplateConstructor1Derived::TemplateConstructor1Derived<int>; %inline %{
struct TemplateConstructor2Base {
%{ virtual ~TemplateConstructor2Base() {}
void tester() { protected:
TemplateConstructor1Derived tc = TemplateConstructor1Derived(123, "hi"); // No implicit constructor
tc.meth(); template <typename T> TemplateConstructor2Base(T t, const char *s) {}
// Note not valid c++: template <typename T> void template_method(T t, const char *s) {}
// TemplateConstructor1Derived tc2 = TemplateConstructor1Derived::TemplateConstructor1Derived<int>(123, "hi"); virtual void normal_method() {}
} };
%} %}
// Note that templated methods also not working with using declarations for inheriting from base templated methods %template(TemplateConstructor2Base) TemplateConstructor2Base::TemplateConstructor2Base<int>;
%template(TemplateConstructor2Base) TemplateConstructor2Base::TemplateConstructor2Base<const char *>;
%template(TemplateConstructor2Base) TemplateConstructor2Base::TemplateConstructor2Base<double>;
%template(template_method) TemplateConstructor2Base::template_method<int>;
%template(template_method) TemplateConstructor2Base::template_method<const char *>;
%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
};
%}

View File

@ -114,7 +114,7 @@ public class cpp11_director_using_constructor_runme {
DeepProtectedBase3 dbp3 = new DeepProtectedBase3(11, 22, 33); DeepProtectedBase3 dbp3 = new DeepProtectedBase3(11, 22, 33);
// Missing base // Missing base
// new HiddenDerived1(); new HiddenDerived1();
// Templates and public base constructors (derive from non-template) // Templates and public base constructors (derive from non-template)
new TemplatePublicDerived1Int(0, "hi").meth(); new TemplatePublicDerived1Int(0, "hi").meth();
@ -135,6 +135,38 @@ public class cpp11_director_using_constructor_runme {
new TemplPublicDerived5Int().meth(); new TemplPublicDerived5Int().meth();
new TemplPublicDerived6Int(0, "hi").meth(); new TemplPublicDerived6Int(0, "hi").meth();
new TemplPublicDerived6Int().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");
} }
// //

View File

@ -1,5 +1,6 @@
import cpp11_using_constructor.*; import cpp11_using_constructor.*;
import java.lang.reflect.*;
public class cpp11_using_constructor_runme { public class cpp11_using_constructor_runme {
@ -112,7 +113,7 @@ public class cpp11_using_constructor_runme {
DeepProtectedBase3 dbp3 = new DeepProtectedBase3(11, 22, 33); DeepProtectedBase3 dbp3 = new DeepProtectedBase3(11, 22, 33);
// Missing base // Missing base
// new HiddenDerived1(); new HiddenDerived1();
// Templates and public base constructors (derive from non-template) // Templates and public base constructors (derive from non-template)
new TemplatePublicDerived1Int(0, "hi").meth(); new TemplatePublicDerived1Int(0, "hi").meth();
@ -134,9 +135,36 @@ public class cpp11_using_constructor_runme {
new TemplPublicDerived6Int(0, "hi").meth(); new TemplPublicDerived6Int(0, "hi").meth();
new TemplPublicDerived6Int().meth(); new TemplPublicDerived6Int().meth();
// Templated constructors // Templated constructors (public)
new TemplateConstructor1Base(0, "hi").meth(); TemplateConstructor1Base tcb = new TemplateConstructor1Base(0, "hi");
// TODO: missing constructor... tcb = new TemplateConstructor1Base("hi", "hi");
// new TemplateConstructor1Derived(0, "hi").meth(); 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");
} }
} }

View File

@ -100,7 +100,7 @@ db3 = DeepBase3(11, 22, 33)
dbp3 = DeepProtectedBase3(11, 22, 33) dbp3 = DeepProtectedBase3(11, 22, 33)
# Missing base # Missing base
# HiddenDerived1() HiddenDerived1()
# Templates and public base constructors (derive from non-template) # Templates and public base constructors (derive from non-template)
TemplatePublicDerived1Int(0, "hi").meth() TemplatePublicDerived1Int(0, "hi").meth()
@ -121,3 +121,25 @@ TemplPublicDerived4Int().meth()
TemplPublicDerived5Int().meth() TemplPublicDerived5Int().meth()
TemplPublicDerived6Int(0, "hi").meth() TemplPublicDerived6Int(0, "hi").meth()
TemplPublicDerived6Int().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")

View File

@ -100,7 +100,7 @@ db3 = DeepBase3(11, 22, 33)
dbp3 = DeepProtectedBase3(11, 22, 33) dbp3 = DeepProtectedBase3(11, 22, 33)
# Missing base # Missing base
# HiddenDerived1() HiddenDerived1()
# Templates and public base constructors (derive from non-template) # Templates and public base constructors (derive from non-template)
TemplatePublicDerived1Int(0, "hi").meth() TemplatePublicDerived1Int(0, "hi").meth()
@ -121,3 +121,25 @@ TemplPublicDerived4Int().meth()
TemplPublicDerived5Int().meth() TemplPublicDerived5Int().meth()
TemplPublicDerived6Int(0, "hi").meth() TemplPublicDerived6Int(0, "hi").meth()
TemplPublicDerived6Int().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")

View File

@ -952,20 +952,42 @@ Allocate():
} else if (Equal(nodeType(ns), "constructor") && !GetFlag(n, "usingctor")) { } 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"))); 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 { } else {
String *ntype = nodeType(ns); if (inclass && !GetFlag(n, "feature:ignore") && Getattr(n, "sym:name")) {
if (Equal(ntype, "cdecl") || Equal(ntype, "constructor")) {
{ {
/* A normal C declaration or constructor declaration String *ntype = nodeType(ns);
* Now add a new class member top the parse tree (copied from the base class member pointed to by the using declaration) */ if (Equal(ntype, "cdecl") || Equal(ntype, "constructor") || Equal(ntype, "template")) {
if ((inclass) && (!GetFlag(n, "feature:ignore")) && (Getattr(n, "sym:name"))) { /* 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 *c = ns;
Node *unodes = 0, *last_unodes = 0; Node *unodes = 0, *last_unodes = 0;
int ccount = 0; int ccount = 0;
while (c) { 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); 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 <typename T> void template_method(T, T) {} };
* struct Derived : Base { using Base::template_method; };
* %template() Base::template_method<int>; // SWIG template instantiation
* template void Base::template_method<int>(int, int); // C++ template instantiation
* template void Derived::template_method<int>(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)) { for (Node *member = firstChild(c); member; member = nextSibling(member)) {
add_member_for_using_declaration(member, n, ccount, unodes, last_unodes); add_member_for_using_declaration(member, n, ccount, unodes, last_unodes);
} }