Thread overview
Strange template problem
Jul 08, 2006
Kirk McDonald
Jul 08, 2006
Tom S
Jul 08, 2006
Kirk McDonald
Jul 08, 2006
Kirk McDonald
Jul 08, 2006
Tom S
Jul 09, 2006
Bruno Medeiros
July 08, 2006
I'm having some trouble with a very odd template problem. Take the following code:

[test.d]
import std.stdio;
import ftype; // Daniel Keep's ftype module

// A function with default arguments
void foo(int i=20, char[] s="Monkey") {
    writefln("foo: %s %s", i, s);
}

// A function with the same arguments and no defaults
void bar(int i, char[] s) {
    writefln("bar: %s %s", i, s);
}

// A function that takes a function pointer and the minimum
// number of arguments the function can be called with, and
// calls it.
void Baz(fn_t, uint MIN_ARGS)(fn_t fn) {
    static if (MIN_ARGS == 0)
        fn();
    else static if (MIN_ARGS == 1)
        fn(10);
    else static if (MIN_ARGS == 2)
        fn(15, "Copper");
}

// A template function that takes a function alias and the
// minimum number of args it can be called with, and calls
// Baz
void Blah(alias Fn, uint MIN_ARGS = NumberOfArgs!(typeof(&Fn))) () {
    alias typeof(&Fn) fn_t;
    fn_t fn = &Fn;
    Baz!(fn_t, MIN_ARGS)(fn);
}

void main() {
    Blah!(bar);    // Line 28
    Blah!(foo, 0); // Line 29
}

$ build test
test.d(14): Error: expected 2 arguments, not 0

test.d(24): template instance test22.Baz!(void(*)(int i, char[] s),0u) error instantiating
test.d(29): template instance test22.Blah!(foo,0) error instantiating

Curiously, this works if I comment out line 28:

$ build test
gcc test.o ftype.o -o test -m32 -lphobos -lpthread -lm
$ ./test
foo: 20 Monkey

It also works if I comment out line 29 instead:

$ build test
gcc test.o ftype.o -o test -m32 -lphobos -lpthread -lm
$ ./test
bar: 15 Copper

But if both lines are there together, it fails.

-- 
Kirk McDonald
Pyd: Wrapping Python with D
http://dsource.org/projects/pyd/wiki
July 08, 2006
Kirk McDonald wrote:
> I'm having some trouble with a very odd template problem. Take the following code:
> 
> [test.d]
> import std.stdio;
> import ftype; // Daniel Keep's ftype module
> 
> // A function with default arguments
> void foo(int i=20, char[] s="Monkey") {
>     writefln("foo: %s %s", i, s);
> }
> 
> // A function with the same arguments and no defaults
> void bar(int i, char[] s) {
>     writefln("bar: %s %s", i, s);
> }
> 
> // A function that takes a function pointer and the minimum
> // number of arguments the function can be called with, and
> // calls it.
> void Baz(fn_t, uint MIN_ARGS)(fn_t fn) {
>     static if (MIN_ARGS == 0)
>         fn();

MIN_ARGS is 0 for 'foo', because thats what the NumberOfArgs template returns, using the function *alias*. the *type* doesnt tell the compiler that it can be called with no args. thats just a func type that takes two args. thus the error.


>     else static if (MIN_ARGS == 1)
>         fn(10);
>     else static if (MIN_ARGS == 2)
>         fn(15, "Copper");
> }
> 
> // A template function that takes a function alias and the
> // minimum number of args it can be called with, and calls
> // Baz
> void Blah(alias Fn, uint MIN_ARGS = NumberOfArgs!(typeof(&Fn))) () {
>     alias typeof(&Fn) fn_t;
>     fn_t fn = &Fn;
>     Baz!(fn_t, MIN_ARGS)(fn);
> }
> 
> void main() {
>     Blah!(bar);    // Line 28
>     Blah!(foo, 0); // Line 29
> }
> 
> $ build test
> test.d(14): Error: expected 2 arguments, not 0
> 
> test.d(24): template instance test22.Baz!(void(*)(int i, char[] s),0u) error instantiating
> test.d(29): template instance test22.Blah!(foo,0) error instantiating
> 
> Curiously, this works if I comment out line 28:
> 
> $ build test
> gcc test.o ftype.o -o test -m32 -lphobos -lpthread -lm
> $ ./test
> foo: 20 Monkey
> 
> It also works if I comment out line 29 instead:
> 
> $ build test
> gcc test.o ftype.o -o test -m32 -lphobos -lpthread -lm
> $ ./test
> bar: 15 Copper
> 
> But if both lines are there together, it fails.
> 


