Skip to content

Commit

Permalink
Add support for friend templates, including operator overloading.
Browse files Browse the repository at this point in the history
Closes #196.
  • Loading branch information
wsfulton committed May 5, 2015
1 parent e044dc4 commit 428b617
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 3 deletions.
21 changes: 21 additions & 0 deletions CHANGES.current
Expand Up @@ -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 Type> class MyClass {
friend int friendfunc <Type>(double is, MyClass <Type> & x);
friend int operator<< <Type>(double un, const MyClass <Type> &x);
};

The following also previously incorrectly warned with:

"Warning 302: Identifier 'template_friend' redefined (ignored),"

template<typename T> T template_friend(T);
struct MyTemplate {
template<typename T> 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.
Expand Down
1 change: 1 addition & 0 deletions Examples/test-suite/common.mk
Expand Up @@ -234,6 +234,7 @@ CPP_TEST_CASES += \
features \
fragments \
friends \
friends_template \
funcptr_cpp \
fvirtual \
global_namespace \
Expand Down
26 changes: 26 additions & 0 deletions Examples/test-suite/errors/cpp_template_friend.i
@@ -0,0 +1,26 @@
%module cpp_template_friend

template<typename T> T template_friend1(T);
template<typename T> T template_friend1(T);
struct MyTemplate1 {
template<typename T> friend T template_friend1(T);
};

template<typename T> T template_friend2(T);
struct MyTemplate2 {
template<typename T> friend T template_friend2(T);
};
template<typename T> 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);
8 changes: 8 additions & 0 deletions 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'.
46 changes: 46 additions & 0 deletions Examples/test-suite/friends_template.i
@@ -0,0 +1,46 @@
%module friends_template

%{
template <typename Type> class MyClass;

template <typename Type> int operator<<(double un, const MyClass <Type> & x) { return 0; }
template <typename Type> int funk_hidden(double is, MyClass <Type> & x) { return 2; }

template <typename T> T template_friend_hidden(T t) { return t + 1; }
%}

%inline %{
template <typename Type> int operator>>(double is, MyClass <Type> & x) { return 1; }
template <typename Type> int funk_seen(double is, MyClass <Type> & x) { return 2; }
template <typename T> T template_friend_seen(T t1, T t2) { return t1 + t2; }
int friend_plain_seen(int i) { return i; }

template <class Type> class MyClass
{
friend int operator<< <Type>(double un, const MyClass <Type> & x);
friend int operator>> <Type>(double is, MyClass <Type> & x);
friend int funk_hidden <Type>(double is, MyClass <Type> & x);
friend int funk_seen <Type>(double is, MyClass <Type> & x);
};

struct MyTemplate {
template <typename T> friend T template_friend_hidden(T);
template <typename T> friend T template_friend_seen(T, T);
friend int friend_plain_seen(int i);
};

MyClass<int> makeMyClassInt() { return MyClass<int>(); }
%}

// Although the friends in MyClass are automatically instantiated via %template(MyClassDouble) MyClass<int>,
// the operator friends are not valid and hence %rename is needed.
%rename(OperatorInputDouble) operator>> <double>;
%rename(OperatorOutputDouble) operator<< <double>;
%template(MyClassDouble) MyClass<double>;

%template(TemplateFriendHiddenInt) template_friend_hidden<int>;
%template(TemplateFriendSeenInt) template_friend_seen<int>;

// These have no %template(XX) MyClass<int> to instantiate, but they can be instantiated separately...
%template(OperatorInputInt) operator>> <int>;
%template(OperatorFunkSeenInt) funk_seen <int>;
28 changes: 28 additions & 0 deletions 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);
}
}
4 changes: 2 additions & 2 deletions Source/CParse/parser.y
Expand Up @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions Source/CParse/templ.c
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion Source/Modules/lang.cxx
Expand Up @@ -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;
}

Expand Down
4 changes: 4 additions & 0 deletions Source/Swig/naming.c
Expand Up @@ -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;
}
Expand Down

0 comments on commit 428b617

Please sign in to comment.