Thread overview
Function overloading
Jul 31, 2007
Vladimir
Jul 31, 2007
Regan Heath
Jul 31, 2007
Frits van Bommel
Jul 31, 2007
Regan Heath
Jul 31, 2007
Vladimir
Jul 31, 2007
Regan Heath
Jul 31, 2007
Vladimir
July 31, 2007
Hi there,

Why not to allow template function overloading? Like

void test (T) (T)
{
}

void test (int)
{
}


July 31, 2007
Vladimir wrote:
> Hi there,
> 
> Why not to allow template function overloading? Like
> 
> void test (T) (T)
> {
> }
> 
> void test (int)
> {
> }

It is:

import std.stdio;

void test(T)(T p)
{
	writefln("misc called");
}

void test(T : int)(T p)
{
	writefln("int called");
}

void main()
{
	int i;
	long l;
	float f;
	test!(int)(i);
	test!(long)(l);
	test!(float)(f);
}

Output:
int called
misc called
misc called


However, without the !(int) etc it fails to compile with: "template tplspec.test(T : int) specialization not allowed for deduced parameter T"

Given the documentation:

<copy>
Argument Deduction

The types of template parameters are deduced for a particular template instantiation by comparing the template argument with the corresponding template parameter.

For each template parameter, the following rules are applied in order until a type is deduced for each parameter:

   1. If there is no type specialization for the parameter, the type of the parameter is set to the template argument.
   2. If the type specialization is dependent on a type parameter, the type of that parameter is set to be the corresponding part of the type argument.
   3. If after all the type arguments are examined there are any type parameters left with no type assigned, they are assigned types corresponding to the template argument in the same position in the TemplateArgumentList.
   4. If applying the above rules does not result in exactly one type for each template parameter, then it is an error.
</copy>

It seems that for the first call "foo(i)" step #1 does nothing, step #2 sets the type to 'int', step #3 does nothing and step #4 finds that there _is_ exactly 1 type per argument so why the error?

Is this a bug?

Regan
July 31, 2007
Regan Heath wrote:
> import std.stdio;
> 
> void test(T)(T p)
> {
>     writefln("misc called");
> }
> 
> void test(T : int)(T p)
> {
>     writefln("int called");
> }
> 
> void main()
> {
>     int i;
>     long l;
>     float f;
>     test!(int)(i);
>     test!(long)(l);
>     test!(float)(f);
> }
> 
> Output:
> int called
> misc called
> misc called
> 
> 
> However, without the !(int) etc it fails to compile with: "template tplspec.test(T : int) specialization not allowed for deduced parameter T"

Workaround:
---
import std.stdio;

void testImpl(T)(T p)
{
    writefln("misc called");
}

void testImpl(T : int)(T p)
{
    writefln("int called");
}

void test(T)(T p) { return testImpl!(T)(p); }

void main()
{
    int i;
    long l;
    float f;
    test(i);
    test(l);
    test(f);
}
---
(test uses the deduced parameter to explicitly instantiate testImpl)
July 31, 2007
Frits van Bommel wrote:
> Regan Heath wrote:
>> import std.stdio;
>>
>> void test(T)(T p)
>> {
>>     writefln("misc called");
>> }
>>
>> void test(T : int)(T p)
>> {
>>     writefln("int called");
>> }
>>
>> void main()
>> {
>>     int i;
>>     long l;
>>     float f;
>>     test!(int)(i);
>>     test!(long)(l);
>>     test!(float)(f);
>> }
>>
>> Output:
>> int called
>> misc called
>> misc called
>>
>>
>> However, without the !(int) etc it fails to compile with: "template tplspec.test(T : int) specialization not allowed for deduced parameter T"
> 
> Workaround:
> ---
> import std.stdio;
> 
> void testImpl(T)(T p)
> {
>     writefln("misc called");
> }
> 
> void testImpl(T : int)(T p)
> {
>     writefln("int called");
> }
> 
> void test(T)(T p) { return testImpl!(T)(p); }
> 
> void main()
> {
>     int i;
>     long l;
>     float f;
>     test(i);
>     test(l);
>     test(f);
> }
> ---
> (test uses the deduced parameter to explicitly instantiate testImpl)

