Wrap friend functions that are defined or declared within a namespace

Previously unqualified friend definitions/declarations in a namespace were
ignored.
This commit is contained in:
William S Fulton 2024-01-15 09:13:17 +00:00
parent 77210e13af
commit 0a16044a27
8 changed files with 121 additions and 25 deletions

View File

@ -7,6 +7,15 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/
Version 4.2.1 (in progress)
===========================
2024-01-15: wsfulton
https://sourceforge.net/p/swig/bugs/807/
Wrap friend functions that are defined or declared within a namespace.
Previously unqualified friend definitions/declarations in a namespace were
ignored.
The visibility of unqualified friend functions in C++ is somewhat quirky
and the documentation has been enhanced to aid wrapping of friends.
2024-01-12: wsfulton
#2749 Fix seg fault handling friend constructor/destructor declarations.

View File

@ -2,6 +2,11 @@ using System;
using friendsNamespace;
public class friends_runme {
private static void check_equal(int a, int b) {
if (a != b)
throw new Exception("Not equal " + a + " != " + b);
}
public static void Main() {
A a = new A(2);
@ -43,6 +48,17 @@ public class friends_runme {
throw new Exception("failed");
if (friends.mate_blah() != 4321)
throw new Exception("failed");
Foe foe = new Foe(111);
check_equal(friends.friend_definition(), 10);
check_equal(friends.friend_declaration(), 11);
check_equal(friends.friend_args_definition(foe), 111);
check_equal(friends.friend_args_declaration(foe), 111);
check_equal(friends.friend_definition_compiler(), 20);
check_equal(friends.friend_declaration_compiler(), 21);
check_equal(friends.friend_args_definition_compiler(foe), 111);
check_equal(friends.friend_args_declaration_compiler(foe), 111);
}
}

View File

@ -5,7 +5,6 @@
%warnfilter(SWIGWARN_LANG_IDENTIFIER) operator<<;
%warnfilter(SWIGWARN_LANG_IDENTIFIER) operator>>;
%warnfilter(SWIGWARN_LANG_IDENTIFIER) ns1::ns2::bar; // This warning suppression is hiding a bug when using namespaces and friends, warning should not be issued
#if defined(SWIGOCTAVE)
%warnfilter(SWIGWARN_IGNORE_OPERATOR_LSHIFT_MSG) operator<<;
@ -141,9 +140,7 @@
};
namespace ns1 {
void bas() {}
void baz() {}
}
}
@ -208,16 +205,45 @@ void Mate::private_function() { this->val = 4321; }
%}
// Foe class tests friend definitions/declarations in a namespace
%inline %{
namespace ns1 {
namespace ns2 {
class Foo {
class Foe {
int val;
public:
Foo() {}
friend void bar();
friend void ns1::baz();
Foe() : val() {}
Foe(int val) : val(val) {}
// Unqualified friends visible to SWIG in outer scope
friend int friend_definition() { return Foe(10).val; }
friend int friend_declaration();
friend int friend_args_definition(Foe &foe) { return foe.val; }
friend int friend_args_declaration(Foe &foe);
// Unqualified friends only visible to C++ compiler in outer scope
friend int friend_definition_compiler() { return Foe(20).val; }
friend int friend_declaration_compiler();
friend int friend_args_definition_compiler(Foe &foe) { return foe.val; }
friend int friend_args_declaration_compiler(Foe &foe);
// Qualified friend (silently ignored)
friend void ns1::baz();
};
void bar() {}
int friend_definition();
int friend_declaration() { return Foe(11).val; }
int friend_args_definition(Foe &foe);
int friend_args_declaration(Foe &foe) { return foe.val; }
}
}
%}
%{
namespace ns1 {
namespace ns2 {
int friend_definition_compiler();
int friend_declaration_compiler() { return Foe(21).val; }
// int friend_args_definition_compiler(Foe &foe); // ADL is used to find this, so no declaration is needed
int friend_args_declaration_compiler(Foe &foe) { return foe.val; }
}
}
%}

View File

@ -11,6 +11,11 @@ public class friends_runme {
}
}
private static void check_equal(int a, int b) {
if (a != b)
throw new RuntimeException("Not equal " + a + " != " + b);
}
public static void main(String argv[]) throws Throwable
{
A a = new A(2);
@ -53,6 +58,17 @@ public class friends_runme {
throw new RuntimeException("failed");
if (friends.mate_blah() != 4321)
throw new RuntimeException("failed");
Foe foe = new Foe(111);
check_equal(friends.friend_definition(), 10);
check_equal(friends.friend_declaration(), 11);
check_equal(friends.friend_args_definition(foe), 111);
check_equal(friends.friend_args_declaration(foe), 111);
check_equal(friends.friend_definition_compiler(), 20);
check_equal(friends.friend_declaration_compiler(), 21);
check_equal(friends.friend_args_definition_compiler(foe), 111);
check_equal(friends.friend_args_declaration_compiler(foe), 111);
}
}

View File

