From 428b6176df750e41e3db98f029381af0df2a34c3 Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Mon, 4 May 2015 10:01:34 +0100 Subject: [PATCH] Add support for friend templates, including operator overloading. Closes #196. --- CHANGES.current | 21 +++++++++ Examples/test-suite/common.mk | 1 + .../test-suite/errors/cpp_template_friend.i | 26 +++++++++++ .../errors/cpp_template_friend.stderr | 8 ++++ Examples/test-suite/friends_template.i | 46 +++++++++++++++++++ .../java/friends_template_runme.java | 28 +++++++++++ Source/CParse/parser.y | 4 +- Source/CParse/templ.c | 9 ++++ Source/Modules/lang.cxx | 2 +- Source/Swig/naming.c | 4 ++ 10 files changed, 146 insertions(+), 3 deletions(-) create mode 100644 Examples/test-suite/errors/cpp_template_friend.i create mode 100644 Examples/test-suite/errors/cpp_template_friend.stderr create mode 100644 Examples/test-suite/friends_template.i create mode 100644 Examples/test-suite/java/friends_template_runme.java diff --git a/CHANGES.current b/CHANGES.current index a3b03b07a3e..3bc2a950bed 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -5,6 +5,27 @@ See the RELEASENOTES file for a summary of changes in each release. Version 3.0.6 (in progress) =========================== +2015-05-04: wsfulton + Add support for friend templates, including operator overloading - fixes #196. Considering + the example below, previously the operator gave a syntax error and friendfunc incorrectly + warned with: + + "Warning 503: Can't wrap 'friendfunc<(Type)>' unless renamed to a valid identifier." + + template class MyClass { + friend int friendfunc (double is, MyClass & x); + friend int operator<< (double un, const MyClass &x); + }; + + The following also previously incorrectly warned with: + + "Warning 302: Identifier 'template_friend' redefined (ignored)," + + template T template_friend(T); + struct MyTemplate { + template friend T template_friend(T); + }; + 2015-05-01: wsfulton Fix handling of conversion operators where the operator is split over multiple lines or has comments within the operator type. Fixes #401. diff --git a/Examples/test-suite/common.mk b/Examples/test-suite/common.mk index e2ab8ed3f1f..c9e7b701d0a 100644 --- a/Examples/test-suite/common.mk +++ b/Examples/test-suite/common.mk @@ -234,6 +234,7 @@ CPP_TEST_CASES += \ features \ fragments \ friends \ + friends_template \ funcptr_cpp \ fvirtual \ global_namespace \ diff --git a/Examples/test-suite/errors/cpp_template_friend.i b/Examples/test-suite/errors/cpp_template_friend.i new file mode 100644 index 00000000000..c9d1c9d5d13 --- /dev/null +++ b/Examples/test-suite/errors/cpp_template_friend.i @@ -0,0 +1,26 @@ +%module cpp_template_friend + +template T template_friend1(T); +template T template_friend1(T); +struct MyTemplate1 { + template friend T template_friend1(T); +}; + +template T template_friend2(T); +struct MyTemplate2 { + template friend T template_friend2(T); +}; +template T template_friend2(T); + + +int normal_friend1(int); +int normal_friend1(int); +struct MyClass1 { + friend int normal_friend1(int); +}; + +int normal_friend2(int); +struct MyClass2 { + friend int normal_friend2(int); +}; +int normal_friend2(int); diff --git a/Examples/test-suite/errors/cpp_template_friend.stderr b/Examples/test-suite/errors/cpp_template_friend.stderr new file mode 100644 index 00000000000..8dea195dc0a --- /dev/null +++ b/Examples/test-suite/errors/cpp_template_friend.stderr @@ -0,0 +1,8 @@ +cpp_template_friend.i:4: Warning 302: Identifier 'template_friend1' redefined (ignored), +cpp_template_friend.i:3: Warning 302: previous definition of 'template_friend1'. +cpp_template_friend.i:13: Warning 302: Identifier 'template_friend2' redefined (ignored), +cpp_template_friend.i:9: Warning 302: previous definition of 'template_friend2'. +cpp_template_friend.i:17: Warning 322: Redundant redeclaration of 'normal_friend1', +cpp_template_friend.i:16: Warning 322: previous declaration of 'normal_friend1'. +cpp_template_friend.i:26: Warning 322: Redundant redeclaration of 'normal_friend2', +cpp_template_friend.i:22: Warning 322: previous declaration of 'normal_friend2'. diff --git a/Examples/test-suite/friends_template.i b/Examples/test-suite/friends_template.i new file mode 100644 index 00000000000..48623f2ca38 --- /dev/null +++ b/Examples/test-suite/friends_template.i @@ -0,0 +1,46 @@ +%module friends_template + +%{ +template class MyClass; + +template int operator<<(double un, const MyClass & x) { return 0; } +template int funk_hidden(double is, MyClass & x) { return 2; } + +template T template_friend_hidden(T t) { return t + 1; } +%} + +%inline %{ +template int operator>>(double is, MyClass & x) { return 1; } +template int funk_seen(double is, MyClass & x) { return 2; } +template T template_friend_seen(T t1, T t2) { return t1 + t2; } +int friend_plain_seen(int i) { return i; } + +template class MyClass +{ + friend int operator<< (double un, const MyClass & x); + friend int operator>> (double is, MyClass & x); + friend int funk_hidden (double is, MyClass & x); + friend int funk_seen (double is, MyClass & x); +}; + +struct MyTemplate { + template friend T template_friend_hidden(T); + template friend T template_friend_seen(T, T); + friend int friend_plain_seen(int i); +}; + +MyClass makeMyClassInt() { return MyClass(); } +%} + +// Although the friends in MyClass are automatically instantiated via %template(MyClassDouble) MyClass, +// the operator friends are not valid and hence %rename is needed. +%rename(OperatorInputDouble) operator>> ; +%rename(OperatorOutputDouble) operator<< ; +%template(MyClassDouble) MyClass; + +%template(TemplateFriendHiddenInt) template_friend_hidden; +%template(TemplateFriendSeenInt) template_friend_seen; + +// These have no %template(XX) MyClass to instantiate, but they can be instantiated separately... +%template(OperatorInputInt) operator>> ; +%template(OperatorFunkSeenInt) funk_seen ; diff --git a/Examples/test-suite/java/friends_template_runme.java b/Examples/test-suite/java/friends_template_runme.java new file mode 100644 index 00000000000..eb66cd5cbea --- /dev/null +++ b/Examples/test-suite/java/friends_template_runme.java @@ -0,0 +1,28 @@ + +import friends_template.*; + +public class friends_template_runme { + + static { + try { + System.loadLibrary("friends_template"); + } 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 main(String argv[]) { + friends_template.OperatorOutputDouble(1.1, new MyClassDouble()); + friends_template.OperatorInputDouble(1.1, new MyClassDouble()); + friends_template.funk_hidden(1.1, new MyClassDouble()); + friends_template.funk_seen(1.1, new MyClassDouble()); + + friends_template.TemplateFriendHiddenInt(0); + friends_template.TemplateFriendSeenInt(0, 0); + + SWIGTYPE_p_MyClassT_int_t myClassInt = friends_template.makeMyClassInt(); + friends_template.OperatorInputInt(1, myClassInt); + friends_template.OperatorFunkSeenInt(1.1, myClassInt); + } +} diff --git a/Source/CParse/parser.y b/Source/CParse/parser.y index 08d391b3827..30b0edfe2c4 100644 --- a/Source/CParse/parser.y +++ b/Source/CParse/parser.y @@ -6453,8 +6453,8 @@ idcolon : idtemplate idcolontail { | NONID DCOLON idtemplate { $$ = NewStringf("::%s",$3); } - | OPERATOR { - $$ = NewString($1); + | OPERATOR template_decl { + $$ = NewStringf("%s%s",$1,$2); } | NONID DCOLON OPERATOR { $$ = NewStringf("::%s",$3); diff --git a/Source/CParse/templ.c b/Source/CParse/templ.c index fe8fc280055..9768f1b99cf 100644 --- a/Source/CParse/templ.c +++ b/Source/CParse/templ.c @@ -97,6 +97,15 @@ static int cparse_template_expand(Node *n, String *tname, String *rname, String Append(cpatchlist, Getattr(n, "sym:name")); } } + if (checkAttribute(n, "storage", "friend")) { + String *symname = Getattr(n, "sym:name"); + if (symname) { + String *stripped_name = SwigType_templateprefix(symname); + Setattr(n, "sym:name", stripped_name); + Delete(stripped_name); + } + Append(typelist, Getattr(n, "name")); + } add_parms(Getattr(n, "parms"), cpatchlist, typelist); add_parms(Getattr(n, "throws"), cpatchlist, typelist); diff --git a/Source/Modules/lang.cxx b/Source/Modules/lang.cxx index 3efd4e425ea..7943dc4c773 100644 --- a/Source/Modules/lang.cxx +++ b/Source/Modules/lang.cxx @@ -940,7 +940,7 @@ int Language::cDeclaration(Node *n) { } if (!validIdentifier(symname)) { - Swig_warning(WARN_LANG_IDENTIFIER, input_file, line_number, "Can't wrap '%s' unless renamed to a valid identifier.\n", symname); + Swig_warning(WARN_LANG_IDENTIFIER, input_file, line_number, "Can't wrap '%s' unless renamed to a valid identifier.\n", SwigType_namestr(symname)); return SWIG_NOWRAP; } diff --git a/Source/Swig/naming.c b/Source/Swig/naming.c index 9e2b4a436ef..272961c2597 100644 --- a/Source/Swig/naming.c +++ b/Source/Swig/naming.c @@ -1003,6 +1003,10 @@ static int nodes_are_equivalent(Node *a, Node *b, int a_inclass) { } return 0; } + if (Equal(ta, "template") && Equal(tb, "template")) { + if (Cmp(a_storage, "friend") == 0 || Cmp(b_storage, "friend") == 0) + return 1; + } } return 0; }