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
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) 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 2018-04-18: olly
[Python] Suppress new pycodestyle warning: [Python] Suppress new pycodestyle warning:
E252 missing whitespace around parameter equals 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_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_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_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_extending_proxy_class">Extending proxy classes with additional C# code</a>
<li><a href="#CSharp_enum_underlying_type">Underlying type for enums</a> <li><a href="#CSharp_enum_underlying_type">Underlying type for enums</a>
</ul> </ul>
@ -2515,7 +2516,7 @@ Pay special attention to the memory management issues, using these attributes.
</p> </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> <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. The following example is an alternative approach to adding managed code to the generated proxy class.
</p> </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> <p>
@ -2654,7 +2745,7 @@ public class ExtendMe : global::System.IDisposable {
</pre> </pre>
</div> </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> <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_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_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_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_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> <li><a href="CSharp.html#CSharp_enum_underlying_type">Underlying type for enums</a>
</ul> </ul>

View file

@ -6560,6 +6560,8 @@ used for all proxy classes except those which have a base class
<br> <br>
Note that the <tt>delete()</tt> method name is configurable and is specified by the <tt>methodname</tt> attribute. 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. 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> </div>
<p><tt>%typemap(javadestruct_derived, methodname="delete", methodmodifiers="public synchronized")</tt></p> <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> <br>
Note that the <tt>delete()</tt> method name is configurable and is specified by the <tt>methodname</tt> attribute. 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. 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> </div>
<p><tt>%typemap(javaimports)</tt></p> <p><tt>%typemap(javaimports)</tt></p>

View file

@ -170,6 +170,7 @@ CPP_TEST_CASES += \
defvalue_constructor \ defvalue_constructor \
derived_byvalue \ derived_byvalue \
derived_nested \ derived_nested \
destructor_methodmodifiers \
destructor_reprotected \ destructor_reprotected \
director_abstract \ director_abstract \
director_alternating \ 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); Replaceall(destruct, "$imcall", destructor_call);
else else
Replaceall(destruct, "$imcall", "throw new global::System.MethodAccessException(\"C++ destructor does not have public access\")"); Replaceall(destruct, "$imcall", "throw new global::System.MethodAccessException(\"C++ destructor does not have public access\")");
if (*Char(destruct)) if (*Char(destruct)) {
Printv(proxy_class_def, "\n ", destruct_methodmodifiers, " ", derived ? "override" : "virtual", " void ", destruct_methodname, "() ", destruct, "\n", Printv(proxy_class_def, "\n ", NIL);
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)) if (*Char(interface_upcasts))
Printv(proxy_class_def, interface_upcasts, NIL); Printv(proxy_class_def, interface_upcasts, NIL);
@ -2860,7 +2866,11 @@ public:
if (proxy_flag) { if (proxy_flag) {
Printv(destructor_call, full_imclass_name, ".", Swig_name_destroy(getNSpace(), symname), "(swigCPtr)", NIL); 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; return SWIG_OK;
} }

View file

@ -1298,7 +1298,12 @@ public:
virtual int destructorHandler(Node *n) { virtual int destructorHandler(Node *n) {
Language::destructorHandler(n); Language::destructorHandler(n);
String *symname = Getattr(n, "sym:name"); String *symname = Getattr(n, "sym:name");
Printv(destructor_call, im_dmodule_fq_name, ".", Swig_name_destroy(getNSpace(),symname), "(cast(void*)swigCPtr)", NIL); 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; return SWIG_OK;
} }
@ -3312,9 +3317,13 @@ private:
} }
if (*Char(dispose_code)) { if (*Char(dispose_code)) {
Printv(body, "\n", dispose_methodmodifiers, Printv(body, "\n", NIL);
(derived ? " override" : ""), " void ", dispose_methodname, "() ", const String *methodmods = Getattr(n, "destructmethodmodifiers");
dispose_code, "\n", NIL); 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); Replaceall(destruct, "$jnicall", destructor_call);
else else
Replaceall(destruct, "$jnicall", "throw new UnsupportedOperationException(\"C++ destructor does not have public access\")"); Replaceall(destruct, "$jnicall", "throw new UnsupportedOperationException(\"C++ destructor does not have public access\")");
if (*Char(destruct)) if (*Char(destruct)) {
Printv(proxy_class_def, "\n ", destruct_methodmodifiers, " void ", destruct_methodname, "()", destructor_throws_clause, " ", destruct, "\n", NIL); 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)) if (*Char(interface_upcasts))
Printv(proxy_class_def, interface_upcasts, NIL); Printv(proxy_class_def, interface_upcasts, NIL);
@ -2830,6 +2837,9 @@ public:
if (proxy_flag) { if (proxy_flag) {
Printv(destructor_call, full_imclass_name, ".", Swig_name_destroy(getNSpace(), symname), "(swigCPtr)", NIL); Printv(destructor_call, full_imclass_name, ".", Swig_name_destroy(getNSpace(), symname), "(swigCPtr)", NIL);
generateThrowsClause(n, destructor_throws_clause); generateThrowsClause(n, destructor_throws_clause);
const String *methodmods = Getattr(n, "feature:java:methodmodifiers");
if (methodmods)
Setattr(getCurrentClass(), "destructmethodmodifiers", methodmods);
} }
return SWIG_OK; return SWIG_OK;
} }