www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Proposal: static template(fail)

reply "Janice Caron" <caron800 googlemail.com> writes:
I would like to propose a new statement:

    static template(fail);

which causes a compile-time substitution failure. And remember: SFINAE
- Substitution Failure Is Not An Error

Let me give you an example:

    struct S(T)
    {
        static if(isFloatingPoint!(T))
        {
            /*...*/
        }
        else static if(passesSomeVeryComplicatedTest!(T))
        {
            /*...*/
        }
        else
        {
            static template(fail); // SFINAE
        }
    }

This has advantages over old-style specialisation.
Dec 13 2007
next sibling parent reply "Aziz K." <aziz.kerim gmail.com> writes:
Why not use a static assert: static assert(0, "Substitution Failure");
Dec 13 2007
parent reply Jason House <jason.james.house gmail.com> writes:
Janice Caron Wrote:

 On 12/13/07, Aziz K. <aziz.kerim gmail.com> wrote:
 Why not use a static assert: static assert(0, "Substitution Failure");

A static assert is a compile error. Different thing. What you want is for the template not to be instanted, not for it to be instantiated with a compile error. Put it another way. Suppose you write S(int) s; Which would you prefer - an error message saying that s couldn't be instantiated (giving the filename and line number of the above line), or an error message saying that a static assert in the middle of some library file had been hit?

I'd prefer a static assert. As with all errors, I'd want a backtrace. Functions currently have in/body/out sections. I wouldn't mind seeing something like an "in" function for templated objects. To me, that's much more generic. In a world without backtraces for errors, it'd also allow a hint to the compiler that it should point the finger at the code that caused the instantiation.
Dec 13 2007
next sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Janice Caron wrote:
 On 12/13/07, Jason House <jason.james.house gmail.com> wrote:
 Janice Caron Wrote:

 On 12/13/07, Aziz K. <aziz.kerim gmail.com> wrote:
 Why not use a static assert: static assert(0, "Substitution Failure");

for the template not to be instanted, not for it to be instantiated with a compile error. Put it another way. Suppose you write S(int) s; Which would you prefer - an error message saying that s couldn't be instantiated (giving the filename and line number of the above line), or an error message saying that a static assert in the middle of some library file had been hit?

As with all errors, I'd want a backtrace.

You're missing the point, which is IT'S NOT AN ERROR. It's a substitution failure - and substitution failure IS NOT AN ERROR. That's a basic principle of template programming.

I don't understand how it's not an error if you can't instantiate the template.
Dec 13 2007
next sibling parent Gregor Richards <Richards codu.org> writes:
Ary Borenszweig wrote:
 Janice Caron wrote:
 On 12/13/07, Jason House <jason.james.house gmail.com> wrote:
 Janice Caron Wrote:

 On 12/13/07, Aziz K. <aziz.kerim gmail.com> wrote:
 Why not use a static assert: static assert(0, "Substitution Failure");

for the template not to be instanted, not for it to be instantiated with a compile error. Put it another way. Suppose you write S(int) s; Which would you prefer - an error message saying that s couldn't be instantiated (giving the filename and line number of the above line), or an error message saying that a static assert in the middle of some library file had been hit?

As with all errors, I'd want a backtrace.

You're missing the point, which is IT'S NOT AN ERROR. It's a substitution failure - and substitution failure IS NOT AN ERROR. That's a basic principle of template programming.

I don't understand how it's not an error if you can't instantiate the template.

Because it could potentially instantiate a different (overloaded) template. - Gregor Richards
Dec 13 2007
prev sibling parent "Rioshin an'Harthen" <rharth75 hotmail.com> writes:
"Ary Borenszweig" <ary esperanto.org.ar> kirjoitti viestissä 
news:fjrqqp$1f0e$1 digitalmars.com...
 Janice Caron wrote:
 On 12/13/07, Jason House <jason.james.house gmail.com> wrote:
 Janice Caron Wrote:

 On 12/13/07, Aziz K. <aziz.kerim gmail.com> wrote:
 Why not use a static assert: static assert(0, "Substitution Failure");

for the template not to be instanted, not for it to be instantiated with a compile error. Put it another way. Suppose you write S(int) s; Which would you prefer - an error message saying that s couldn't be instantiated (giving the filename and line number of the above line), or an error message saying that a static assert in the middle of some library file had been hit?

As with all errors, I'd want a backtrace.

You're missing the point, which is IT'S NOT AN ERROR. It's a substitution failure - and substitution failure IS NOT AN ERROR. That's a basic principle of template programming.

I don't understand how it's not an error if you can't instantiate the template.

It should *not* be an error of the template that a certain call can't instantiate it, since another (overloaded) template, as Gregor mentioned, can possibly be instantiated. That's basically what the current template with static if forces us to do: make it an error in the template, if we want to avoid template bloat. Instead, the error should be at the site of instatiation, if no template matches. When the template instatiation fails because we've found a matching template where specific overloaded specializations are combined into the single template, and where a specific condition doesn't exist for the template to be useful - eg. if it fails the isFloatingPoint!(T) and passesSomeVeryComplicatedTest!(T) example conditions from Janice's first post in this thread, then 1) with a static assert and no specializations we get an error, inside the code of the template; 2) with Janice's syntax, the compiler would note that this template didn't match, and proceed onward with the testing of other templates, finally giving an error at the location of instantiation.
Dec 13 2007
prev sibling parent reply Jason House <jason.james.house gmail.com> writes:
Janice Caron Wrote:
 You're missing the point, which is IT'S NOT AN ERROR. It's a
 substitution failure - and substitution failure IS NOT AN ERROR.
 That's a basic principle of template programming.

