Add support for C++11 using declarations for inheriting constructors

Closes #2641
This commit is contained in:
William S Fulton 2023-07-04 12:07:16 +01:00
parent 3ef3f39516
commit 61e60271fe
14 changed files with 171 additions and 21 deletions

View File

@ -7,6 +7,10 @@ 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-07-04: wsfulton
#2641 Add support for C++11 using declarations for inheriting
constructors.
2023-06-30: wsfulton 2023-06-30: wsfulton
#2640 Fix syntax error parsing an expression which calls a function #2640 Fix syntax error parsing an expression which calls a function
with no parameters within additional brackets. with no parameters within additional brackets.

View File

@ -73,8 +73,7 @@
<p>This chapter gives you a brief overview about the SWIG <p>This chapter gives you a brief overview about the SWIG
implementation of the C++11 standard. This part of SWIG is still a work in implementation of the C++11 standard.
progress.
</p> </p>
<p>SWIG supports the new C++ syntax changes with some minor limitations <p>SWIG supports the new C++ syntax changes with some minor limitations
in some areas such as decltype expressions and variadic templates. Wrappers for the in some areas such as decltype expressions and variadic templates. Wrappers for the
@ -863,12 +862,9 @@ where peer constructors can be called. SWIG handles this without any issue.
<p> <p>
The second improvement is constructor inheritance via a <tt>using</tt> declaration. The second improvement is constructor inheritance via a <tt>using</tt> declaration.
This is parsed correctly, but the additional constructors are not currently added to the derived proxy class in the target language. The extra constructors provided by the <tt>using</tt> declaration will add the appropriate constructors into the target language proxy derived classes.
An example is shown below: In the example below a wrapper for the <tt>DerivedClass(int)</tt> constructor is added to <tt>DerivedClass</tt>:
<!--
The extra constructors provided by the <tt>using</tt> syntax will add the appropriate constructors into the target language proxy derived classes.
In the example below a wrapper for the <tt>DerivedClass(int)</tt> is added to <tt>DerivedClass</tt>:
-->
</p> </p>
<div class="code"><pre> <div class="code"><pre>
@ -883,6 +879,10 @@ class DerivedClass: public BaseClass {
}; };
</pre></div> </pre></div>
<p>
<b>Compatibility note:</b> SWIG-4.2.0 was the first version to generate wrappers for constructors inherited via <tt>using</tt> declarations.
</p>
<p> <p>
The final part is member initialization at the site of the declaration. The final part is member initialization at the site of the declaration.
This kind of initialization is handled by SWIG. This kind of initialization is handled by SWIG.

View File

@ -5347,6 +5347,11 @@ you wrap this code in Python, the module works just like you would expect:
</pre> </pre>
</div> </div>
<p>
The C++11 standard supports using declarations for inheriting constructors and this is covered in
<a href="CPlusPlus11_object_construction_improvement">Object construction improvement</a>.
</p>
<p> <p>
C++ <tt>using</tt> declarations can also be used to change access when applicable. C++ <tt>using</tt> declarations can also be used to change access when applicable.
For example, protected methods in a base class can be made public in a derived class: For example, protected methods in a base class can be made public in a derived class:

View File

@ -637,6 +637,7 @@ CPP11_TEST_CASES += \
cpp11_uniform_initialization \ cpp11_uniform_initialization \
cpp11_unrestricted_unions \ cpp11_unrestricted_unions \
cpp11_userdefined_literals \ cpp11_userdefined_literals \
cpp11_using_constructor \
cpp11_using_typedef_struct \ cpp11_using_typedef_struct \
cpp11_variadic_function_templates \ cpp11_variadic_function_templates \
cpp11_variadic_templates \ cpp11_variadic_templates \

View File

@ -10,6 +10,7 @@ private:
int _val; int _val;
public: public:
BaseClass(int iValue) { _val = iValue; } BaseClass(int iValue) { _val = iValue; }
int retrieveValue() { return _val; }
}; };
// Constructor inheritance via using declaration // Constructor inheritance via using declaration

View File

