diff --git a/CHANGES.current b/CHANGES.current index 2f98d6af0..8534b19f5 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -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. diff --git a/Doc/Manual/CPlusPlus11.html b/Doc/Manual/CPlusPlus11.html index 9aceb8dfd..6c160c907 100644 --- a/Doc/Manual/CPlusPlus11.html +++ b/Doc/Manual/CPlusPlus11.html @@ -73,8 +73,7 @@

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.

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.

The second improvement is constructor inheritance via a using 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 using declaration will add the appropriate constructors into the target language proxy derived classes. +In the example below a wrapper for the DerivedClass(int) constructor is added to DerivedClass: +

@@ -883,6 +879,10 @@ class DerivedClass: public BaseClass {
 };
 
+

+Compatibility note: SWIG-4.2.0 was the first version to generate wrappers for constructors inherited via using declarations. +

+

The final part is member initialization at the site of the declaration. This kind of initialization is handled by SWIG. diff --git a/Doc/Manual/SWIGPlus.html b/Doc/Manual/SWIGPlus.html index 8de6e19e1..956ea7713 100644 --- a/Doc/Manual/SWIGPlus.html +++ b/Doc/Manual/SWIGPlus.html @@ -5347,6 +5347,11 @@ you wrap this code in Python, the module works just like you would expect: +

+The C++11 standard supports using declarations for inheriting constructors and this is covered in +Object construction improvement. +

+

C++ using 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: diff --git a/Examples/test-suite/common.mk b/Examples/test-suite/common.mk index c1976adf4..a883ec90c 100644 --- a/Examples/test-suite/common.mk +++ b/Examples/test-suite/common.mk @@ -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 \ diff --git a/Examples/test-suite/cpp11_inheriting_constructors.i b/Examples/test-suite/cpp11_inheriting_constructors.i index ccdf050c3..4c2ed3feb 100644 --- a/Examples/test-suite/cpp11_inheriting_constructors.i +++ b/Examples/test-suite/cpp11_inheriting_constructors.i @@ -10,6 +10,7 @@ private: int _val; public: BaseClass(int iValue) { _val = iValue; } + int retrieveValue() { return _val; } }; // Constructor inheritance via using declaration diff --git a/Examples/test-suite/java/cpp11_inheriting_constructors_runme.java b/Examples/test-suite/java/cpp11_inheriting_constructors_runme.java new file mode 100644 index 000000000..5f00c64a4 --- /dev/null +++ b/Examples/test-suite/java/cpp11_inheriting_constructors_runme.java @@ -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"); + } +} + + diff --git a/Examples/test-suite/java/cpp11_using_constructor_runme.java b/Examples/test-suite/java/cpp11_using_constructor_runme.java new file mode 100644 index 000000000..1e8a36881 --- /dev/null +++ b/Examples/test-suite/java/cpp11_using_constructor_runme.java @@ -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 + } +} diff --git a/Examples/test-suite/python/cpp11_inheriting_constructors_runme.py b/Examples/test-suite/python/cpp11_inheriting_constructors_runme.py new file mode 100644 index 000000000..28fb52dbb --- /dev/null +++ b/Examples/test-suite/python/cpp11_inheriting_constructors_runme.py @@ -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"); diff --git a/Examples/test-suite/python/cpp11_using_constructor_runme.py b/Examples/test-suite/python/cpp11_using_constructor_runme.py new file mode 100644 index 000000000..0b511ef4b --- /dev/null +++ b/Examples/test-suite/python/cpp11_using_constructor_runme.py @@ -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 diff --git a/Source/CParse/parser.y b/Source/CParse/parser.y index f215c7e7f..5a750263a 100644 --- a/Source/CParse/parser.y +++ b/Source/CParse/parser.y @@ -497,9 +497,12 @@ static void add_symbols(Node *n) { } } } else { - Setattr(n,"access", "public"); + Setattr(n, "access", "public"); } + } else if (extendmode && !inclass) { + Setattr(n, "access", "public"); } + if (Getattr(n,"sym:name")) { n = nextSibling(n); continue; diff --git a/Source/Modules/allocate.cxx b/Source/Modules/allocate.cxx index 233830791..b1cfaf826 100644 --- a/Source/Modules/allocate.cxx +++ b/Source/Modules/allocate.cxx @@ -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"); } } diff --git a/Source/Modules/lang.cxx b/Source/Modules/lang.cxx index e796adf15..f2be8d42d 100644 --- a/Source/Modules/lang.cxx +++ b/Source/Modules/lang.cxx @@ -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; diff --git a/Source/Modules/swigmod.h b/Source/Modules/swigmod.h index 849cc5f41..0b739c50a 100644 --- a/Source/Modules/swigmod.h +++ b/Source/Modules/swigmod.h @@ -98,6 +98,7 @@ public: protected: AccessMode cplus_mode; + AccessMode accessModeFromString(String *access); }; /* ---------------------------------------------------------------------------- diff --git a/Source/Modules/typepass.cxx b/Source/Modules/typepass.cxx index ea7378e43..23b943cbf 100644 --- a/Source/Modules/typepass.cxx +++ b/Source/Modules/typepass.cxx @@ -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"));