That's certainly true in C++, but I was not aware that was the way D template programming was intended to work. Static if seems like a much cleaner way to do things. If it is intended to have more than one templated definition that could match, I'd prefer to see D try to instantiate all candidates, and ensure that all but one instance has an error. If more than one matches successfully, the code wasn't coded well enough. If all throw an error, then the respective errors for each are printed and allows the user to see what's going wrong with each candidate.
Dec 13 2007
parent reply Jason House <jason.james.house gmail.com> writes:
Janice Caron wrote:

 On 12/13/07, Jason House <jason.james.house gmail.com> wrote:
 If more than one matches successfully, the code wasn't coded well enough.

But then you'd have to do template A(T:int) {/*code*/} template A(T:anything except an int){/*code*/}

You can do the following for something so simple template A(T){ static if (T:int){/*code*/} else {/*code*/} } I've always been under the impression that use of static if is the "D way". Maybe that assumption is wrong. If my assumption isn't wrong, then attempts at one or more substitutions will be a rare exception rather than the rule. For rarer uses of code, if there's a chance the programmer (or more likely, a later code maintainer) can get confused, then I think it's better to err on the side of rigorous compile-time errors.
Dec 13 2007
next sibling parent Jason House <jason.james.house gmail.com> writes:
Janice Caron Wrote:

 On 12/14/07, Jason House <jason.james.house gmail.com> wrote:
 I've always been under the impression that use of static if is the "D way".

It's a choice. It happens to be my /preferred/ choice, but the multiple-templates way still needs to be supported.

After reflecting, I agree. What I'd been talking about assumes a mostly orthogonal split of template parameters rather than a hierarchy of approximate matches. Here's something more along the lines of what I was thinking about: template(T,U) { void foo(); } template(T : int,U){ void foo(); } template(T,U : int){ void foo(); } foo!(int,int)(); If both the 2nd and 3rd template works, which should the compiler choose? Maybe The 2nd template should assert U can't be int and the 3rd template should assert T can't be int. Even if that's done, is it an error or should the default be used? It very well could be the programmer never intended both parameters to be integers. Using the generic default may be a logic error. In this type of situation, I think it may help to force the programmer to be more explicit... such that if they don't catch this case properly, instantiating with both ints should be a compile-time error.
 Even if you /only/ use the static if style, and there are no
 alternative overloads, at the end of your chain of static ifs, you
 still want to be able so say static template(fail) (or whatever syntax
 is deemed right), rather than static assert, because if the template
 can't be instantiated at all, yes there's an error /somewhere/ -- but
 it's not in the template code; it's in the calling code - and that's
 the only place an error should be reported.

I have to disagree here. That's like saying it's incorrect to use an assert in an "in" contract. The same logic could apply. The caller passed in incorrect data, so the error occurred in the caller, not the callee. I just can't buy that. The callee can provide context-specific errors (free text detailing the likely problem or the logical condition that failed). I don't view template instantiation any differently. If there's an error, I want to templated code's perspective of what the error is *and* a complete backtrace. Now, when I say "an error", I mean a real error... It may be true that SFINAE when alternatives exist, but when you exhaust alternatives, it's an error.
Dec 14 2007
prev sibling next sibling parent BCS <ao pathlink.com> writes:
Reply to Jason,

 Janice Caron wrote:
 
 On 12/13/07, Jason House <jason.james.house gmail.com> wrote:
 
 If more than one matches successfully, the code wasn't coded well
 enough.
 

template A(T:int) {/*code*/} template A(T:anything except an int){/*code*/}

template A(T){ static if (T:int){/*code*/} else {/*code*/} }

the problem with static if is that it turns into a work-or-die-hard solution. Once the static if is gotten to, there is no way to back out and say "I don't known how to handle this". The template(fail) would allow for complex static logic inside a template that can then be counted as a "this doesn't match, try other things" cases. This would come in handy in cases where you are are making a special cases handler template but want to be able to also have other templates be tried. Example (each case maintained independently) template Foo(T) { static if(/*logic 1 on T*/) /* code */ else template(fail); } template Foo(T) { static if(/*logic 2 on T*/) /* code */ else template(fail); } template Foo(T) { /* general case code */ }
Dec 14 2007
prev sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 12/14/07, BCS <ao pathlink.com> wrote:
 Example (each case maintained independently)

I don't know if this is reasonable, because you've got multiple different versions of Foo(T). But maybe...
 template Foo(T)
 {
   static if(/*logic 1 on T*/)
     /* code */
   else
     template(fail);
 }

 template Foo(T)
 {
   static if(/*logic 2 on T*/)
     /* code */
   else
     template(fail);
 }

 template Foo(T)
 {
   /* general case code */
 }

