Fixes for the family of %interface macros for overloaded methods

When C++ methods are not able to be overloaded in a derived class,
such as when they differ by just const, or the target language
parameters types are identical even when the C++ parameter types
are different, SWIG will ignore one of the overloaded methods with
a warning. A %ignore is required to explicitly ignore one of the
overloaded methods to avoid the warning message. Methods added
in the derived classes due to one of the %interface macros are now
similarly ignored/not added to the derived class.

The adding of additional methods into the parse tree is now more
robust and complete resulting in support for %feature and %rename
for the added methods.

Closes #1277
This commit is contained in:
William S Fulton 2022-03-12 12:46:59 +00:00
commit b6ece11fc1
12 changed files with 259 additions and 15 deletions

View file

@ -7,6 +7,23 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/
Version 4.1.0 (in progress) Version 4.1.0 (in progress)
=========================== ===========================
2022-03-12: wsfulton
#1277 Fixes for the family of %interface macros, %interface,
%interface_impl and %interface_custom fixes for overloaded methods
in an inheritance chain.
When C++ methods are not able to be overloaded in a derived class,
such as when they differ by just const, or the target language
parameters types are identical even when the C++ parameter types
are different, SWIG will ignore one of the overloaded methods with
a warning. A %ignore is required to explicitly ignore one of the
overloaded methods to avoid the warning message. Methods added
in the derived classes due to one of the %interface macros are now
similarly ignored/not added to the derived class.
The methods added to the derived classes can now also be modified
via %feature and %rename.
2022-03-08: olly 2022-03-08: olly
#1006 SWIG now copes with an interface filename specified on the #1006 SWIG now copes with an interface filename specified on the
command line which contains a closing parenthesis `)`, and more command line which contains a closing parenthesis `)`, and more
@ -626,7 +643,6 @@ Version 4.1.0 (in progress)
%typemap(csinterfacemodifiers) X "internal interface" %typemap(csinterfacemodifiers) X "internal interface"
2020-09-24: geefr 2020-09-24: geefr
[C#] #1868 Fix wchar_t* csvarout typemap for member variable wrappers. [C#] #1868 Fix wchar_t* csvarout typemap for member variable wrappers.

View file

@ -3434,9 +3434,11 @@ Consider the following C++ code:
namespace Space { namespace Space {
struct Base1 { struct Base1 {
virtual void Method1(); virtual void Method1();
virtual Base1();
}; };
struct Base2 { struct Base2 {
virtual void Method2(); virtual void Method2();
virtual Base2();
}; };
struct Derived : Base1, Base2 { struct Derived : Base1, Base2 {
}; };
@ -3453,7 +3455,7 @@ SWIG generates a warning for the above code:
<div class="shell"> <div class="shell">
<pre> <pre>
example.i:10: Warning 813: Warning for Derived, base Base2 ignored. example.i:12: Warning 813: Warning for Derived, base Base2 ignored.
Multiple inheritance is not supported in Java. Multiple inheritance is not supported in Java.
</pre> </pre>
</div> </div>
@ -3506,7 +3508,7 @@ public class Base1SwigImpl implements Base1 {
</div> </div>
<p> <p>
In fact any class deriving from <tt>Base</tt> will now implement the interface instead of In fact any class using <tt>Base</tt> as an immediate base class will now implement the interface instead of
deriving from it (or ignoring the base in the case of multiple base classes). deriving from it (or ignoring the base in the case of multiple base classes).
Hence the <tt>Derived</tt> proxy class will now implement both bases: Hence the <tt>Derived</tt> proxy class will now implement both bases:
</p> </p>
@ -3535,6 +3537,15 @@ public class Derived implements Base1, Base2 {
</pre> </pre>
</div> </div>
<p>
The proxy class has methods added to it, from the implemented bases, so that
the underlying C++ implementation can be called.
In the example above, <tt>Method1</tt> and <tt>Method2</tt> have been added from the implemented bases.
If a method is ignored in the base, such as via <tt>%ignore</tt>, then that method
will be excluded from the interface and there will not be an additional method
added to the proxy class implementing that interface.
</p>
<p> <p>
Wherever a class marked as an interface is used, such as the <tt>UseBases</tt> method in the example, Wherever a class marked as an interface is used, such as the <tt>UseBases</tt> method in the example,
the interface name is used as the type in the Java layer: the interface name is used as the type in the Java layer:

View file

@ -308,6 +308,7 @@ CPP_TEST_CASES += \
multiple_inheritance_abstract \ multiple_inheritance_abstract \
multiple_inheritance_interfaces \ multiple_inheritance_interfaces \
multiple_inheritance_nspace \ multiple_inheritance_nspace \
multiple_inheritance_overload \
multiple_inheritance_shared_ptr \ multiple_inheritance_shared_ptr \
name_cxx \ name_cxx \
name_warnings \ name_warnings \

View file

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using multiple_inheritance_overloadNamespace;
public class multiple_inheritance_overload_runme {
public static void check(bool fail, String msg) {
if (fail)
throw new Exception(msg);
}
public static void Main() {
int i = 0;
Base b1 = new Derived();
check(b1.Method(i) != 0, "b1.Method failed");
check(b1.MethodForRenaming(i) != 0, "b1.MethodForRenaming failed");
check(b1.MethodForRenamingConst(i) != 1, "b1.MethodForRenamingConst failed");
check(b1.MethodWarningSuppressed(i) != 0, "b1.MethodWarningSuppressed failed");
check(b1.NotVirtualMethod(i) != 0, "b1.NotVirtualMethod failed");
check(b1.SimilarOverloadedMethod(i) != 0, "b1.NotVirtualMethod failed");
Derived d1 = new Derived();
check(d1.Method(i) != 0, "d1.Method failed");
check(d1.MethodForRenaming(i) != 0, "d1.MethodForRenaming failed");
check(d1.MethodForRenamingConst(i) != 1, "d1.MethodForRenamingConst failed");
check(d1.MethodWarningSuppressed(i) != 0, "d1.MethodWarningSuppressed failed");
check(d1.NotVirtualMethod(i) != 0, "d1.NotVirtualMethod failed");
check(d1.SimilarOverloadedMethod(i) != 0, "d1.NotVirtualMethod failed");
check(d1.AnotherMethod(i) != 0, "d1.AnotherMethod failed");
Base db1 = BaseSwigImpl.inout(d1);
check(db1.Method(i) != 0, "db1.Method failed");
check(db1.MethodForRenaming(i) != 0, "db1.MethodForRenaming failed");
check(db1.MethodForRenamingConst(i) != 1, "db1.MethodForRenamingConst failed");
check(db1.MethodWarningSuppressed(i) != 0, "db1.MethodWarningSuppressed failed");
check(db1.NotVirtualMethod(i) != 0, "db1.NotVirtualMethod failed");
check(db1.SimilarOverloadedMethod(i) != 0, "db1.NotVirtualMethod failed");
MoreDerived m1 = new MoreDerived();
check(m1.Method(i) != 0, "m1.Method failed");
check(m1.MethodForRenaming(i) != 0, "m1.MethodForRenaming failed");
check(m1.MethodForRenamingConst(i) != 1, "m1.MethodForRenamingConst failed");
check(m1.MethodWarningSuppressed(i) != 0, "m1.MethodWarningSuppressed failed");
check(m1.NotVirtualMethod(i) != 0, "m1.NotVirtualMethod failed");
check(m1.SimilarOverloadedMethod(i) != 0, "m1.NotVirtualMethod failed");
check(m1.AnotherMethod(i) != 0, "m1.AnotherMethod failed");
Base mb2 = BaseSwigImpl.inout(m1);
check(mb2.Method(i) != 0, "mb2.Method failed");
check(mb2.MethodForRenaming(i) != 0, "mb2.MethodForRenaming failed");
check(mb2.MethodForRenamingConst(i) != 1, "mb2.MethodForRenamingConst failed");
check(mb2.MethodWarningSuppressed(i) != 0, "mb2.MethodWarningSuppressed failed");
check(mb2.NotVirtualMethod(i) != 0, "mb2.NotVirtualMethod failed");
check(mb2.SimilarOverloadedMethod(i) != 0, "mb2.NotVirtualMethod failed");
}
}

View file

@ -0,0 +1,65 @@
import multiple_inheritance_overload.*;
public class multiple_inheritance_overload_runme {
static {
try {
System.loadLibrary("multiple_inheritance_overload");
} 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 check(boolean fail, String msg) {
if (fail)
throw new RuntimeException(msg);
}
public static void main(String argv[]) {
int i = 0;
Base b1 = new Derived();
check(b1.Method(i) != 0, "b1.Method failed");
check(b1.MethodForRenaming(i) != 0, "b1.MethodForRenaming failed");
check(b1.MethodForRenamingConst(i) != 1, "b1.MethodForRenamingConst failed");
check(b1.MethodWarningSuppressed(i) != 0, "b1.MethodWarningSuppressed failed");
check(b1.NotVirtualMethod(i) != 0, "b1.NotVirtualMethod failed");
check(b1.SimilarOverloadedMethod(i) != 0, "b1.NotVirtualMethod failed");
Derived d1 = new Derived();
check(d1.Method(i) != 0, "d1.Method failed");
check(d1.MethodForRenaming(i) != 0, "d1.MethodForRenaming failed");
check(d1.MethodForRenamingConst(i) != 1, "d1.MethodForRenamingConst failed");
check(d1.MethodWarningSuppressed(i) != 0, "d1.MethodWarningSuppressed failed");
check(d1.NotVirtualMethod(i) != 0, "d1.NotVirtualMethod failed");
check(d1.SimilarOverloadedMethod(i) != 0, "d1.NotVirtualMethod failed");
check(d1.AnotherMethod(i) != 0, "d1.AnotherMethod failed");
Base db1 = BaseSwigImpl.inout(d1);
check(db1.Method(i) != 0, "db1.Method failed");
check(db1.MethodForRenaming(i) != 0, "db1.MethodForRenaming failed");
check(db1.MethodForRenamingConst(i) != 1, "db1.MethodForRenamingConst failed");
check(db1.MethodWarningSuppressed(i) != 0, "db1.MethodWarningSuppressed failed");
check(db1.NotVirtualMethod(i) != 0, "db1.NotVirtualMethod failed");
check(db1.SimilarOverloadedMethod(i) != 0, "db1.NotVirtualMethod failed");
MoreDerived m1 = new MoreDerived();
check(m1.Method(i) != 0, "m1.Method failed");
check(m1.MethodForRenaming(i) != 0, "m1.MethodForRenaming failed");
check(m1.MethodForRenamingConst(i) != 1, "m1.MethodForRenamingConst failed");
check(m1.MethodWarningSuppressed(i) != 0, "m1.MethodWarningSuppressed failed");
check(m1.NotVirtualMethod(i) != 0, "m1.NotVirtualMethod failed");
check(m1.SimilarOverloadedMethod(i) != 0, "m1.NotVirtualMethod failed");
check(m1.AnotherMethod(i) != 0, "m1.AnotherMethod failed");
Base mb2 = BaseSwigImpl.inout(m1);
check(mb2.Method(i) != 0, "mb2.Method failed");
check(mb2.MethodForRenaming(i) != 0, "mb2.MethodForRenaming failed");
check(mb2.MethodForRenamingConst(i) != 1, "mb2.MethodForRenamingConst failed");
check(mb2.MethodWarningSuppressed(i) != 0, "mb2.MethodWarningSuppressed failed");
check(mb2.NotVirtualMethod(i) != 0, "mb2.NotVirtualMethod failed");
check(mb2.SimilarOverloadedMethod(i) != 0, "mb2.NotVirtualMethod failed");
}
}

View file

@ -5,7 +5,7 @@
SWIGWARN_PHP_MULTIPLE_INHERITANCE); /* languages not supporting multiple inheritance */ SWIGWARN_PHP_MULTIPLE_INHERITANCE); /* languages not supporting multiple inheritance */
#if defined(SWIGJAVA) || defined(SWIGCSHARP) #if defined(SWIGJAVA) || defined(SWIGCSHARP)
%include "swiginterface.i" %include <swiginterface.i>
%interface_impl(Space::ABase1) %interface_impl(Space::ABase1)
%interface_impl(Space::CBase1) %interface_impl(Space::CBase1)
%interface_impl(Space::CBase2) %interface_impl(Space::CBase2)

View file

@ -4,7 +4,7 @@
SWIGWARN_PHP_MULTIPLE_INHERITANCE); /* languages not supporting multiple inheritance */ SWIGWARN_PHP_MULTIPLE_INHERITANCE); /* languages not supporting multiple inheritance */
#if defined(SWIGJAVA) || defined(SWIGCSHARP) #if defined(SWIGJAVA) || defined(SWIGCSHARP)
%include "swiginterface.i" %include <swiginterface.i>
%interface_custom("A", "IA", IA) %interface_custom("A", "IA", IA)
%interface_custom("B", "IB", IB) %interface_custom("B", "IB", IB)
%interface_custom("%(strip:[I])s", "I%s", IC) // same as %interface_custom("C", "IC", IC) %interface_custom("%(strip:[I])s", "I%s", IC) // same as %interface_custom("C", "IC", IC)

View file

@ -10,7 +10,7 @@
#endif #endif
#if defined(SWIGJAVA) || defined(SWIGCSHARP) #if defined(SWIGJAVA) || defined(SWIGCSHARP)
%include "swiginterface.i" %include <swiginterface.i>
%interface(Space::ABase1) %interface(Space::ABase1)
%interface(Space::CBase1) %interface(Space::CBase1)
%interface(Space::CBase2) %interface(Space::CBase2)

View file

@ -0,0 +1,67 @@
%module(ruby_minherit="1") multiple_inheritance_overload
%warnfilter(SWIGWARN_D_MULTIPLE_INHERITANCE,
SWIGWARN_PHP_MULTIPLE_INHERITANCE); /* languages not supporting multiple inheritance */
#if defined(SWIGJAVA) || defined(SWIGCSHARP)
%include <swiginterface.i>
%interface_impl(Space::Base);
%interface_impl(Space::AnotherBase);
#endif
%ignore AnotherSpace::AnotherBase::AnotherMethod(int i) const;
%ignore Space::Base::Method(int i) const;
%ignore Space::Base::NotVirtualMethod(int i) const;
%ignore Space::Base::SimilarOverloadedMethod(unsigned short i);
%rename(MethodForRenamingConst) Space::Base::MethodForRenaming(int i) const;
// Different overloaded warning filters needed for scripting languages (eg Python) and for statically typed languages (eg C#).
%warnfilter(509, 516) Space::Base::MethodWarningSuppressed(int i) const;
%inline %{
namespace AnotherSpace {
class AnotherBase {
public:
virtual int AnotherMethod(int i) { return 0; }
virtual int AnotherMethod(int i) const { return 1; }
virtual ~AnotherBase() {}
};
}
namespace Space {
class Base
{
public:
virtual int Method(int i) { return 0; }
virtual int Method(int i) const { return 1; }
virtual int MethodForRenaming(int i) { return 0; }
virtual int MethodForRenaming(int i) const { return 1; }
virtual int MethodWarningSuppressed(int i) { return 0; }
virtual int MethodWarningSuppressed(int i) const { return 1; }
int NotVirtualMethod(int i) { return 0; }
int NotVirtualMethod(int i) const { return 1; }
typedef int Integer;
// int and unsigned short are wrapped with a Java int and so would be automatically ignored with a warning
virtual int SimilarOverloadedMethod(Integer i) { return 0; }
virtual int SimilarOverloadedMethod(unsigned short i) { return 1; }
virtual ~Base() {}
static Base *inout(Base *p) { return p; }
};
class Derived : public Base, public AnotherSpace::AnotherBase
{
public:
int member_var;
};
class MoreDerived : public Derived {
};
}
namespace OtherSpace {
class OtherDerived : public Space::Base
{
};
}
%}

View file

@ -48,7 +48,7 @@
%shared_ptr(Space::Bottom2) %shared_ptr(Space::Bottom2)
%shared_ptr(Space::Bottom3) %shared_ptr(Space::Bottom3)
%include "swiginterface.i" %include <swiginterface.i>
SWIG_SHARED_PTR_INTERFACE_TYPEMAPS(, Space::ABase1) SWIG_SHARED_PTR_INTERFACE_TYPEMAPS(, Space::ABase1)
SWIG_SHARED_PTR_INTERFACE_TYPEMAPS(, Space::CBase1) SWIG_SHARED_PTR_INTERFACE_TYPEMAPS(, Space::CBase1)
SWIG_SHARED_PTR_INTERFACE_TYPEMAPS(, Space::CBase2) SWIG_SHARED_PTR_INTERFACE_TYPEMAPS(, Space::CBase2)

View file

@ -5,7 +5,7 @@
SWIGWARN_PHP_MULTIPLE_INHERITANCE); /* languages not supporting multiple inheritance or %interface */ SWIGWARN_PHP_MULTIPLE_INHERITANCE); /* languages not supporting multiple inheritance or %interface */
#if defined(SWIGJAVA) || defined(SWIGCSHARP) #if defined(SWIGJAVA) || defined(SWIGCSHARP)
%include "swiginterface.i" %include <swiginterface.i>
%interface(IA) %interface(IA)
#endif #endif