@ -0,0 +1,27 @@
import cpp11_inheriting_constructors.*;
public class cpp11_inheriting_constructors_runme {
static {
try {
System.loadLibrary("cpp11_inheriting_constructors");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e);
System.exit(1);
}
}
public static void main(String argv[]) {
// Constructor inheritance via using declaration
DerivedClass d = new DerivedClass(10);
if (d.retrieveValue() != 10)
throw new RuntimeException("retrieveValue() failed");
// Member initialization at the site of the declaration
SomeClass s = new SomeClass();
if (s.getValue() != 5)
throw new RuntimeException("s.value failed");
}
}

View File

@ -0,0 +1,27 @@
import cpp11_using_constructor.*;
public class cpp11_using_constructor_runme {
static {
try {
System.loadLibrary("cpp11_using_constructor");
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e);
System.exit(1);
}
}
public static void main(String argv[]) {
// Public base constructors
new PublicDerived1(0, "hi").meth();
new PublicDerived2().meth();
new PublicDerived2(0, "hi").meth();
new PublicDerived3().meth();
new PublicDerived3(0, "hi").meth();
new PublicDerived4().meth();
// Protected base constructors
// Cannot test these as the constructors are protected
}
}

View File

@ -0,0 +1,11 @@
from cpp11_inheriting_constructors import *
# Constructor inheritance via using declaration
d = DerivedClass(10)
if d.retrieveValue() != 10:
raise RuntimeError("retrieveValue() failed");
# Member initialization at the site of the declaration
s = SomeClass()
if s.value != 5:
raise RuntimeError("s.value failed");

View File

@ -0,0 +1,13 @@
from cpp11_using_constructor import *
# Public base constructors
a = PublicDerived1(0, "hi").meth()
a = PublicDerived2().meth()
a = PublicDerived2(0, "hi").meth()
a = PublicDerived3().meth()
a = PublicDerived3(0, "hi").meth()
a = PublicDerived4().meth()
# Protected base constructors
# Cannot test these as the constructors are protected

View File