My feeling is, you could recode that as template Foo(T) { static if(/*logic 1 on T*/) /* code */ else static if(/*logic 2 on T*/) /* code */ else /* general case code */ } without needing either specialisation or template(fail). (But there are other cases where it /would/ be useful, obviously).
Dec 14 2007
next sibling parent reply BCS <ao pathlink.com> writes:
Reply to Janice,

 On 12/14/07, BCS <ao pathlink.com> wrote:
 
 Example (each case maintained independently)
 

different versions of Foo(T). But maybe...
 template Foo(T)
 {
 static if(/*logic 1 on T*/)
 /* code */
 else
 template(fail);
 }
 template Foo(T)
 {
 static if(/*logic 2 on T*/)
 /* code */
 else
 template(fail);
 }
 template Foo(T)
 {
 /* general case code */
 }

template Foo(T) { static if(/*logic 1 on T*/) /* code */ else static if(/*logic 2 on T*/) /* code */ else /* general case code */ } without needing either specialisation or template(fail). (But there are other cases where it /would/ be useful, obviously).

That only works if the maintainers of the two templates work together. Here is an example where they wouldn't be: ////// module A; void Func(T)(T t); // does some stuff with a T, Expects to find a Go(T) that works for T ////// module B; import A; void Go(T: B.Type)(T t); // library that works with B.Type and interacts with A.Func ////// module C; import A; void Go(T: C.Thing)(T t); // different library that works with C.Thing and interacts with A.Func ////// module D; import A; import B; import C; // use it all here Now that works as is (assuming no errors in my typing) but if you now want to have a module that defines a Go(T) that uses static if's to find if the T is something it can handle, you can only have one of those.
Dec 14 2007
parent reply Christopher Wright <dhasenan gmail.com> writes:
BCS wrote:
 That only works if the maintainers of the two templates work together. 
 Here is an example where they wouldn't be:
 
 //////
 module A;
 void Func(T)(T t);  // does some stuff with a T, Expects to find a Go(T) 
 that works for T
 
 //////
 module B;
 import A;
 void Go(T: B.Type)(T t); // library that works with B.Type and interacts 
 with A.Func
 
 //////
 module C;
 import A;
 void Go(T: C.Thing)(T t); // different library that works with C.Thing 
 and interacts with A.Func
 
 //////
 module D;
 import A;
 import B;
 import C;
 // use it all here
 
 
 Now that works as is (assuming no errors in my typing) but if you now 
 want to have a module that defines a Go(T) that uses static if's to find 
 if the T is something it can handle, you can only have one of those.

Static import for the win! You might need some glue to hold it all together, and you'd have to define it at the location of use: template Go(T) { static if (I can handle it) { struct Go {} } else static if (compiles, A.Go!(T)) { alias A.Go!(T) Go; } else ... } Okay, that's ugly, and it puts the burden closer to the user. But it works, and it's not a terribly common situation.
Dec 14 2007
parent BCS <ao pathlink.com> writes:
Reply to Christopher,

 BCS wrote:
 
 That only works if the maintainers of the two templates work
 together. Here is an example where they wouldn't be:
 
 //////
 module A;
 void Func(T)(T t);  // does some stuff with a T, Expects to find a
 Go(T)
 that works for T
 //////
 module B;
 import A;
 void Go(T: B.Type)(T t); // library that works with B.Type and
 interacts
 with A.Func
 //////
 module C;
 import A;
 void Go(T: C.Thing)(T t); // different library that works with
 C.Thing
 and interacts with A.Func
 //////
 module D;
 import A;
 import B;
 import C;
 // use it all here
 Now that works as is (assuming no errors in my typing) but if you now
 want to have a module that defines a Go(T) that uses static if's to
 find if the T is something it can handle, you can only have one of
 those.
 

You might need some glue to hold it all together, and you'd have to define it at the location of use: template Go(T) { static if (I can handle it) { struct Go {} } else static if (compiles, A.Go!(T)) { alias A.Go!(T) Go; } else ... } Okay, that's ugly, and it puts the burden closer to the user. But it works, and it's not a terribly common situation.

Garsh Darn It!!! while that doesn't even get close to doing what I wanted (I think) it does bring up the point that what I was proposing doesn't even work because the template function in A doesn't import anything with regards to B and C. The only way to make it work would be to mixin stuff from A into D and that just gets a bit nasty. <grumble/> OTOH it brings up an interesting question, what syntactic scope does a template get instanced in. The scope it's defined in, the scope it's used in or the scope the thing it's specialized on is defined in. Or is it come combination of the three? module A; struct B {} struct C { B b; } void Do(T)(T t){writef("hello world\n");} module B void Do(T)(T t){Do(t.b);} module C; import A; import B; C c; Do(c); what happens? B.Do can see into A b/c it can see A.C.b so can it see A.Do as well?
Dec 14 2007
prev sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 12/14/07, BCS <ao pathlink.com> wrote:
 That only works if the maintainers of the two templates work together. Here
 is an example where they wouldn't be:

Gotcha. Well, it seems doable, so long as there is zero chance of order-dependency creeping it. That would be my only worry. If order dependency does turn out to be an issue, then it might be wise to consider the alternative (and I believe, equivalent) suggestion: template A(T:if(someTest!(T))) { /*code*/ } which would eliminate the need for that sort of overloading, but still give just as much power to template programming.
Dec 14 2007
parent BCS <ao pathlink.com> writes:
Reply to Janice,

 On 12/14/07, BCS <ao pathlink.com> wrote:
 
 That only works if the maintainers of the two templates work
 together. Here is an example where they wouldn't be:
 