@ -2,8 +2,8 @@
require "tests.php";
check::functions(array('globalscope','mix','get_val2','get_val3','bas','baz','bar','get_val1','set','chum_blah','mate_blah'));
check::classes(array('friends','Foo','A','B','D_i','D_d','CModelParameterCompartment','CModelParameterSpecies','Chum','Mate'));
check::functions(array('globalscope','mix','get_val2','get_val3','bas','baz','get_val1','set','chum_blah','mate_blah','friend_definition','friend_declaration','friend_args_definition','friend_args_declaration','friend_definition_compiler','friend_declaration_compiler','friend_args_definition_compiler','friend_args_declaration_compiler'));
check::classes(array('friends','Foe','A','B','D_i','D_d','CModelParameterCompartment','CModelParameterSpecies','Chum','Mate'));
// No new vars
check::globals(array());
@ -37,4 +37,15 @@ check::equal(get_val1($dd), 1.3);
check::equal(chum_blah(), 1234);
check::equal(mate_blah(), 4321);
$foe = new Foe(111);
check::equal(friend_definition(), 10);
check::equal(friend_declaration(), 11);
check::equal(friend_args_definition($foe), 111);
check::equal(friend_args_declaration($foe), 111);
check::equal(friend_definition_compiler(), 20);
check::equal(friend_declaration_compiler(), 21);
check::equal(friend_args_definition_compiler($foe), 111);
check::equal(friend_args_declaration_compiler($foe), 111);
check::done();

View File

@ -1,5 +1,9 @@
import friends
def check_equal(a, b):
if a != b:
raise RuntimeError("Not equal {} != {}".format(a, b))
a = friends.A(2)
if friends.get_val1(a) != 2:
@ -40,3 +44,14 @@ if friends.chum_blah() != 1234:
raise RuntimeError("failed")
if friends.mate_blah() != 4321:
raise RuntimeError("failed")
foe = friends.Foe(111)
check_equal(friends.friend_definition(), 10)
check_equal(friends.friend_declaration(), 11)
check_equal(friends.friend_args_definition(foe), 111)
check_equal(friends.friend_args_declaration(foe), 111)
check_equal(friends.friend_definition_compiler(), 20)
check_equal(friends.friend_declaration_compiler(), 21)
check_equal(friends.friend_args_definition_compiler(foe), 111)
check_equal(friends.friend_args_declaration_compiler(foe), 111)

View File

@ -411,7 +411,6 @@ static void add_symbols(Node *n) {
}
while (n) {
String *symname = 0;
/* for friends, we need to pop the scope once */
String *old_prefix = 0;
Symtab *old_scope = 0;
int isfriend = inclass && Checkattr(n, "storage", "friend");
@ -421,26 +420,17 @@ static void add_symbols(Node *n) {
if (inclass) {
String *name = Getattr(n, "name");
if (isfriend) {
/* for friends, we need to add the scopename if needed */
/* For friends, set the scope to the same as the class that the friend is defined/declared in, that is, pop scope once */
String *prefix = name ? Swig_scopename_prefix(name) : 0;
old_prefix = Namespaceprefix;
old_scope = Swig_symbol_popscope();
Namespaceprefix = Swig_symbol_qualifiedscopename(0);
if (!prefix) {
if (name && !is_operator(name) && Namespaceprefix) {
String *nname = NewStringf("%s::%s", Namespaceprefix, name);
Setattr(n,"name",nname);
Delete(nname);
String *friendusing = NewStringf("using namespace %s;", Namespaceprefix);
Setattr(n, "friendusing", friendusing);
Delete(friendusing);
}
} else {
Symtab *st = Swig_symbol_getscope(prefix);
String *ns = st ? Getattr(st,"name") : prefix;
String *base = Swig_scopename_last(name);
String *nname = NewStringf("%s::%s", ns, base);
Setattr(n,"name",nname);
Delete(nname);
Delete(base);
Delete(prefix);
}
Namespaceprefix = 0;
} else if (Equal(nodeType(n), "using")) {

View File

@ -1166,7 +1166,20 @@ int Language::globalfunctionHandler(Node *n) {
String *extendname = Getattr(n, "extendname");
String *call = Swig_cfunction_call(extendname ? extendname : name, parms);
String *cres = Swig_cresult(type, Swig_cresult_name(), call);
Setattr(n, "wrap:action", cres);
String *friendusing = Getattr(n, "friendusing");
if (friendusing) {
// Add a using directive to avoid having to possibly fully qualify the call to the friend function.
// Unconventional for SWIG generation, but the alternative is to implement Argument Dependent Lookup
// as friend functions are quirky and not visible, except for ADL. An ADL implementation would be needed
// in order to work out when the friend function is visible or not, in order to determine whether to
// rely on ADL (with no qualification) or to fully qualify the call to the friend function made
// visible via a matching declaration at namespace scope.
String *action = NewStringf("%s\n%s", friendusing, cres);
Setattr(n, "wrap:action", action);
Delete(action);
} else {
Setattr(n, "wrap:action", cres);
}
Delete(cres);
Delete(call);
functionWrapper(n);