Add the %interface_additional macro

New macro added to the family of %interface macros. This new macro
is for adding additional interfaces for the generated interface to
extend/derive from.

Closes #1188
This commit is contained in:
William S Fulton 2024-09-11 20:05:30 +01:00
parent f93cf07015
commit cbff768feb
7 changed files with 171 additions and 15 deletions

View File

@ -7,6 +7,11 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/
Version 4.3.0 (in progress)
===========================
2024-09-11: wsfulton
[C# Java] #1188 Add the %interface_additional macro to the family of
%interface macros for adding additional interfaces for the generated
interface to extend/derive from.
2024-09-07: wsfulton
#2875 Fix swig-4.1.0 regression using the %interface family of macros
for multiple inheritance and common bases.

View File

@ -3445,6 +3445,10 @@ There is more than one macro in order to provide a choice for choosing the Java
<td><tt>%interface_custom("PROXY", "INTERFACE", CTYPE)</tt></td>
<td>For C++ class <tt>CTYPE</tt>, proxy class name is given by the string <tt>PROXY</tt>, interface name is given by the string <tt>INTERFACE</tt>. The <tt>PROXY</tt> and <tt>INTERFACE</tt> names can use the <a href="SWIG.html#SWIG_advanced_renaming">string formatting functions</a> used in <tt>%rename</tt>.</td>
</tr>
<tr>
<td><tt>%interface_additional("PROXY", "INTERFACE", "ADDITIONAL", CTYPE)</tt></td>
<td>For C++ class <tt>CTYPE</tt>, proxy class name is given by the string <tt>PROXY</tt>, interface name is given by the string <tt>INTERFACE</tt>. The <tt>PROXY</tt> and <tt>INTERFACE</tt> names can use the <a href="SWIG.html#SWIG_advanced_renaming">string formatting functions</a> used in <tt>%rename</tt>. <tt>ADDITIONAL</tt> should contain a comma separated list of interfaces for the interface class to additionally extend. This is for adding interfaces not parsed by SWIG and is useful for adding in pure Java interfaces</td>
</tr>
</table>
<p>
@ -3648,8 +3652,82 @@ which calls an appropriate C++ cast of the pointer up the inheritance chain.
</p>
<p>
The <tt>%interface_additional</tt> macro is useful for adding additional interfaces to the Java interface class.
Consider a simple class hierarchy, where <tt>Whizz</tt> inherits from <tt>Bang</tt> and we want
these two classes to be wrapped as Java interfaces and the <tt>Whizz</tt> interface to additionally extend from the pure Java interface <tt>java.util.EventListener</tt>.
</p>
<div class="code">
<pre>
%interface_custom("Bang", "IBang", Space::Bang);
%interface_additional("Whizz", "IWhizz", "java.util.EventListener", Space::Whizz)
namespace Space {
struct Bang {
virtual void bang() = 0;
};
struct Whizz : Bang {
virtual void whizz() = 0;
};
}
</pre>
</div>
<p>
The classes and interfaces generated are shown below, noting that <tt>IWhizz</tt> extends the additional interface <tt>java.util.EventListener</tt>:
</p>
<div class="code">
<pre>
public interface IBang { ... }
public interface IWhizz extends java.util.EventListener, IBang { ... }
public class Bang implements IBang { ... }
public class Whizz implements IWhizz, IBang { ... }
</pre>
</div>
<p>
Note the subtle difference to the <tt>javainterfaces</tt> typemap discussed elsewhere.
The typemap applies to the generated Java proxy class only, not the Java interface.
A slight change to our example above as follows:
</p>
<div class="code">
<pre>
%interface_custom("Bang", "IBang", Space::Bang);
%interface_custom("Whizz", "IWhizz", Space::Whizz)
%typemap(javainterfaces) Space::Whizz "java.util.EventListener"
namespace Space {
... as shown above ...
}
</pre>
</div>
<p>
will then the generate different code for the proxy class <tt>Whizz</tt> and the interface <tt>IWhizz</tt> as follows:
</p>
<div class="code">
<pre>
public interface IBang { ... }
public interface IWhizz extends IBang { ... }
public class Bang implements IBang { ... }
public class Whizz implements IWhizz, IBang, java.util.EventListener { ... }
</pre>
</div>
<p>
Finally, a note on the implementation of the interface macros.
The interface macros are implemented using the <tt>interface</tt> feature and typemaps.
For example:
A couple of examples:
</p>
<div class="code">
@ -3658,18 +3736,30 @@ For example:
%feature("interface", name="%sSwigInterface") CTYPE;
INTERFACE_TYPEMAPS(CTYPE)
%enddef
%define %interface_additional(PROXY, INTERFACE, ADDITIONAL, CTYPE...)
%rename(PROXY) CTYPE;
%feature("interface", name=INTERFACE, additional=ADDITIONAL) CTYPE;
INTERFACE_TYPEMAPS(CTYPE)
%enddef
</pre>
</div>
<p>
The feature accepts one attribute called <tt>name</tt>, which is the name of the Java interface mentioned earlier.
The feature accepts two attributes named <tt>name</tt> and <tt>additional</tt>.
The <tt>name</tt> attribute should contain the name of the Java interface.
The <tt>additional</tt> attribute should contain additional interfaces for the interface class, not parsed by SWIG.
The <tt>INTERFACE_TYPEMAPS</tt> macro implements the typemaps and can be viewed in the
<tt>swiginterface.i</tt> file and contain
<tt>swiginterface.i</tt> file and contains
the usual Java typemaps for generating code plus the <tt>javainterfacecode</tt>
typemap which is only used when a class is marked with the <tt>interface</tt> feature.
See <a href="Java.html#Java_code_typemaps">Java code typemaps</a> for details.
</p>
<p>
<b>Compatibility note:</b> The <tt>additional</tt> attribute and <tt>%interface_additional</tt> macro was added in SWIG-4.3.0.
</p>
<H2><a name="Java_directors">27.5 Cross language polymorphism using directors</a></H2>
@ -6987,7 +7077,7 @@ The "javaimports" typemap is ignored if the enum class is wrapped by an inner Ja
<div class="code">
<pre>
[ javaimports typemap ]
[ javainterfacemodifiers typemap ] [ javainterfacename ] {
[ javainterfacemodifiers typemap ] [ name ] [extends additional [, ...] ] {
[ javainterfacecode:cptrmethod typemap attribute ]
... interface declarations ...
}
@ -6995,7 +7085,9 @@ The "javaimports" typemap is ignored if the enum class is wrapped by an inner Ja
</div>
<p>
where <tt>javainterfacename</tt> is the <tt>name</tt> attribute in the <a href="Java.html#Java_interfaces">interface feature</a>.
where <tt>name</tt> is the <tt>name</tt> attribute and
<tt>additional</tt> is the <tt>additional</tt> attribute
in the <a href="Java.html#Java_interfaces">interface feature</a>.
</p>
<p>

View File

@ -130,3 +130,48 @@ namespace Space {
};
}
%}
// Test additional interfaces - these are designed for non-C++ interfaces
#if defined(SWIGJAVA) || defined(SWIGCSHARP)
%interface_custom("Additional1", "IAdditional1", IAdditional1)
#endif
#if defined(SWIGJAVA)
%interface_additional("Additional2", "IAdditional2", "java.util.EventListener", IAdditional2)
%interface_additional("Additional3", "IAdditional3", "java.util.EventListener", IAdditional3)
#elif defined(SWIGCSHARP)
%interface_additional("Additional2", "IAdditional2", "global::System.ICloneable", IAdditional2)
%interface_additional("Additional3", "IAdditional3", "global::System.ICloneable", IAdditional3)
%extend IAdditional2 {
%proxycode %{
public virtual object Clone() {
return new Additional2(this);
}
%}
}
%extend IAdditional3 {
%proxycode %{
public virtual object Clone() {
return new Additional3(this);
}
%}
}
%extend AdditionalConcrete {
%proxycode %{
public virtual object Clone() {
return new AdditionalConcrete(this);
}
%}
}
#endif
%copyctor AdditionalConcrete;
%copyctor IAdditional2;
%copyctor IAdditional3;
%inline %{
struct IAdditional1 { virtual ~IAdditional1() {} };
struct IAdditional2 { virtual ~IAdditional2() {} };
struct IAdditional3 : IAdditional1, IAdditional2 {};
struct AdditionalConcrete : IAdditional1, IAdditional2 {};
%}