@ -497,9 +497,12 @@ static void add_symbols(Node *n) {
} }
} }
} else { } else {
Setattr(n,"access", "public"); Setattr(n, "access", "public");
} }
} else if (extendmode && !inclass) {
Setattr(n, "access", "public");
} }
if (Getattr(n,"sym:name")) { if (Getattr(n,"sym:name")) {
n = nextSibling(n); n = nextSibling(n);
continue; continue;

View File

@ -755,11 +755,13 @@ Allocate():
Node *c = 0; Node *c = 0;
for (c = firstChild(n); c; c = nextSibling(c)) { for (c = firstChild(n); c; c = nextSibling(c)) {
if (Strcmp(nodeType(c), "cdecl") == 0) { if (Equal(nodeType(c), "cdecl")) {
process_exceptions(c); process_exceptions(c);
if (inclass) if (inclass)
class_member_is_defined_in_bases(c, inclass); class_member_is_defined_in_bases(c, inclass);
} else if (Equal(nodeType(c), "constructor")) {
constructorDeclaration(c);
} }
} }
@ -868,21 +870,22 @@ Allocate():
if (!inclass) if (!inclass)
return SWIG_OK; return SWIG_OK;
Parm *parms = Getattr(n, "parms"); Parm *parms = Getattr(n, "parms");
AccessMode access_mode = accessModeFromString(Getattr(n, "access"));
process_exceptions(n); process_exceptions(n);
if (!extendmode) { if (!extendmode) {
if (!ParmList_numrequired(parms)) { if (!ParmList_numrequired(parms)) {
/* Class does define a default constructor */ /* Class does define a default constructor */
/* However, we had better see where it is defined */ /* However, we had better see where it is defined */
if (cplus_mode == PUBLIC) { if (access_mode == PUBLIC) {
Setattr(inclass, "allocate:default_constructor", "1"); Setattr(inclass, "allocate:default_constructor", "1");
} else if (cplus_mode == PROTECTED) { } else if (access_mode == PROTECTED) {
Setattr(inclass, "allocate:default_base_constructor", "1"); Setattr(inclass, "allocate:default_base_constructor", "1");
} }
} }
/* Class defines some kind of constructor. May or may not be public */ /* Class defines some kind of constructor. May or may not be public */
Setattr(inclass, "allocate:has_constructor", "1"); Setattr(inclass, "allocate:has_constructor", "1");
if (cplus_mode == PUBLIC) { if (access_mode == PUBLIC) {
Setattr(inclass, "allocate:public_constructor", "1"); Setattr(inclass, "allocate:public_constructor", "1");
} }
} else { } else {
@ -931,9 +934,9 @@ Allocate():
if (copy_constructor) { if (copy_constructor) {
Setattr(n, "copy_constructor", "1"); Setattr(n, "copy_constructor", "1");
Setattr(inclass, "allocate:has_copy_constructor", "1"); Setattr(inclass, "allocate:has_copy_constructor", "1");
if (cplus_mode == PUBLIC) { if (access_mode == PUBLIC) {
Setattr(inclass, "allocate:copy_constructor", "1"); Setattr(inclass, "allocate:copy_constructor", "1");
} else if (cplus_mode == PROTECTED) { } else if (access_mode == PROTECTED) {
Setattr(inclass, "allocate:copy_base_constructor", "1"); Setattr(inclass, "allocate:copy_base_constructor", "1");
} }
} }

View File

@ -221,94 +221,138 @@ int Dispatcher::emit_children(Node *n) {
int Dispatcher::defaultHandler(Node *) { int Dispatcher::defaultHandler(Node *) {
return SWIG_OK; return SWIG_OK;
} }
int Dispatcher::extendDirective(Node *n) { int Dispatcher::extendDirective(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::applyDirective(Node *n) { int Dispatcher::applyDirective(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::clearDirective(Node *n) { int Dispatcher::clearDirective(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::constantDirective(Node *n) { int Dispatcher::constantDirective(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::fragmentDirective(Node *n) { int Dispatcher::fragmentDirective(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::importDirective(Node *n) { int Dispatcher::importDirective(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::includeDirective(Node *n) { int Dispatcher::includeDirective(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::insertDirective(Node *n) { int Dispatcher::insertDirective(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::moduleDirective(Node *n) { int Dispatcher::moduleDirective(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::nativeDirective(Node *n) { int Dispatcher::nativeDirective(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::pragmaDirective(Node *n) { int Dispatcher::pragmaDirective(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::typemapDirective(Node *n) { int Dispatcher::typemapDirective(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::typemapitemDirective(Node *n) { int Dispatcher::typemapitemDirective(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::typemapcopyDirective(Node *n) { int Dispatcher::typemapcopyDirective(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::typesDirective(Node *n) { int Dispatcher::typesDirective(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::cDeclaration(Node *n) { int Dispatcher::cDeclaration(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::externDeclaration(Node *n) { int Dispatcher::externDeclaration(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::enumDeclaration(Node *n) { int Dispatcher::enumDeclaration(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::enumvalueDeclaration(Node *n) { int Dispatcher::enumvalueDeclaration(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::enumforwardDeclaration(Node *n) { int Dispatcher::enumforwardDeclaration(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::classDeclaration(Node *n) { int Dispatcher::classDeclaration(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::templateDeclaration(Node *n) { int Dispatcher::templateDeclaration(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::lambdaDeclaration(Node *n) { int Dispatcher::lambdaDeclaration(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::classforwardDeclaration(Node *n) { int Dispatcher::classforwardDeclaration(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::constructorDeclaration(Node *n) { int Dispatcher::constructorDeclaration(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::destructorDeclaration(Node *n) { int Dispatcher::destructorDeclaration(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::accessDeclaration(Node *n) { int Dispatcher::accessDeclaration(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::usingDeclaration(Node *n) { int Dispatcher::usingDeclaration(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
int Dispatcher::namespaceDeclaration(Node *n) { int Dispatcher::namespaceDeclaration(Node *n) {
return defaultHandler(n); return defaultHandler(n);
} }
Dispatcher::AccessMode Dispatcher::accessModeFromString(String *access) {
Dispatcher::AccessMode mode = PUBLIC;
if (Cmp(access, "public") == 0) {
mode = PUBLIC;
} else if (Cmp(access, "private") == 0) {
mode = PRIVATE;
} else if (Cmp(access, "protected") == 0) {
mode = PROTECTED;
} else {
assert(0);
}
return mode;
}
/* Allocators */ /* Allocators */
Language::Language(): Language::Language():
none_comparison(NewString("$arg != 0")), none_comparison(NewString("$arg != 0")),
@ -2675,7 +2719,7 @@ int Language::constructorDeclaration(Node *n) {
Setattr(CurrentClass, "sym:cleanconstructor", "1"); Setattr(CurrentClass, "sym:cleanconstructor", "1");
} }
if ((cplus_mode != PUBLIC)) { if (!is_public(n)) {
/* check only for director classes */ /* check only for director classes */
if (!Swig_directorclass(CurrentClass) || !need_nonpublic_ctor(n)) if (!Swig_directorclass(CurrentClass) || !need_nonpublic_ctor(n))
return SWIG_NOWRAP; return SWIG_NOWRAP;

View File

@ -98,6 +98,7 @@ public:
protected: protected:
AccessMode cplus_mode; AccessMode cplus_mode;
AccessMode accessModeFromString(String *access);
}; };
/* ---------------------------------------------------------------------------- /* ----------------------------------------------------------------------------

View File

@ -1012,7 +1012,7 @@ class TypePass:private Dispatcher {
/* Only a single symbol is being used. There are only a few symbols that /* Only a single symbol is being used. There are only a few symbols that
we actually care about. These are typedef, class declarations, and enum */ we actually care about. These are typedef, class declarations, and enum */
String *ntype = nodeType(ns); String *ntype = nodeType(ns);
if (Strcmp(ntype, "cdecl") == 0) { if (Equal(ntype, "cdecl") || Equal(ntype, "constructor")) {
if (checkAttribute(ns, "storage", "typedef")) { if (checkAttribute(ns, "storage", "typedef")) {
/* A typedef declaration */ /* A typedef declaration */
String *uname = Getattr(n, "uname"); String *uname = Getattr(n, "uname");
@ -1040,7 +1040,7 @@ class TypePass:private Dispatcher {
} }
while (c) { while (c) {
if (Strcmp(nodeType(c), "cdecl") == 0) { if (Strcmp(nodeType(c), ntype) == 0) {
if (!(Swig_storage_isstatic(c) if (!(Swig_storage_isstatic(c)
|| checkAttribute(c, "storage", "typedef") || checkAttribute(c, "storage", "typedef")
|| Strstr(Getattr(c, "storage"), "friend") || Strstr(Getattr(c, "storage"), "friend")
@ -1074,8 +1074,6 @@ class TypePass:private Dispatcher {
Node *nn = copyNode(c); Node *nn = copyNode(c);
Setfile(nn, Getfile(n)); Setfile(nn, Getfile(n));
Setline(nn, Getline(n)); Setline(nn, Getline(n));
Delattr(nn, "access"); // access might be different from the method in the base class
Setattr(nn, "access", Getattr(n, "access"));
if (!Getattr(nn, "sym:name")) if (!Getattr(nn, "sym:name"))
Setattr(nn, "sym:name", symname); Setattr(nn, "sym:name", symname);
Symtab *st = Getattr(n, "sym:symtab"); Symtab *st = Getattr(n, "sym:symtab");
@ -1083,7 +1081,19 @@ class TypePass:private Dispatcher {
Setattr(nn, "sym:symtab", st); Setattr(nn, "sym:symtab", st);
// The real parent is the "using" declaration node, but subsequent code generally handles // The real parent is the "using" declaration node, but subsequent code generally handles
// and expects a class member to point to the parent class node // and expects a class member to point to the parent class node
Setattr(nn, "parentNode", parentNode(n)); Node *parent = parentNode(n);
Setattr(nn, "parentNode", parent);
if (Equal(ntype, "constructor")) {
Setattr(nn, "name", Getattr(parent, "name"));
Setattr(nn, "sym:name", Getattr(parent, "sym:name"));
// Note that the added constructor's access is the same as that of
// the base class' constructor not of the using declaration
} else {
// Access might be different from the method in the base class
Delattr(nn, "access");
Setattr(nn, "access", Getattr(n, "access"));
}
if (!GetFlag(nn, "feature:ignore")) { if (!GetFlag(nn, "feature:ignore")) {
ParmList *parms = CopyParmList(Getattr(c, "parms")); ParmList *parms = CopyParmList(Getattr(c, "parms"));