-- 
Tomasz Stachowiak  /+ a.k.a. h3r3tic +/
July 08, 2006
Tom S wrote:
> Kirk McDonald wrote:
> 
>> I'm having some trouble with a very odd template problem. Take the following code:
>>
>> [test.d]
>> import std.stdio;
>> import ftype; // Daniel Keep's ftype module
>>
>> // A function with default arguments
>> void foo(int i=20, char[] s="Monkey") {
>>     writefln("foo: %s %s", i, s);
>> }
>>
>> // A function with the same arguments and no defaults
>> void bar(int i, char[] s) {
>>     writefln("bar: %s %s", i, s);
>> }
>>
>> // A function that takes a function pointer and the minimum
>> // number of arguments the function can be called with, and
>> // calls it.
>> void Baz(fn_t, uint MIN_ARGS)(fn_t fn) {
>>     static if (MIN_ARGS == 0)
>>         fn();
> 
> 
> MIN_ARGS is 0 for 'foo', because thats what the NumberOfArgs template returns, using the function *alias*. the *type* doesnt tell the compiler that it can be called with no args. thats just a func type that takes two args. thus the error.

Then why does it work when I comment out line 28?

> 
> 
>>     else static if (MIN_ARGS == 1)
>>         fn(10);
>>     else static if (MIN_ARGS == 2)
>>         fn(15, "Copper");
>> }
>>
>> // A template function that takes a function alias and the
>> // minimum number of args it can be called with, and calls
>> // Baz
>> void Blah(alias Fn, uint MIN_ARGS = NumberOfArgs!(typeof(&Fn))) () {
>>     alias typeof(&Fn) fn_t;
>>     fn_t fn = &Fn;
>>     Baz!(fn_t, MIN_ARGS)(fn);
>> }
>>
>> void main() {
>>     Blah!(bar);    // Line 28
>>     Blah!(foo, 0); // Line 29
>> }
>>
>> $ build test
>> test.d(14): Error: expected 2 arguments, not 0
>>
>> test.d(24): template instance test22.Baz!(void(*)(int i, char[] s),0u) error instantiating
>> test.d(29): template instance test22.Blah!(foo,0) error instantiating
>>
>> Curiously, this works if I comment out line 28:
>>
>> $ build test
>> gcc test.o ftype.o -o test -m32 -lphobos -lpthread -lm
>> $ ./test
>> foo: 20 Monkey
>>
>> It also works if I comment out line 29 instead:
>>
>> $ build test
>> gcc test.o ftype.o -o test -m32 -lphobos -lpthread -lm
>> $ ./test
>> bar: 15 Copper
>>
>> But if both lines are there together, it fails.
>>
> 
> 


-- 
Kirk McDonald
Pyd: Wrapping Python with D
http://dsource.org/projects/pyd/wiki
July 08, 2006
Kirk McDonald wrote:
> Tom S wrote:
> 
>> Kirk McDonald wrote:
>>
>>> I'm having some trouble with a very odd template problem. Take the following code:
>>>
>>> [test.d]
>>> import std.stdio;
>>> import ftype; // Daniel Keep's ftype module
>>>
>>> // A function with default arguments
>>> void foo(int i=20, char[] s="Monkey") {
>>>     writefln("foo: %s %s", i, s);
>>> }
>>>
>>> // A function with the same arguments and no defaults
>>> void bar(int i, char[] s) {
>>>     writefln("bar: %s %s", i, s);
>>> }
>>>
>>> // A function that takes a function pointer and the minimum
>>> // number of arguments the function can be called with, and
>>> // calls it.
>>> void Baz(fn_t, uint MIN_ARGS)(fn_t fn) {
>>>     static if (MIN_ARGS == 0)
>>>         fn();
>>
>>
>>
>> MIN_ARGS is 0 for 'foo', because thats what the NumberOfArgs template returns, using the function *alias*. the *type* doesnt tell the compiler that it can be called with no args. thats just a func type that takes two args. thus the error.
> 
> 
> Then why does it work when I comment out line 28?

Actually, I think I'm going to be really pedantic and correct you:

MIN_ARGS is 0 for 'foo' because I explicitly told it to be (on line 29). NumberOfArgs would evaluate to 2, because it does indeed operate on the type of the function pointer, which doesn't know a thing about default arguments. If lines 28 and 29 instead read:

    Blah!(bar);
    Blah!(foo);

Then it works just fine, calling both functions with two args:

$ ./test
bar: 15 Copper
foo: 15 Copper

But it also works when I comment out line 28 and say "Blah!(foo, 0)", which is slightly baffling. Pyd has hitherto relied on this behavior to implement its default argument support, and I only recently had this issue bite me in the ass.

The following does not work:

void function(int, char[]) fn = &foo;
fn();

And yet, these templates almost seem to trick it into working. Is this a compiler check that is preventing possible runtime behavior? (A check that my template wackiness has somehow gotten around?)

> 
>>
>>
>>>     else static if (MIN_ARGS == 1)
>>>         fn(10);
>>>     else static if (MIN_ARGS == 2)
>>>         fn(15, "Copper");
>>> }
>>>
>>> // A template function that takes a function alias and the
>>> // minimum number of args it can be called with, and calls
>>> // Baz
>>> void Blah(alias Fn, uint MIN_ARGS = NumberOfArgs!(typeof(&Fn))) () {
>>>     alias typeof(&Fn) fn_t;
>>>     fn_t fn = &Fn;
>>>     Baz!(fn_t, MIN_ARGS)(fn);
>>> }
>>>
>>> void main() {
>>>     Blah!(bar);    // Line 28
>>>     Blah!(foo, 0); // Line 29
>>> }
>>>
>>> $ build test
>>> test.d(14): Error: expected 2 arguments, not 0
>>>
>>> test.d(24): template instance test22.Baz!(void(*)(int i, char[] s),0u) error instantiating
>>> test.d(29): template instance test22.Blah!(foo,0) error instantiating
>>>
>>> Curiously, this works if I comment out line 28:
>>>
>>> $ build test
>>> gcc test.o ftype.o -o test -m32 -lphobos -lpthread -lm
>>> $ ./test
>>> foo: 20 Monkey
>>>
>>> It also works if I comment out line 29 instead:
>>>
>>> $ build test
>>> gcc test.o ftype.o -o test -m32 -lphobos -lpthread -lm
>>> $ ./test
>>> bar: 15 Copper
>>>
>>> But if both lines are there together, it fails.
>>>
>>
>>
> 
> 