order-dependency creeping it. That would be my only worry. If order dependency does turn out to be an issue, then it might be wise to consider the alternative (and I believe, equivalent) suggestion: template A(T:if(someTest!(T))) { /*code*/ } which would eliminate the need for that sort of overloading, but still give just as much power to template programming.

I think that would be preferable in some ways because it would place the logic in a consistent place. OTOH it forces the logic into a "single expression" form. I guess you can always push the login into another template. In the end it's the functionality I want, as long as the syntax is usable it's all the same to me.
Dec 14 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/13/07, Aziz K. <aziz.kerim gmail.com> wrote:
 Why not use a static assert: static assert(0, "Substitution Failure");

A static assert is a compile error. Different thing. What you want is for the template not to be instanted, not for it to be instantiated with a compile error. Put it another way. Suppose you write S(int) s; Which would you prefer - an error message saying that s couldn't be instantiated (giving the filename and line number of the above line), or an error message saying that a static assert in the middle of some library file had been hit?
Dec 13 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/13/07, Jason House <jason.james.house gmail.com> wrote:
 Janice Caron Wrote:

 On 12/13/07, Aziz K. <aziz.kerim gmail.com> wrote:
 Why not use a static assert: static assert(0, "Substitution Failure");

A static assert is a compile error. Different thing. What you want is for the template not to be instanted, not for it to be instantiated with a compile error. Put it another way. Suppose you write S(int) s; Which would you prefer - an error message saying that s couldn't be instantiated (giving the filename and line number of the above line), or an error message saying that a static assert in the middle of some library file had been hit?

I'd prefer a static assert. As with all errors, I'd want a backtrace.

You're missing the point, which is IT'S NOT AN ERROR. It's a substitution failure - and substitution failure IS NOT AN ERROR. That's a basic principle of template programming. Look, right now you can do: template A(T:U) { /*code*/ } template A(T:V) { /*the same code*/ } template A(T:W) { /*the same code*/ } template A(T:X) { /*the same code*/ } template A(T:Y) { /*the same code*/ } but you can't simplify it as: template A(T:U|V|W|X|Y) What you /could/ do is: template A(T) { static if(is(T:U) | is(T:V) | is(T:W) | is(T:X) | is(T:y)) { /*code*/ } but you can't take the obvious next step of else substitution failure ...you have to make it an actual error. Template specialisation is a powerful tool in metaprogramming, but right now we have to use multiple specialisations in order to use it. We've got "static if" to move us away from that antiquated C++ model, but still the only way to generate a substitution failure is to go back to that model. Note that an alternative way to achieve much the same thing would be to extend the syntax of specialisation to include something like (T:if(expression)) where the specialisation is deemed to hold if the expression evaluates to true at compile time. I quite like that - but I like my first suggestion more. (Of course, they're not mutually exclusive).
 Functions currently have in/body/out sections.  I wouldn't mind seeing
something like an "in" function for templated objects.

Right - so like my second suggestion: template A(T:if(someTest!(T)))
 To me, that's much more generic.

I'd say they're equivalent. It's just a question of whether you like to read if/else style code, or multiple specialisation style code.
 In a world without backtraces for errors, it'd also allow a hint to the
compiler that it should point the finger at the code that caused the
instantiation.

Well, obviously we want backtraces for instantiation errors. But that's as well, not instead of! :-)
Dec 13 2007
prev sibling next sibling parent reply BCS <ao pathlink.com> writes:
Reply to Janice,

 I would like to propose a new statement:
 
 static template(fail);
 
 which causes a compile-time substitution failure. And remember: SFINAE
 - Substitution Failure Is Not An Error
 
 Let me give you an example:
 
 struct S(T)
 {
 static if(isFloatingPoint!(T))
 {
 /*...*/
 }
 else static if(passesSomeVeryComplicatedTest!(T))
 {
 /*...*/
 }
 else
 {
 static template(fail); // SFINAE
 }
 }
 This has advantages over old-style specialisation.
 

vote++ either for the "static template(fail)" or "T:if(exp)" form
Dec 13 2007
parent reply Gregor Richards <Richards codu.org> writes:
BCS wrote:
 Reply to Janice,
 
 I would like to propose a new statement:

 static template(fail);

 which causes a compile-time substitution failure. And remember: SFINAE
 - Substitution Failure Is Not An Error

 Let me give you an example:

 struct S(T)
 {
 static if(isFloatingPoint!(T))
 {
 /*...*/
 }
 else static if(passesSomeVeryComplicatedTest!(T))
 {
 /*...*/
 }
 else
 {
 static template(fail); // SFINAE
 }
 }
 This has advantages over old-style specialisation.

vote++ either for the "static template(fail)" or "T:if(exp)" form

static template(fail) seems a bit more general purpose so probably better, but the syntax makes me go "uhhh, what?" I think a different syntax would be good, but the idea is sound. - Gregor Richards
Dec 13 2007
next sibling parent Leandro Lucarella <llucax gmail.com> writes:
Janice Caron, el 13 de diciembre a las 19:47 me escribiste:
 On 12/13/07, Gregor Richards <Richards codu.org> wrote:
 I think a different
 syntax would be good, but the idea is sound.

