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

View File

@ -73,8 +73,7 @@
<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
progress.
implementation of the C++11 standard.
</p>
<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
@ -863,12 +862,9 @@ where peer constructors can be called. SWIG handles this without any issue.
<p>
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.
An example is shown below:
<!--
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>:
-->
The extra constructors provided by the <tt>using</tt> declaration will add the appropriate constructors into the target language proxy derived classes.
In the example below a wrapper for the <tt>DerivedClass(int)</tt> constructor is added to <tt>DerivedClass</tt>:
</p>
<div class="code"><pre>
@ -883,6 +879,10 @@ class DerivedClass: public BaseClass {
};
</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>
The final part is member initialization at the site of the declaration.
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>
</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>
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:

View File

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

View File

@ -10,6 +10,7 @@ private:
int _val;
public:
BaseClass(int iValue) { _val = iValue; }
int retrieveValue() { return _val; }
};
// 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

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

View File

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

View File

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

View File

@ -98,6 +98,7 @@ public:
protected:
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
we actually care about. These are typedef, class declarations, and enum */
String *ntype = nodeType(ns);
if (Strcmp(ntype, "cdecl") == 0) {
if (Equal(ntype, "cdecl") || Equal(ntype, "constructor")) {
if (checkAttribute(ns, "storage", "typedef")) {
/* A typedef declaration */
String *uname = Getattr(n, "uname");
@ -1040,7 +1040,7 @@ class TypePass:private Dispatcher {
}
while (c) {
if (Strcmp(nodeType(c), "cdecl") == 0) {
if (Strcmp(nodeType(c), ntype) == 0) {
if (!(Swig_storage_isstatic(c)
|| checkAttribute(c, "storage", "typedef")
|| Strstr(Getattr(c, "storage"), "friend")
@ -1074,8 +1074,6 @@ class TypePass:private Dispatcher {
Node *nn = copyNode(c);
Setfile(nn, Getfile(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"))
Setattr(nn, "sym:name", symname);
Symtab *st = Getattr(n, "sym:symtab");
@ -1083,7 +1081,19 @@ class TypePass:private Dispatcher {
Setattr(nn, "sym:symtab", st);
// 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
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")) {
ParmList *parms = CopyParmList(Getattr(c, "parms"));