View File

@ -61,3 +61,8 @@ INTERFACE_TYPEMAPS(CTYPE)
INTERFACE_TYPEMAPS(CTYPE)
%enddef
%define %interface_additional(PROXY, INTERFACE, ADDITIONAL, CTYPE...)
%rename(PROXY) CTYPE;
%feature("interface", name=INTERFACE, additional=ADDITIONAL) CTYPE;
INTERFACE_TYPEMAPS(CTYPE)
%enddef

View File

@ -72,3 +72,8 @@ INTERFACE_TYPEMAPS(CTYPE)
INTERFACE_TYPEMAPS(CTYPE)
%enddef
%define %interface_additional(PROXY, INTERFACE, ADDITIONAL, CTYPE...)
%rename(PROXY) CTYPE;
%feature("interface", name=INTERFACE, additional=ADDITIONAL) CTYPE;
INTERFACE_TYPEMAPS(CTYPE)
%enddef

View File

@ -2103,8 +2103,10 @@ public:
Printv(f_interface, typemapLookup(n, "csimports", Getattr(n, "classtypeobj"), WARN_NONE), "\n", NIL);
Printv(f_interface, typemapLookup(n, "csinterfacemodifiers", Getattr(n, "classtypeobj"), WARN_CSHARP_TYPEMAP_INTERFACEMODIFIERS_UNDEF), NIL);
Printf(f_interface, " %s", interface_name);
String *additional = Getattr(n, "feature:interface:additional");
String *bases = additional ? NewStringf(" : %s", additional) : 0;
if (List *baselist = Getattr(n, "bases")) {
String *bases = 0;
for (Iterator base = First(baselist); base.item; base = Next(base)) {
if (GetFlag(base.item, "feature:ignore") || !GetFlag(base.item, "feature:interface"))
continue; // TODO: warn about skipped non-interface bases
@ -2116,10 +2118,10 @@ public:
Append(bases, base_iname);
}
}
if (bases) {
Printv(f_interface, bases, NIL);
Delete(bases);
}
}
if (bases) {
Printv(f_interface, bases, NIL);
Delete(bases);
}
Printf(f_interface, " {\n");

View File

@ -2094,8 +2094,10 @@ public:
Printv(f_interface, typemapLookup(n, "javaimports", Getattr(n, "classtypeobj"), WARN_NONE), "\n", NIL);
Printv(f_interface, typemapLookup(n, "javainterfacemodifiers", Getattr(n, "classtypeobj"), WARN_JAVA_TYPEMAP_INTERFACEMODIFIERS_UNDEF), NIL);
Printf(f_interface, " %s", interface_name);
String *additional = Getattr(n, "feature:interface:additional");
String *bases = additional ? Copy(additional) : 0;
if (List *baselist = Getattr(n, "bases")) {
String *bases = 0;
for (Iterator base = First(baselist); base.item; base = Next(base)) {
if (GetFlag(base.item, "feature:ignore") || !GetFlag(base.item, "feature:interface"))
continue; // TODO: warn about skipped non-interface bases
@ -2107,10 +2109,10 @@ public:
Append(bases, base_iname);
}
}
if (bases) {
Printv(f_interface, " extends ", bases, NIL);
Delete(bases);
}
}
if (bases) {
Printv(f_interface, " extends ", bases, NIL);
Delete(bases);
}
Printf(f_interface, " {\n");