Hey, we always argue about the syntax. It's a tradition! :-) Yeah, I don't care about the syntax. I though of "static break" first, but then I thought that might conflict with possible future features (static switch/case/break anyone?). "template(fail)" was the best I could come up with quickly, but probably not the best we can do between us.

template break? =) -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- "Lidiar" no es lo mismo que "holguear"; ya que "lidiar" es relativo a "lidia" y "holguear" es relativo a "olga". -- Ricardo Vaporeso
Dec 13 2007
prev sibling parent Dejan Lekic <dejan.lekic gmail.com> writes:
Yes, idea is good, but proposed syntax is bad.
Dec 13 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/13/07, Gregor Richards <Richards codu.org> wrote:
 I think a different
 syntax would be good, but the idea is sound.

Hey, we always argue about the syntax. It's a tradition! :-) Yeah, I don't care about the syntax. I though of "static break" first, but then I thought that might conflict with possible future features (static switch/case/break anyone?). "template(fail)" was the best I could come up with quickly, but probably not the best we can do between us.
Dec 13 2007
prev sibling next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Janice Caron wrote:
 I would like to propose a new statement:
 
     static template(fail);
 
 which causes a compile-time substitution failure. And remember: SFINAE
 - Substitution Failure Is Not An Error
 
 Let me give you an example:
 
     struct S(T)
     {
         static if(isFloatingPoint!(T))
         {
             /*...*/
         }
         else static if(passesSomeVeryComplicatedTest!(T))
         {
             /*...*/
         }
         else
         {
             static template(fail); // SFINAE
         }
     }
 
 This has advantages over old-style specialisation.

All you need is *anything* that would generate an ordinary compiler error. So actually your suggested syntax may /already/ work! --bb
Dec 13 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Janice Caron wrote:
 On 12/13/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 All you need is *anything* that would generate an ordinary compiler
 error.

The idea is /not/ to generate a compiler error. :-)

Right, and it won't because as you've been reiterating "substitution error is not a failure". If the template results in a something that's an ordinary syntax error then it's just a substitution failure. "static assert" is special in that it short circuits that behavior. --bb
Dec 13 2007
parent reply "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Thu, 13 Dec 2007 20:53:23 -0000, Janice Caron <caron800 googlemail.com>  
wrote:

 On 12/13/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 If the template results in a something that's
 an ordinary syntax error then it's just a substitution failure.  "static
 assert" is special in that it short circuits that behavior.

Really? Wow! I'm going to have to try that now!

So did it work? I guess you are using the Unix silence is golden principle.
Dec 13 2007
parent reply Robert DaSilva <sp.unit.262+digitalmars gmail.com> writes:
Bruce Adams wrote:
 On Thu, 13 Dec 2007 20:53:23 -0000, Janice Caron
 <caron800 googlemail.com> wrote:
 
 On 12/13/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 If the template results in a something that's
 an ordinary syntax error then it's just a substitution failure.  "static
 assert" is special in that it short circuits that behavior.

Really? Wow! I'm going to have to try that now!

So did it work? I guess you are using the Unix silence is golden principle.

I've tried it and it didn't work for me.
Dec 13 2007
next sibling parent "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Fri, 14 Dec 2007 07:33:18 -0000, Janice Caron <caron800 googlemail.com>  
wrote:

 On 12/14/07, Robert DaSilva <sp.unit.262+digitalmars gmail.com> wrote:
 I've tried it and it didn't work for me.

I didn't think it would. I didn't get a chance to try before Robert, because of timezone differences (I was in bed asleep between the two posts). Still, it's nice to see that folk in other parts of the globe can do the experiment while I'm tucked up in bed! :-) I believe that under the current system, if the (T:whatever) rule passes, the template is deemed to have matched, and nothing can subsequently change its mind. Any compile error encountered from there on is considered a real compile error. And I think that's correct behavior. The "template(fail)" suggestion would add a way to say, after the event, "I've changed my mind. Please consider this unmatched after all".

You've just added our old friend the backtracking algorithm. It does seem to me that as templates develop further they become more and more like a compile time prolog interpreter. This is how prolog predicates work. Each one is matched in turn and everything is undone unless it returns true.
Dec 14 2007
prev sibling next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Robert DaSilva wrote:
 Bruce Adams wrote:
 On Thu, 13 Dec 2007 20:53:23 -0000, Janice Caron
 <caron800 googlemail.com> wrote:

 On 12/13/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 If the template results in a something that's
 an ordinary syntax error then it's just a substitution failure.  "static
 assert" is special in that it short circuits that behavior.


I guess you are using the Unix silence is golden principle.

I've tried it and it didn't work for me.

Why be so cryptic? Show us what you tried for goodness sake. --bb
Dec 14 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Bill Baxter wrote:
 Robert DaSilva wrote:
 Bruce Adams wrote:
 On Thu, 13 Dec 2007 20:53:23 -0000, Janice Caron
 <caron800 googlemail.com> wrote:

 On 12/13/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 If the template results in a something that's
 an ordinary syntax error then it's just a substitution failure.  
 "static
 assert" is special in that it short circuits that behavior.


