C#, D, Java methodmodifiers on destructors

Add support so that the %csmethodmodifiers, %dmethodmodifiers,
%javamethodmodifiers can modify the method modifiers for the destructor wrappers
in the proxy class: dispose, Dispose, delete. With this feature, it is now possible
to make a C# proxy class sealed, eg when wrapping a class X, the virtual method modifiers
can be removed using:

  %typemap(csclassmodifiers) X "public sealed class"
  %csmethodmodifiers X::~X "public /*virtual*/";
This commit is contained in:
William S Fulton 2018-05-11 18:09:51 +01:00
parent 1f7689fa8f
commit ee17f8d04f
9 changed files with 210 additions and 13 deletions

View File

@ -7,6 +7,16 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/
Version 4.0.0 (in progress)
===========================
2018-05-11: wsfulton
[C#, D, Java] Add support so that the %csmethodmodifiers, %dmethodmodifiers,
%javamethodmodifiers can modify the method modifiers for the destructor wrappers
in the proxy class: dispose, Dispose, delete. With this feature, it is now possible
to make a C# proxy class sealed, eg when wrapping a class X, the virtual method modifiers
can be removed using:
%typemap(csclassmodifiers) X "public sealed class"
%csmethodmodifiers X::~X "public /*virtual*/";
2018-04-18: olly
[Python] Suppress new pycodestyle warning:
E252 missing whitespace around parameter equals

View File

@ -44,7 +44,8 @@
<li><a href="#CSharp_date_marshalling">Date marshalling using the csin typemap and associated attributes</a>
<li><a href="#CSharp_date_properties">A date example demonstrating marshalling of C# properties</a>
<li><a href="#CSharp_date_pre_post_directors">Date example demonstrating the 'pre' and 'post' typemap attributes for directors</a>
<li><a href="#CSharp_partial_classes">Turning wrapped classes into partial classes</a>
<li><a href="#CSharp_partial_classes">Turning proxy classes into partial classes</a>
<li><a href="#CSharp_sealed_proxy_class">Turning proxy classes into sealed classes</a>
<li><a href="#CSharp_extending_proxy_class">Extending proxy classes with additional C# code</a>
<li><a href="#CSharp_enum_underlying_type">Underlying type for enums</a>
</ul>
@ -2515,7 +2516,7 @@ Pay special attention to the memory management issues, using these attributes.
</p>
<H3><a name="CSharp_partial_classes">20.8.6 Turning wrapped classes into partial classes</a></H3>
<H3><a name="CSharp_partial_classes">20.8.6 Turning proxy classes into partial classes</a></H3>
<p>
@ -2615,7 +2616,97 @@ demonstrating that the class contains methods calling both unmanaged code - <tt>
The following example is an alternative approach to adding managed code to the generated proxy class.
</p>
<H3><a name="CSharp_extending_proxy_class">20.8.7 Extending proxy classes with additional C# code</a></H3>
<H3><a name="CSharp_sealed_proxy_class">20.8.7 Turning proxy classes into sealed classes</a></H3>
<p>
The technique in the previous section can be used to make the proxy class a sealed class.
Consider a C++ class <tt>NotABaseClass</tt> that you don't want to be derived from in C#:
</p>
<div class="code">
<pre>
struct NotABaseClass {
NotABaseClass();
~NotABaseClass();
};
</pre>
</div>
<p>
The default C# proxy class method generated with Dispose method is:
</p>
<div class="code">
<pre>
public class NotABaseClass : global::System.IDisposable {
...
public virtual void Dispose() {
...
}
}
</pre>
</div>
<p>
The <tt>csclassmodifiers</tt> typemap can be used to modify the class modifiers and
the <tt>csmethodmodifiers</tt> feature can be used on the destructor to modify the proxy's <tt>Dispose</tt> method:
</p>
<div class="code">
<pre>
%typemap(csclassmodifiers) NotABaseClass "public sealed class"
%csmethodmodifiers NotABaseClass::~NotABaseClass "public /*virtual*/";
</pre>
</div>
<p>
The relevant generated code is thus:
</p>
<div class="code">
<pre>
public sealed class NotABaseClass : global::System.IDisposable {
...
public /*virtual*/ void Dispose() {
...
}
}
</pre>
</div>
<p>
Any attempt to derive from the <tt>NotABaseClass</tt> in C# will result in a C# compiler error, for example:
</p>
<div class="code">
<pre>
public class Derived : NotABaseClass {
};
</pre>
</div>
<div class="shell">
<pre>
runme.cs(6,14): error CS0509: `Derived': cannot derive from sealed type `NotABaseClass'
</pre>
</div>
<p>
Finally, if you get a warning about use of 'protected' in the generated base class:
</p>
<div class="shell">
<pre>
NotABaseClass.cs(14,18): warning CS0628: `NotABaseClass.swigCMemOwn': new protected member declared in sealed class
</pre>
</div>
<p>
Either suppress the warning or modify the generated code by copying and tweaking the default
'csbody' typemap code in csharp.swg by modifying swigCMemOwn to not be protected.
</p>
<H3><a name="CSharp_extending_proxy_class">20.8.8 Extending proxy classes with additional C# code</a></H3>
<p>
@ -2654,7 +2745,7 @@ public class ExtendMe : global::System.IDisposable {
</pre>
</div>
<H3><a name="CSharp_enum_underlying_type">20.8.8 Underlying type for enums</a></H3>
<H3><a name="CSharp_enum_underlying_type">20.8.9 Underlying type for enums</a></H3>
<P>

View File

@ -787,7 +787,8 @@
<li><a href="CSharp.html#CSharp_date_marshalling">Date marshalling using the csin typemap and associated attributes</a>
<li><a href="CSharp.html#CSharp_date_properties">A date example demonstrating marshalling of C# properties</a>
<li><a href="CSharp.html#CSharp_date_pre_post_directors">Date example demonstrating the 'pre' and 'post' typemap attributes for directors</a>
<li><a href="CSharp.html#CSharp_partial_classes">Turning wrapped classes into partial classes</a>
<li><a href="CSharp.html#CSharp_partial_classes">Turning proxy classes into partial classes</a>
<li><a href="CSharp.html#CSharp_sealed_proxy_class">Turning proxy classes into sealed classes</a>
<li><a href="CSharp.html#CSharp_extending_proxy_class">Extending proxy classes with additional C# code</a>
<li><a href="CSharp.html#CSharp_enum_underlying_type">Underlying type for enums</a>
</ul>

View File

@ -6560,6 +6560,8 @@ used for all proxy classes except those which have a base class
<br>
Note that the <tt>delete()</tt> method name is configurable and is specified by the <tt>methodname</tt> attribute.
The method modifiers are also configurable via the <tt>methodmodifiers</tt> attribute.
If a <tt>%javamethodmodifiers</tt> is attached to the class' destructor,
it will be used in preference to the <tt>methodmodifiers</tt> typemap attribute for the class.
</div>
<p><tt>%typemap(javadestruct_derived, methodname="delete", methodmodifiers="public synchronized")</tt></p>
@ -6571,6 +6573,8 @@ same as "javadestruct" but only used for derived proxy classes
<br>
Note that the <tt>delete()</tt> method name is configurable and is specified by the <tt>methodname</tt> attribute.
The method modifiers are also configurable via the <tt>methodmodifiers</tt> attribute.
If a <tt>%javamethodmodifiers</tt> is attached to the class' destructor,
it will be used in preference to the <tt>methodmodifiers</tt> typemap attribute for the class.
</div>
<p><tt>%typemap(javaimports)</tt></p>

View File

@ -170,6 +170,7 @@ CPP_TEST_CASES += \
defvalue_constructor \
derived_byvalue \
derived_nested \
destructor_methodmodifiers \
destructor_reprotected \
director_abstract \
director_alternating \

View File

@ -0,0 +1,61 @@
%module destructor_methodmodifiers
// This test changes the proxy classes so that they cannot be inherited from in the target language
// Previously the %csmethodmodifiers, %dmethodmodifiers, %javamethodmodifiers on destructors were ignored
// Now they can control the dispose/Dispose/delete method modifiers
#if defined(SWIGCSHARP)
// remove all use of protected and virtual keywords
%typemap(csclassmodifiers) NotForDeriving1, NotForDeriving2 "public sealed class"
%csmethodmodifiers NotForDeriving1::~NotForDeriving1 "public /*not virtual nor override*/";
%csmethodmodifiers NotForDeriving2::~NotForDeriving2 "public /*not virtual nor override*/";
// remove protected keyword to remove compiler warning
%typemap(csbody) NotForDeriving1, NotForDeriving2 %{
private global::System.Runtime.InteropServices.HandleRef swigCPtr;
private /*protected*/ bool swigCMemOwn;
internal $csclassname(global::System.IntPtr cPtr, bool cMemoryOwn) {
swigCMemOwn = cMemoryOwn;
swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
}
internal static global::System.Runtime.InteropServices.HandleRef getCPtr($csclassname obj) {
return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr;
}
%}
#elif defined(SWIGD)
%typemap(dclassmodifiers) NotForDeriving1, NotForDeriving2 "final class"
%dmethodmodifiers NotForDeriving1::~NotForDeriving1 "public final";
%dmethodmodifiers NotForDeriving2::~NotForDeriving2 "public final";
#elif defined(SWIGJAVA)
%typemap(javaclassmodifiers) NotForDeriving1, NotForDeriving2 "public final class"
%javamethodmodifiers NotForDeriving1::~NotForDeriving1 "public synchronized final";
%javamethodmodifiers NotForDeriving2::~NotForDeriving2 "public synchronized final";
#endif
%inline %{
//#include <iostream>
struct NotForDeriving1 {
void notvirtual() {}
~NotForDeriving1() {
// std::cout << "~NotForDeriving1 called" << std::endl;
}
};
struct NotForDeriving2 {
void notvirtual() {}
#if defined(SWIG)
%extend {
~NotForDeriving2() {
// std::cout << "~NotForDeriving2 called" << std::endl;
}
}
#endif
};
%}

View File

@ -1910,9 +1910,15 @@ public:
Replaceall(destruct, "$imcall", destructor_call);
else
Replaceall(destruct, "$imcall", "throw new global::System.MethodAccessException(\"C++ destructor does not have public access\")");
if (*Char(destruct))
Printv(proxy_class_def, "\n ", destruct_methodmodifiers, " ", derived ? "override" : "virtual", " void ", destruct_methodname, "() ", destruct, "\n",
NIL);
if (*Char(destruct)) {
Printv(proxy_class_def, "\n ", NIL);
const String *methodmods = Getattr(n, "destructmethodmodifiers");
if (methodmods)
Printv(proxy_class_def, methodmods, NIL);
else
Printv(proxy_class_def, destruct_methodmodifiers, " ", derived ? "override" : "virtual", NIL);
Printv(proxy_class_def, " void ", destruct_methodname, "() ", destruct, "\n", NIL);
}
}
if (*Char(interface_upcasts))
Printv(proxy_class_def, interface_upcasts, NIL);
@ -2860,7 +2866,11 @@ public:
if (proxy_flag) {
Printv(destructor_call, full_imclass_name, ".", Swig_name_destroy(getNSpace(), symname), "(swigCPtr)", NIL);
const String *methodmods = Getattr(n, "feature:cs:methodmodifiers");
if (methodmods)
Setattr(getCurrentClass(), "destructmethodmodifiers", methodmods);
}
return SWIG_OK;
}

View File

@ -1298,7 +1298,12 @@ public:
virtual int destructorHandler(Node *n) {
Language::destructorHandler(n);
String *symname = Getattr(n, "sym:name");
Printv(destructor_call, im_dmodule_fq_name, ".", Swig_name_destroy(getNSpace(),symname), "(cast(void*)swigCPtr)", NIL);
const String *methodmods = Getattr(n, "feature:d:methodmodifiers");
if (methodmods)
Setattr(getCurrentClass(), "destructmethodmodifiers", methodmods);
return SWIG_OK;
}
@ -3312,9 +3317,13 @@ private:
}
if (*Char(dispose_code)) {
Printv(body, "\n", dispose_methodmodifiers,
(derived ? " override" : ""), " void ", dispose_methodname, "() ",
dispose_code, "\n", NIL);
Printv(body, "\n", NIL);
const String *methodmods = Getattr(n, "destructmethodmodifiers");
if (methodmods)
Printv(body, methodmods, NIL);
else
Printv(body, dispose_methodmodifiers, (derived ? " override" : ""), NIL);
Printv(body, " void ", dispose_methodname, "() ", dispose_code, "\n", NIL);
}
}

View File

@ -1987,8 +1987,15 @@ public:
Replaceall(destruct, "$jnicall", destructor_call);
else
Replaceall(destruct, "$jnicall", "throw new UnsupportedOperationException(\"C++ destructor does not have public access\")");
if (*Char(destruct))
Printv(proxy_class_def, "\n ", destruct_methodmodifiers, " void ", destruct_methodname, "()", destructor_throws_clause, " ", destruct, "\n", NIL);
if (*Char(destruct)) {
Printv(proxy_class_def, "\n ", NIL);
const String *methodmods = Getattr(n, "destructmethodmodifiers");
if (methodmods)
Printv(proxy_class_def, methodmods, NIL);
else
Printv(proxy_class_def, destruct_methodmodifiers, NIL);
Printv(proxy_class_def, " void ", destruct_methodname, "()", destructor_throws_clause, " ", destruct, "\n", NIL);
}
}
if (*Char(interface_upcasts))
Printv(proxy_class_def, interface_upcasts, NIL);
@ -2830,6 +2837,9 @@ public:
if (proxy_flag) {
Printv(destructor_call, full_imclass_name, ".", Swig_name_destroy(getNSpace(), symname), "(swigCPtr)", NIL);
generateThrowsClause(n, destructor_throws_clause);
const String *methodmods = Getattr(n, "feature:java:methodmodifiers");
if (methodmods)
Setattr(getCurrentClass(), "destructmethodmodifiers", methodmods);
}
return SWIG_OK;
}