-- 
Kirk McDonald
Pyd: Wrapping Python with D
http://dsource.org/projects/pyd/wiki
July 08, 2006
Kirk McDonald wrote:
> Kirk McDonald wrote:
>> Tom S wrote:
>>
>>> Kirk McDonald wrote:
>>>
>>>> I'm having some trouble with a very odd template problem. Take the following code:
>>>>
>>>> [test.d]
>>>> import std.stdio;
>>>> import ftype; // Daniel Keep's ftype module
>>>>
>>>> // A function with default arguments
>>>> void foo(int i=20, char[] s="Monkey") {
>>>>     writefln("foo: %s %s", i, s);
>>>> }
>>>>
>>>> // A function with the same arguments and no defaults
>>>> void bar(int i, char[] s) {
>>>>     writefln("bar: %s %s", i, s);
>>>> }
>>>>
>>>> // A function that takes a function pointer and the minimum
>>>> // number of arguments the function can be called with, and
>>>> // calls it.
>>>> void Baz(fn_t, uint MIN_ARGS)(fn_t fn) {
>>>>     static if (MIN_ARGS == 0)
>>>>         fn();
>>>
>>>
>>>
>>> MIN_ARGS is 0 for 'foo', because thats what the NumberOfArgs template returns, using the function *alias*. the *type* doesnt tell the compiler that it can be called with no args. thats just a func type that takes two args. thus the error.
>>
>>
>> Then why does it work when I comment out line 28?
> 
> Actually, I think I'm going to be really pedantic and correct you:

Thanks for correcting me and sorry for being dumb and answering too quickly. ( I was just about to receive a pizza so I was in a hurry, he.. he... )

Looks like a very bizarre bug to me... the simplified case behaves exactly the same:


void foo(int i=20) {
}

void bar(int i) {
}

void main() {
	{
	    alias typeof(&bar) fn_t;    // comment it out and the error goes away
	}

	{
	    alias typeof(&foo) fn_t;
	    fn_t fn = &foo;
	    fn();
	}
}


That's funny... I'm can't stop thinking that I've reported a similar bug more than a year ago :o


-- 
Tomasz Stachowiak  /+ a.k.a. h3r3tic +/
July 09, 2006
Kirk McDonald wrote:
> Kirk McDonald wrote:
>> Tom S wrote:
>>
>>> Kirk McDonald wrote:
>>>
>>>> I'm having some trouble with a very odd template problem. Take the following code:
>>>>
>>>> [test.d]
>>>> import std.stdio;
>>>> import ftype; // Daniel Keep's ftype module
>>>>
>>>> // A function with default arguments
>>>> void foo(int i=20, char[] s="Monkey") {
>>>>     writefln("foo: %s %s", i, s);
>>>> }
>>>>
>>>> // A function with the same arguments and no defaults
>>>> void bar(int i, char[] s) {
>>>>     writefln("bar: %s %s", i, s);
>>>> }
>>>>
>>>> // A function that takes a function pointer and the minimum
>>>> // number of arguments the function can be called with, and
>>>> // calls it.
>>>> void Baz(fn_t, uint MIN_ARGS)(fn_t fn) {
>>>>     static if (MIN_ARGS == 0)
>>>>         fn();
>>>
>>>
>>>
>>> MIN_ARGS is 0 for 'foo', because thats what the NumberOfArgs template returns, using the function *alias*. the *type* doesnt tell the compiler that it can be called with no args. thats just a func type that takes two args. thus the error.
>>
>>
>> Then why does it work when I comment out line 28?
> 
> Actually, I think I'm going to be really pedantic and correct you:
> 
> MIN_ARGS is 0 for 'foo' because I explicitly told it to be (on line 29). NumberOfArgs would evaluate to 2, because it does indeed operate on the type of the function pointer, which doesn't know a thing about default arguments. If lines 28 and 29 instead read:
> 

For what I see, a function pointer *does* know about default arguments. That doesn't change your point though.

-- 
Bruno Medeiros - CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D