I guess you are using the Unix silence is golden principle.

I've tried it and it didn't work for me.

Why be so cryptic? Show us what you tried for goodness sake. --bb

Ok so I tried it myself. Apparently neither D nor C++ works the way I thought it did w.r.t SFINAE. It seems that SFINAE only applies to things in the function's declaration, not its contents. I suppose that does make sense, because otherwise it would be damned hard to figure out when you have a syntax error in specialized templates. Lutz Kettner has a pretty good explanation of how it works: http://www.mpi-inf.mpg.de/~kettner/courses/lib_design_03/notes/meta.html (Wikipedia's explanation is pathetic.) My appologies. Here are my tests: =========C++========= #include <stdio.h> template <typename T> struct Foo { T x; void spew() { printf("Generic struct"); } }; template <typename T> struct Foo<T*> { T x; void spew() { printf("Partial specialized struct\n"); // T y = x.foo; // an error } }; template <typename T> void func(T x) { printf("generic func\n"); } template <> void func<int*>(int* x) { printf("total specialized func\n"); //int z = x.foo; // an error } int main() { int *x; func<int*>(x); Foo<int*> ptrFoo; ptrFoo.spew(); return 0; } ============D================ module sfinae; import std.stdio; void func(T)(T x) { writefln("generic version"); } void func(T:T[])(T[] x) { writefln("specialized version"); // writefln(T.foo); // an error } void main() { int[] x; func!(int[])(x); } =============================== --bb
Dec 14 2007
parent BCS <ao pathlink.com> writes:
Reply to Bill,

 Ok so I tried it myself.  Apparently neither D nor C++ works the way I
 thought it did w.r.t SFINAE.  It seems that SFINAE only applies to
 things in the function's declaration, not its contents.  I suppose
 that does make sense, because otherwise it would be damned hard
 to figure out
 when you have a syntax error in specialized templates.

The body of a template must be syntactically correct no matter what. If template(fail) gets in I would want semantic errors during specialization to be errors. I would expect the programer to catch and bail when unusable arguments are given.
Dec 14 2007
prev sibling parent Jason House <jason.james.house gmail.com> writes:
Janice Caron Wrote:

 I believe that under the current system, if the (T:whatever) rule
 passes, the template is deemed to have matched, and nothing can
 subsequently change its mind. Any compile error encountered from there
 on is considered a real compile error. And I think that's correct
 behavior.
 
 The "template(fail)" suggestion would add a way to say, after the
 event, "I've changed my mind. Please consider this unmatched after
 all".

IMHO, this should have been part of the original post. Your suggestion now sounds much better to me. Of course, I don't like the proposed syntax.
Dec 14 2007
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
Janice Caron wrote:
 I would like to propose a new statement:
 
     static template(fail);
 
 which causes a compile-time substitution failure. And remember: SFINAE
 - Substitution Failure Is Not An Error
 
 Let me give you an example:
 
     struct S(T)
     {
         static if(isFloatingPoint!(T))
         {
             /*...*/
         }
         else static if(passesSomeVeryComplicatedTest!(T))
         {
             /*...*/
         }
         else
         {
             static template(fail); // SFINAE
         }
     }
 
 This has advantages over old-style specialisation.

I guess this would require the compiler to not complain if I did this? struct S(T) { static if(is(T==int)) static template(fail); } struct S(T) { } S!(int); Sean
Dec 13 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Sean Kelly wrote:
 Janice Caron wrote:
 I would like to propose a new statement:

     static template(fail);

 which causes a compile-time substitution failure. And remember: SFINAE
 - Substitution Failure Is Not An Error

 Let me give you an example:

     struct S(T)
     {
         static if(isFloatingPoint!(T))
         {
             /*...*/
         }
         else static if(passesSomeVeryComplicatedTest!(T))
         {
             /*...*/
         }
         else
         {
             static template(fail); // SFINAE
         }
     }

 This has advantages over old-style specialisation.

I guess this would require the compiler to not complain if I did this? struct S(T) { static if(is(T==int)) static template(fail); } struct S(T) { } S!(int); Sean

No, you have to make one of them specialized somehow, otherwise it's just a good old fashioned redundant definition error. Like if you tried to make two functions "void foo(int)". --bb
Dec 13 2007
parent Sean Kelly <sean f4.ca> writes:
Bill Baxter wrote:
 Sean Kelly wrote:
 Janice Caron wrote:
 I would like to propose a new statement:

     static template(fail);

 which causes a compile-time substitution failure. And remember: SFINAE
 - Substitution Failure Is Not An Error

 Let me give you an example:

     struct S(T)
     {
         static if(isFloatingPoint!(T))
         {
             /*...*/
         }
         else static if(passesSomeVeryComplicatedTest!(T))
         {
             /*...*/
         }
         else
         {
             static template(fail); // SFINAE
         }
     }

 This has advantages over old-style specialisation.

I guess this would require the compiler to not complain if I did this? struct S(T) { static if(is(T==int)) static template(fail); } struct S(T) { } S!(int);

No, you have to make one of them specialized somehow, otherwise it's just a good old fashioned redundant definition error. Like if you tried to make two functions "void foo(int)".

