www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - how to properly overload function templates?

reply Trass3r <un known.com> writes:
I stumbled across this while playing with operator overloading. Since they  
are now function templates, this becomes an issue.

struct Vector2(T)
{
	T x;
	T y;

	/// element-wise operations, +, -,
	Vector2 opBinary(string op)(ref Vector2 v)
	{
		mixin("return Vector2!(T)( cast(T)(x " ~ op ~ " v.x), cast(T)(y " ~ op ~  
" v.y) );");
	}

	/// operation with scalar
	Vector2 opBinary(string op)(int i)
	{
		mixin("return Vector2!(T) ( cast(T)(x " ~ op ~ " i), cast(T)(y " ~ op ~  
" i) );");
	}
}

This yields:
template instance opBinary!("+") matches more than one template declaration

Of course this can be circumvented by using
opBinary(string op, U:Vector2)(U v)
opBinary(string op, U:int)(U v)


But is this how it's supposed to be done? Couldn't the compiler detect  
that itself?
Mar 11 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Trass3r:
 Of course this can be circumvented by using
 opBinary(string op, U:Vector2)(U v)
 opBinary(string op, U:int)(U v)
 
 But is this how it's supposed to be done? Couldn't the compiler detect  
 that itself?

The compiler can probably do that by itself, but to do that I think templates need to change a little how they work. That code doesn't look too much bad, I think it's acceptable. You can also use template constraints, but the code gets a little worse: opBinary(string op, T)(T v) if (is(T == Vector2) { opBinary(string op, T)(T v) if (is(T == int) { Or you can squash it in a single template, but I think it's worse: opBinary(string op, T)(T v) { static if (is(T == Vector2) { ... } else static if (is(T == int) { ... } else assert(0, "..."); } Bye, bearophile
Mar 11 2010
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 11 Mar 2010 19:15:37 -0500, Trass3r <un known.com> wrote:

 I stumbled across this while playing with operator overloading. Since  
 they are now function templates, this becomes an issue.

 struct Vector2(T)
 {
 	T x;
 	T y;

 	/// element-wise operations, +, -,
 	Vector2 opBinary(string op)(ref Vector2 v)
 	{
 		mixin("return Vector2!(T)( cast(T)(x " ~ op ~ " v.x), cast(T)(y " ~ op  
 ~ " v.y) );");
 	}

 	/// operation with scalar
 	Vector2 opBinary(string op)(int i)
 	{
 		mixin("return Vector2!(T) ( cast(T)(x " ~ op ~ " i), cast(T)(y " ~ op  
 ~ " i) );");
 	}
 }

 This yields:
 template instance opBinary!("+") matches more than one template  
 declaration

 Of course this can be circumvented by using
 opBinary(string op, U:Vector2)(U v)
 opBinary(string op, U:int)(U v)


 But is this how it's supposed to be done? Couldn't the compiler detect  
 that itself?

The issue is with two things. First, there are two instantiations of the template. You have to realize that a template is somewhat of a namespace, and they do not append to eachother. Think about it this way, a template function is really a shortcut for this: template fn(T) { fn(args) {...} } So what you have done is: template fn(T) { fn(args1) {...} } template fn(T) { fn(args2) {...} } The compiler doesn't combine the two templates together, so it doesn't know which namespace you are talking about. I tried something like this: template fn(T) { fn(args1) {...} fn(args2) {...} } but this doesn't work, because the compiler refuses to call the function via fn!(T)(args1). I think the only option you are faced with at the moment is to include the argument type in the template specification, thereby separating the namespace. You should file a bug on this. I think it's not a good result of the new operator overloading regime. However, I think it can be fixed, by either handling template function overloads, or having the compiler rewrite x + b as x.opBinary!("+").opBinary(b); since we aren't even using IFTI here. Then my proposed overloaded template functions will work. -Steve
Mar 12 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
Steven Schveighoffer:

However, I think it can be fixed, by either handling template function
overloads,

That's the more general solution. Do you know how much hard is to implement that? Do you see any downsides or possible bad side effects? Bye, bearophile
Mar 12 2010
parent reply Don <nospam nospam.com> writes:
bearophile wrote:
 Steven Schveighoffer:
 
 However, I think it can be fixed, by either handling template function
overloads,

That's the more general solution. Do you know how much hard is to implement that? Do you see any downsides or possible bad side effects? Bye, bearophile

Walter tried to do it, about a year ago, but gave up because it turned out to be too difficult.
Mar 15 2010
parent bearophile <bearophileHUGS lycos.com> writes:
Don:
 Walter tried to do it, about a year ago, but gave up because it turned 
 out to be too difficult.

I think the refused C++0x feature of "Extern template" has some semantic relation to this, and it too much hard to implement. Better to not make the compiler overly-complex. Bye, bearophile
Mar 15 2010
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 12 Mar 2010 08:52:29 -0500, bearophile <bearophileHUGS lycos.com>  
wrote:

 Steven Schveighoffer:

 However, I think it can be fixed, by either handling template function  
 overloads,

That's the more general solution. Do you know how much hard is to implement that? Do you see any downsides or possible bad side effects?

I imagine it's not trivial. You would be combining multiple templates into one. At the very least, it should be restricted to a single module. I would restrict it also to templates that have only eponymous members (members of the same name). You would also have to allow a template with multiple eponymous functions to resolve to the overload set of those functions. The compiler currently does not allow that (or my workaround would have compiled). -Steve
Mar 12 2010