View file

@ -15,6 +15,7 @@
* ----------------------------------------------------------------------------- */ * ----------------------------------------------------------------------------- */
#include "swigmod.h" #include "swigmod.h"
#include "cparse.h"
static bool interface_feature_enabled = false; static bool interface_feature_enabled = false;
@ -38,11 +39,19 @@ static List *collect_interface_methods(Node *n) {
if (Cmp(nodeType(child), "cdecl") == 0) { if (Cmp(nodeType(child), "cdecl") == 0) {
if (GetFlag(child, "feature:ignore") || Getattr(child, "interface:owner")) if (GetFlag(child, "feature:ignore") || Getattr(child, "interface:owner"))
continue; // skip methods propagated to bases continue; // skip methods propagated to bases
Node *m = Copy(child); if (!checkAttribute(child, "kind", "function"))
set_nextSibling(m, NIL); continue;
set_previousSibling(m, NIL); if (checkAttribute(child, "storage", "static"))
Setattr(m, "interface:owner", cls); continue; // accept virtual methods, non-virtual methods too... mmm??. Warn that the interface class has something that is not a virtual method?
Append(methods, m); Node *nn = copyNode(child);
Setattr(nn, "interface:owner", cls);
ParmList *parms = CopyParmList(Getattr(child, "parms"));
Setattr(nn, "parms", parms);
Delete(parms);
ParmList *throw_parm_list = Getattr(child, "throws");
if (throw_parm_list)
Setattr(nn, "throws", CopyParmList(throw_parm_list));
Append(methods, nn);
} }
} }
} }
@ -164,8 +173,25 @@ void Swig_interface_propagate_methods(Node *n) {
} }
Delete(this_decl_resolved); Delete(this_decl_resolved);
if (!identically_overloaded_method) { if (!identically_overloaded_method) {
// TODO: Fix if the method is overloaded with different arguments / has default args // Add method copied from base class to this derived class
appendChild(n, mi.item); Node *cn = mi.item;
Delattr(cn, "sym:overname");
String *prefix = Getattr(n, "name");
String *name = Getattr(cn, "name");
String *decl = Getattr(cn, "decl");
String *oldname = Getattr(cn, "sym:name");
String *symname = Swig_name_make(cn, prefix, name, decl, oldname);
if (Strcmp(symname, "$ignore") != 0) {
Symtab *oldscope = Swig_symbol_setscope(Getattr(n, "symtab"));
Node *on = Swig_symbol_add(symname, cn);
assert(on == cn);
// Features from the copied base class method are already present, now add in features specific to the added method in the derived class
Swig_features_get(Swig_cparse_features(), Swig_symbol_qualifiedscopename(0), name, decl, cn);
Swig_symbol_setscope(oldscope);
appendChild(n, cn);
}
} else { } else {
Delete(mi.item); Delete(mi.item);
} }