So either way I'd need to use specialization? I suppose this would still simplify coding a tad, but... Sean
Dec 13 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/13/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 No, you have to make one of them specialized somehow, otherwise it's
 just a good old fashioned redundant definition error.  Like if you tried
 to make two functions "void foo(int)".

I agree. But in the particular example you cited, you could instead just write struct S(T) { static if(is(T==int)) static template(fail); } because the "else" condition is empty. Then S!(int) would fail, but S!(float) would succeed.
Dec 13 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/13/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 All you need is *anything* that would generate an ordinary compiler
 error.

The idea is /not/ to generate a compiler error. :-)
Dec 13 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/13/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 If the template results in a something that's
 an ordinary syntax error then it's just a substitution failure.  "static
 assert" is special in that it short circuits that behavior.

Really? Wow! I'm going to have to try that now!
Dec 13 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/13/07, Jason House <jason.james.house gmail.com> wrote:
 If more than one matches successfully, the code wasn't coded well enough.

But then you'd have to do template A(T:int) {/*code*/} template A(T:anything except an int){/*code*/} Even apart from the fact that there's no way to express that, it seems silly. It's perfectly reasonable to allow multiple matches. The compiler simply tries the most specialised first.
Dec 13 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/14/07, Jason House <jason.james.house gmail.com> wrote:
 I've always been under the impression that use of static if is the "D way".

It's a choice. It happens to be my /preferred/ choice, but the multiple-templates way still needs to be supported.
 if there's a chance the programmer (or
 more likely, a later code maintainer) can get confused, then I think it's
 better to err on the side of rigorous compile-time errors.

None of which changes the fact that, as I keep stressing, IT'S NOT AN ERROR to fail to match. One should not be forced to generate an error, when there plainly isn't one. Even if you /only/ use the static if style, and there are no alternative overloads, at the end of your chain of static ifs, you still want to be able so say static template(fail) (or whatever syntax is deemed right), rather than static assert, because if the template can't be instantiated at all, yes there's an error /somewhere/ -- but it's not in the template code; it's in the calling code - and that's the only place an error should be reported.
Dec 13 2007
prev sibling next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 12/14/07, Robert DaSilva <sp.unit.262+digitalmars gmail.com> wrote:
 I've tried it and it didn't work for me.

I didn't think it would. I didn't get a chance to try before Robert, because of timezone differences (I was in bed asleep between the two posts). Still, it's nice to see that folk in other parts of the globe can do the experiment while I'm tucked up in bed! :-) I believe that under the current system, if the (T:whatever) rule passes, the template is deemed to have matched, and nothing can subsequently change its mind. Any compile error encountered from there on is considered a real compile error. And I think that's correct behavior. The "template(fail)" suggestion would add a way to say, after the event, "I've changed my mind. Please consider this unmatched after all".
Dec 13 2007
parent reply BCS <ao pathlink.com> writes:
what would this do?:

template Foo(T)
{
  static if(logic1!(T)) template(fail);
}

template Foo(T)
{
  static if(logic2!(T)) template(fail);
}

from a programers level, it might be valid. My though is (an wow could this 
be costly) try all cases*, if there isn't exactly one match, error.


* Walter's not going to like that for performance reasons.
Dec 14 2007
parent reply "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Fri, 14 Dec 2007 19:09:25 -0000, BCS <ao pathlink.com> wrote:

 what would this do?:

 template Foo(T)
 {
   static if(logic1!(T)) template(fail);
 }

 template Foo(T)
 {
   static if(logic2!(T)) template(fail);
 }

 from a programers level, it might be valid. My though is (an wow could  
 this be costly) try all cases*, if there isn't exactly one match, error.


 * Walter's not going to like that for performance reasons.

write poorly designed / optimised code. He may not like it but a programming language can't help but permit it even if it does discourage it.
Dec 14 2007
parent reply BCS <ao pathlink.com> writes:
Reply to Bruce,

 On Fri, 14 Dec 2007 19:09:25 -0000, BCS <ao pathlink.com> wrote:
 
 what would this do?:
 
 template Foo(T)
 {
 static if(logic1!(T)) template(fail);
 }
 template Foo(T)
 {
 static if(logic2!(T)) template(fail);
 }
 from a programers level, it might be valid. My though is (an wow
 could  this be costly) try all cases*, if there isn't exactly one
 match, error.
 
 * Walter's not going to like that for performance reasons.
 

to write poorly designed / optimised code. He may not like it but a programming language can't help but permit it even if it does discourage it.

I'm taking about DMD's performance. The compile time cost of find a template match could go through the roof with the "backtracking finds one match" solution.
Dec 14 2007
parent reply "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Fri, 14 Dec 2007 20:01:17 -0000, BCS <ao pathlink.com> wrote:

 Reply to Bruce,

 On Fri, 14 Dec 2007 19:09:25 -0000, BCS <ao pathlink.com> wrote:

 what would this do?:
  template Foo(T)
 {
 static if(logic1!(T)) template(fail);
 }
 template Foo(T)
 {
 static if(logic2!(T)) template(fail);
 }
 from a programers level, it might be valid. My though is (an wow
 could  this be costly) try all cases*, if there isn't exactly one
 match, error.
  * Walter's not going to like that for performance reasons.

