www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Strange template problem

reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
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
Jul 08 2006
parent reply Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
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 +/
Jul 08 2006
parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
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
Jul 08 2006
parent reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
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
Jul 08 2006
next sibling parent Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
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 +/
Jul 08 2006
prev sibling parent Bruno Medeiros <brunodomedeirosATgmail SPAM.com> writes:
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
Jul 09 2006