Nice!

Regan
July 31, 2007
Ok, it works for that case.

I'm playing with string template parameters. It's a kind of miracle for me, a c++ programmer. I made a precompiled wildcard match and would like static version to overload as follows:

bool wildcardMatch (T : char[]) (T wild, T str) // general case
{
    return false;
}

bool wildcardMatch (char[] W) (T str) // static case
{
    return false;
}

void main()
{
    wildcardMatch ("*", "123");
    wildcardMatch !("*") ("123");
}

Is that posible?
July 31, 2007
Vladimir wrote:
> Ok, it works for that case.
> 
> I'm playing with string template parameters. It's a kind of miracle
> for me, a c++ programmer. I made a precompiled wildcard match and
> would like static version to overload as follows:
> 
> bool wildcardMatch (T : char[]) (T wild, T str) // general case { return false; }
> 
> bool wildcardMatch (char[] W) (T str) // static case { return false; }
> 
> void main() { wildcardMatch ("*", "123"); wildcardMatch !("*")
> ("123"); }
> 
> Is that posible?

At first I thought, maybe "static if" would be better than a template specialization, i.e.

bool wildcardMatch (T : char[]) (T wild, T str)
{
    static if (wild == "*") return true;
    return false;
}

but of course the value of 'wild' is not known at compile time... or is it?

If it's a constant as in your example 'main' above then it is and the compiler could theoretically evaluate it.

But then I thought, if you know it's "*" why make a call to wildcardMatch at all? (after all it will always match)

I came up with:

bool someOtherTpl (T : char[]) (T wild, T str, bool something)
{
  if (something)
    wildcardMatch(wild, str);
}