to write poorly designed / optimised code. He may not like it but a programming language can't help but permit it even if it does discourage it.

I'm taking about DMD's performance. The compile time cost of find a template match could go through the roof with the "backtracking finds one match" solution.

doesn't run that slowly. I don't think it would be much of a problem in D unless you write a huge number of really complex evil templates and stick them all in one module. I'm not sure even the legendary obfuscator Downs could stretch it to breaking point. Mr. Blade library might come closer. Anyway, as with any language feature you should only pay for it if you use it. Unless you are overusing it, it shouldn't be a problem.
Dec 14 2007
parent reply BCS <ao pathlink.com> writes:
Reply to Bruce,

 On Fri, 14 Dec 2007 20:01:17 -0000, BCS <ao pathlink.com> wrote:
 
 Reply to Bruce,
 
 On Fri, 14 Dec 2007 19:09:25 -0000, BCS <ao pathlink.com> wrote:
 
 what would this do?:
 template Foo(T)
 {
 static if(logic1!(T)) template(fail);
 }
 template Foo(T)
 {
 static if(logic2!(T)) template(fail);
 }
 from a programers level, it might be valid. My though is (an wow
 could  this be costly) try all cases*, if there isn't exactly one
 match, error.
 * Walter's not going to like that for performance reasons.

you to write poorly designed / optimised code. He may not like it but a programming language can't help but permit it even if it does discourage it.

template match could go through the roof with the "backtracking finds one match" solution.

and it doesn't run that slowly. I don't think it would be much of a problem in D unless you write a huge number of really complex evil templates and stick them all in one module. I'm not sure even the legendary obfuscator Downs could stretch it to breaking point. Mr. Blade library might come closer. Anyway, as with any language feature you should only pay for it if you use it. Unless you are overusing it, it shouldn't be a problem.

take a look at scrapple.backmath, the bulk of it is a huge list of static ifs inside templates. The way it's set up now it doesn't do any backtracking, but I rather dislike the brut force approach it is taking ("is this xyzzy? No? how about xyzzY? No? How about...[next 100 lines skipped]") The way code seems to go, new stuff will get layered on top of old stuff. Add backtracking into the mix as part of the language and soon D will get a rep of "yah it's good, but I can wright, debug, compile and be done faster in C than I can compile in D".
Dec 17 2007
parent reply "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Mon, 17 Dec 2007 18:53:49 -0000, BCS <ao pathlink.com> wrote:

 Reply to Bruce,

 On Fri, 14 Dec 2007 20:01:17 -0000, BCS <ao pathlink.com> wrote:



and it doesn't run that slowly. I don't think it would be much of a problem in D unless you write a huge number of really complex evil templates and stick them all in one module. I'm not sure even the legendary obfuscator Downs could stretch it to breaking point. Mr. Blade library might come closer. Anyway, as with any language feature you should only pay for it if you use it. Unless you are overusing it, it shouldn't be a problem.

take a look at scrapple.backmath, the bulk of it is a huge list of static ifs inside templates. The way it's set up now it doesn't do any backtracking, but I rather dislike the brut force approach it is taking ("is this xyzzy? No? how about xyzzY? No? How about...[next 100 lines skipped]") The way code seems to go, new stuff will get layered on top of old stuff. Add backtracking into the mix as part of the language and soon D will get a rep of "yah it's good, but I can wright, debug, compile and be done faster in C than I can compile in D".

frequently required. If it is for something that occurs rarely and is discouraged only bad programs will be tangled and bad programs will always be tangled. Is it really likely to occur often? and is there a better solution or workaround?
Dec 17 2007
parent BCS <ao pathlink.com> writes:
Reply to Bruce,

 That is only the case if it is a programming style that is encouraged
 or
 frequently required. If it is for something that occurs rarely and is
 discouraged only bad programs will be tangled and bad programs will
 always
 be tangled. Is it really likely to occur often?

 and is there a better
 solution
 or workaround?

take that last bit out of context and you have just stated my concern. Before template(fail) is added it's implications and alternatives should be considered. Is there some way to make it would neatly and consistently without adding a huge cost? I'm just stating concerns, not saying it's a bad idea.
Dec 18 2007
prev sibling parent "Aziz K." <aziz.kerim gmail.com> writes:
I thought about this issue a little bit before I went to sleep yesterday.  
It just occurred to me that a pragma statement would be perfect to solve  
this problem. Especially since it wouldn't require any modifications to a  
D parser.

struct S(T)
{
   static if(isFloatingPoint!(T))
   {
     /*...*/
   }
   /* other tests */	
   else
     pragma(fail_instantiation);
}

With this statement we can give a hint to the compiler to abort  
instantiation and try other (specialized) templates.
The question now is which identifier would be a good, descriptive and  
concise one.

.) fail_instantiation // good one, but too long?
.) fail_i11n // shortened similar to i18n/internationalization
.) fail // sounds too general and may conflict with possible future uses.
.) substitution_failure // a bit too long?
.) instantiation_failure // ditto
.) iFail // *rolls eyes*
.) stop_it_plz_1exclamation_mark11 // :D

Well, I guess some of you may have more, better suggestions. Let's see  
what the community votes for.
Dec 14 2007