someOtherTpl("*", ...

which is a case where you have to code the call to wildcardMatch because you don't know wild was "*" at that point.


The closest I managed to get with template specialization is:

import std.stdio;

bool wildcardMatch (T) (T wild, T str) // general case
{
    writefln("general case");
    return false;
}

bool wildcardMatch (W:invariant(char[1]), S) (W wild, S str) // "*" case
{
    writefln("* case");
    return false;
}

void main()
{
    wildcardMatch!(char[])("*asd*".dup, "123".dup);
    wildcardMatch!(invariant(char[1]),char[])("*", "123".dup);
    wildcardMatch!(invariant(char[1]),char[])("a", "123".dup);
}

Which incorrectly calls the * case for the wild string "a" because of course it is specializing using a static array of 1 character.

Regan
July 31, 2007
Regan Heath Wrote:

> Vladimir wrote:
> > Ok, it works for that case.
> > 
> > I'm playing with string template parameters. It's a kind of miracle for me, a c++ programmer. I made a precompiled wildcard match and would like static version to overload as follows:
> > 
> > bool wildcardMatch (T : char[]) (T wild, T str) // general case { return false; }
> > 
> > bool wildcardMatch (char[] W) (T str) // static case { return false; }
> > 
> > void main() { wildcardMatch ("*", "123"); wildcardMatch !("*")
> > ("123"); }
> > 
> > Is that posible?
> 
> At first I thought, maybe "static if" would be better than a template specialization, i.e.
> 
> bool wildcardMatch (T : char[]) (T wild, T str)
> {
>      static if (wild == "*") return true;
>      return false;
> }
> 
> but of course the value of 'wild' is not known at compile time... or is it?
> 
> If it's a constant as in your example 'main' above then it is and the compiler could theoretically evaluate it.
> 
> But then I thought, if you know it's "*" why make a call to wildcardMatch at all? (after all it will always match)
> 
> I came up with:
> 
> bool someOtherTpl (T : char[]) (T wild, T str, bool something)
> {
>    if (something)
>      wildcardMatch(wild, str);
> }
> 
> someOtherTpl("*", ...
> 
> which is a case where you have to code the call to wildcardMatch because you don't know wild was "*" at that point.
> 
> 
> The closest I managed to get with template specialization is:
> 
> import std.stdio;
> 
> bool wildcardMatch (T) (T wild, T str) // general case
> {
>      writefln("general case");
>      return false;
> }
> 
> bool wildcardMatch (W:invariant(char[1]), S) (W wild, S str) // "*" case
> {
>      writefln("* case");
>      return false;
> }
> 
> void main()
> {
>      wildcardMatch!(char[])("*asd*".dup, "123".dup);
>      wildcardMatch!(invariant(char[1]),char[])("*", "123".dup);
>      wildcardMatch!(invariant(char[1]),char[])("a", "123".dup);
> }
> 
> Which incorrectly calls the * case for the wild string "a" because of course it is specializing using a static array of 1 character.
> 
> Regan


import std.string;

bool wildcardMatch (char[] E) (char[] aString)
{
	return Expression !(E).Result.match (aString);
}


private
{
	const int TypeEmpty = 0;
	const int TypeAsterisk = 1;
	const int TypeQuestion = 2;
	const int TypeChar = 3;

	//
	int expressionType (char[] aExpression)
	{
		if (aExpression.length == 0)
			return TypeEmpty;
		if (aExpression [0] == '*')
			return TypeAsterisk;
		if (aExpression [0] == '?')
			return TypeQuestion;
		return TypeChar;
	}

	//
	char[] headExpression (char[] aExpression)
	{
		return aExpression [0 .. length - tailExpression (aExpression).length];
	}

	//
	char[] tailExpression (char[] aExpression)
	{
		int type = TypeEmpty;

		for (int i = 0; i < aExpression.length; ++i)
		{
			if (i == 0)
			{
				switch (aExpression [i])
				{
				case '*':
					type = TypeAsterisk;
					break;

				case '?':
					type = TypeQuestion;
					break;

				default:
					type = TypeChar;
				}
			}
			else if (type == TypeAsterisk)
			{
				if (aExpression [i] != '*')
					return aExpression [i .. length];
			}
			else if (type == TypeQuestion)
			{
				if (aExpression [i] != '?')
					return aExpression [i .. length];
			}
			else
			{
				if (aExpression [i] == '*' || aExpression [i] == '?')
					return aExpression [i .. length];
			}
		}

		return "";
	}

	template Expression (char[] E)
	{
		 static if (expressionType (E) == TypeAsterisk)
			alias AsteriksMatch !(E) Result;
		 else static if (expressionType (E) == TypeQuestion)
			alias QuestionMatch !(E) Result;
		 else static if (expressionType (E) == TypeChar)
			alias CharMatch !(E) Result;
		 else
			alias EmptyMatch !(E) Result;
	}

	template AsteriksMatch (char[] E)
	{
		static assert (expressionType (E) == TypeAsterisk);

		bool match (char[] aString)
		{
			const char[] Tail = tailExpression (E);
			static if (Tail.length == 0)
				return true;

			for (int i = aString.length; i >= 0; --i)
			{
				if (Expression !(Tail).Result.match (aString [i .. length]))
					return true;
			}

			return false;
		}
	}

	template QuestionMatch (char[] E)
	{
		static assert (expressionType (E) == TypeQuestion);

		bool match (char[] aString)
		{
			const char[] Head = headExpression (E);
			if (aString.length == Head.length)
				return true;
			if (aString.length < Head.length)
				return false;

			const char[] Tail = tailExpression (E);
			return Expression !(Tail).Result.match (aString [Head.length .. length]);
		}
	}

	template CharMatch (char[] E)
	{
		static assert (expressionType (E) == TypeChar);

		bool match (char[] aString)
		{
			const char[] Head = headExpression (E);
			if (aString.length < Head.length)
				return false;
			if (icmp (Head, aString [0 .. Head.length]) != 0)
				return false;

			const char[] Tail = tailExpression (E);
			return Expression !(Tail).Result.match (aString [Head.length .. length]);
		}
	}

	template EmptyMatch (char[] E)
	{
		static assert (expressionType (E) == TypeEmpty);

		bool match (char[] aString)
		{
			return aString.length == 0;
		}
	}
}

It can be used with any expresion known at compile time:

char[] checkme = "123456";
wildcardMatch !("???5*") (checkme);
wildcardMatch !("1???5*") (checkme);
wildcardMatch !("?????????5*") (checkme);

I dont know how it could be useful, it's just a toy. Surprisingly, I can't freely overload functions like in C++