www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - static try catch construct would be helpful

reply Bill Baxter <dnewsgroup billbaxter.com> writes:
When plugging types together sometimes you find this one is missing a 
member or that one defines it in a different way than you expect or 
maybe a function with some signature isn't supported.

What you'd like to do is say "compile this block if you can -- otherwise 
leave it out"  It's like try-catch, but compile-time.
Except you don't really want a 'catch'.  It's more like an 'else'.  So I 
propose a new form of static:

    static if {
       // some code
    } else static if {
       // other code
    } else {
       // last resort code
    }

The idea is: if "some code" compiles, then use that, otherwise try 
"other code", and finally if none of the other code blocks compiles, use 
the else block.

And of course it could be mix-n-matched with regular static if()'s.


------
Here's a motivating example taken from real code where I was wrapping 
one struct with another.  The Inner type may or may not support a 
particular signature of the "doSomething" function.

struct Outer(Inner)
{
    void doSomething(int a, int b, int c, int d)
    {
       _inner.doSomething(a,b,c,d);
       // do something else here
    }

     ...

    Inner _inner;
}

Currently, the best way I've found to conditionalize that is:

    static if(is(typeof(_inner.doSomething(1,1,1,1)))) {
    void doSomething(int a, int b, int c, int d)
    {
       _inner.doSomething(a,b,c,d);
       // do something else here
    }
    }

Maybe there's a better way to write that one.. I'm not sure.  But the 
signature of the thing that may or may not exist can be arbitrarily 
complex.  It becomes difficult to think of an is-expression that can 
test for what you want.   You can probably break out the test into a 
separate template and make it so you can do
     static if(is(DoSomeCheck!(....))
but writing that DoSomeCheck is annoying too and starts to clutter your 
source with lots of one-off gobbledy gook templates.

I think "argument-less static if blocks" would handle such cases nicely.


There's a slight issue in that it could be pretty hard to debug with 
compiler errors being silently ignored, but that happens currently with 
the expressions inside "static if(is(...))" already.

--bb
Feb 25 2008
next sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
Bill Baxter wrote:
 When plugging types together sometimes you find this one is missing a 
 member or that one defines it in a different way than you expect or 
 maybe a function with some signature isn't supported.
 
 What you'd like to do is say "compile this block if you can -- otherwise 
 leave it out"  It's like try-catch, but compile-time.
 Except you don't really want a 'catch'.  It's more like an 'else'.  So I 
 propose a new form of static:
 
    static if {
       // some code
    } else static if {
       // other code
    } else {
       // last resort code
    }
 
 The idea is: if "some code" compiles, then use that, otherwise try 
 "other code", and finally if none of the other code blocks compiles, use 
 the else block.
 
 And of course it could be mix-n-matched with regular static if()'s.
 
 
 ------
 Here's a motivating example taken from real code where I was wrapping 
 one struct with another.  The Inner type may or may not support a 
 particular signature of the "doSomething" function.
 
 struct Outer(Inner)
 {
    void doSomething(int a, int b, int c, int d)
    {
       _inner.doSomething(a,b,c,d);
       // do something else here
    }
 
     ...
 
    Inner _inner;
 }
 
 Currently, the best way I've found to conditionalize that is:
 
    static if(is(typeof(_inner.doSomething(1,1,1,1)))) {
    void doSomething(int a, int b, int c, int d)
    {
       _inner.doSomething(a,b,c,d);
       // do something else here
    }
    }
 
 Maybe there's a better way to write that one.. I'm not sure.  But the 
 signature of the thing that may or may not exist can be arbitrarily 
 complex.  It becomes difficult to think of an is-expression that can 
 test for what you want.   You can probably break out the test into a 
 separate template and make it so you can do
     static if(is(DoSomeCheck!(....))
 but writing that DoSomeCheck is annoying too and starts to clutter your 
 source with lots of one-off gobbledy gook templates.
 
 I think "argument-less static if blocks" would handle such cases nicely.
 
 
 There's a slight issue in that it could be pretty hard to debug with 
 compiler errors being silently ignored, but that happens currently with 
 the expressions inside "static if(is(...))" already.
 
 --bb
I like it, although I'm not sure how difficult that'd be to implement in the DMDFE's current model. votes++, under the stipulation that all code in question must be syntactically correct, even if it's not semantically correct. Personally, I like "static try { } else static try { } else { }", which I think conveys the meaning better than static if. Also, I think there should definitely be a strong caution on the specs page that other methods should be tried first, since, as you mentioned, it can spuriously compile the wrong thing if you accidentally have an error there.
Feb 25 2008
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Robert Fraser wrote:
 Bill Baxter wrote:
 When plugging types together sometimes you find this one is missing a 
 member or that one defines it in a different way than you expect or 
 maybe a function with some signature isn't supported.

 What you'd like to do is say "compile this block if you can -- 
 otherwise leave it out"  It's like try-catch, but compile-time.
 Except you don't really want a 'catch'.  It's more like an 'else'.  So 
 I propose a new form of static:

    static if {
       // some code
    } else static if {
       // other code
    } else {
       // last resort code
    }

 The idea is: if "some code" compiles, then use that, otherwise try 
 "other code", and finally if none of the other code blocks compiles, 
 use the else block.

 And of course it could be mix-n-matched with regular static if()'s.


 ------
 Here's a motivating example taken from real code where I was wrapping 
 one struct with another.  The Inner type may or may not support a 
 particular signature of the "doSomething" function.

 struct Outer(Inner)
 {
    void doSomething(int a, int b, int c, int d)
    {
       _inner.doSomething(a,b,c,d);
       // do something else here
    }

     ...

    Inner _inner;
 }

 Currently, the best way I've found to conditionalize that is:

    static if(is(typeof(_inner.doSomething(1,1,1,1)))) {
    void doSomething(int a, int b, int c, int d)
    {
       _inner.doSomething(a,b,c,d);
       // do something else here
    }
    }

 Maybe there's a better way to write that one.. I'm not sure.  But the 
 signature of the thing that may or may not exist can be arbitrarily 
 complex.  It becomes difficult to think of an is-expression that can 
 test for what you want.   You can probably break out the test into a 
 separate template and make it so you can do
     static if(is(DoSomeCheck!(....))
 but writing that DoSomeCheck is annoying too and starts to clutter 
 your source with lots of one-off gobbledy gook templates.

 I think "argument-less static if blocks" would handle such cases nicely.


 There's a slight issue in that it could be pretty hard to debug with 
 compiler errors being silently ignored, but that happens currently 
 with the expressions inside "static if(is(...))" already.

 --bb
I like it, although I'm not sure how difficult that'd be to implement in the DMDFE's current model. votes++, under the stipulation that all code in question must be syntactically correct, even if it's not semantically correct. Personally, I like "static try { } else static try { } else { }", which I think conveys the meaning better than static if. Also, I think there should definitely be a strong caution on the specs page that other methods should be tried first, since, as you mentioned, it can spuriously compile the wrong thing if you accidentally have an error there.
And those could be combined with static ifs, too right? static try { ... } else static if (is(T==blah)) { ... } else static try { ... }. --bb
Feb 25 2008
parent Robert Fraser <fraserofthenight gmail.com> writes:
Bill Baxter wrote:
 Robert Fraser wrote:
 Bill Baxter wrote:
 When plugging types together sometimes you find this one is missing a 
 member or that one defines it in a different way than you expect or 
 maybe a function with some signature isn't supported.

 What you'd like to do is say "compile this block if you can -- 
 otherwise leave it out"  It's like try-catch, but compile-time.
 Except you don't really want a 'catch'.  It's more like an 'else'.  
 So I propose a new form of static:

    static if {
       // some code
    } else static if {
       // other code
    } else {
       // last resort code
    }

 The idea is: if "some code" compiles, then use that, otherwise try 
 "other code", and finally if none of the other code blocks compiles, 
 use the else block.

 And of course it could be mix-n-matched with regular static if()'s.


 ------
 Here's a motivating example taken from real code where I was wrapping 
 one struct with another.  The Inner type may or may not support a 
 particular signature of the "doSomething" function.

 struct Outer(Inner)
 {
    void doSomething(int a, int b, int c, int d)
    {
       _inner.doSomething(a,b,c,d);
       // do something else here
    }

     ...

    Inner _inner;
 }

 Currently, the best way I've found to conditionalize that is:

    static if(is(typeof(_inner.doSomething(1,1,1,1)))) {
    void doSomething(int a, int b, int c, int d)
    {
       _inner.doSomething(a,b,c,d);
       // do something else here
    }
    }

 Maybe there's a better way to write that one.. I'm not sure.  But the 
 signature of the thing that may or may not exist can be arbitrarily 
 complex.  It becomes difficult to think of an is-expression that can 
 test for what you want.   You can probably break out the test into a 
 separate template and make it so you can do
     static if(is(DoSomeCheck!(....))
 but writing that DoSomeCheck is annoying too and starts to clutter 
 your source with lots of one-off gobbledy gook templates.

 I think "argument-less static if blocks" would handle such cases nicely.


 There's a slight issue in that it could be pretty hard to debug with 
 compiler errors being silently ignored, but that happens currently 
 with the expressions inside "static if(is(...))" already.

 --bb
I like it, although I'm not sure how difficult that'd be to implement in the DMDFE's current model. votes++, under the stipulation that all code in question must be syntactically correct, even if it's not semantically correct. Personally, I like "static try { } else static try { } else { }", which I think conveys the meaning better than static if. Also, I think there should definitely be a strong caution on the specs page that other methods should be tried first, since, as you mentioned, it can spuriously compile the wrong thing if you accidentally have an error there.
And those could be combined with static ifs, too right? static try { ... } else static if (is(T==blah)) { ... } else static try { ... }. --bb
Well, because of the way the parser words, I'd assume they could be combined with any compile-time construct: static try { } else static if(...) { } else version(...) { } else debug(...) { } else { }
Feb 26 2008
prev sibling next sibling parent reply Jason House <jason.james.house gmail.com> writes:
Bill Baxter wrote:

 When plugging types together sometimes you find this one is missing a
 member or that one defines it in a different way than you expect or
 maybe a function with some signature isn't supported.
 
 What you'd like to do is say "compile this block if you can -- otherwise
 leave it out"  It's like try-catch, but compile-time.
 Except you don't really want a 'catch'.  It's more like an 'else'.  So I
 propose a new form of static:
 
     static if {
        // some code
     } else static if {
        // other code
     } else {
        // last resort code
     }
 
 The idea is: if "some code" compiles, then use that, otherwise try
 "other code", and finally if none of the other code blocks compiles, use
 the else block.
 
 And of course it could be mix-n-matched with regular static if()'s.
 
 
 ------
 Here's a motivating example taken from real code where I was wrapping
 one struct with another.  The Inner type may or may not support a
 particular signature of the "doSomething" function.
 
 struct Outer(Inner)
 {
     void doSomething(int a, int b, int c, int d)
     {
        _inner.doSomething(a,b,c,d);
        // do something else here
     }
 
      ...
 
     Inner _inner;
 }
 
 Currently, the best way I've found to conditionalize that is:
 
     static if(is(typeof(_inner.doSomething(1,1,1,1)))) {
     void doSomething(int a, int b, int c, int d)
     {
        _inner.doSomething(a,b,c,d);
        // do something else here
     }
     }
 
 Maybe there's a better way to write that one.. I'm not sure.  But the
 signature of the thing that may or may not exist can be arbitrarily
 complex.  It becomes difficult to think of an is-expression that can
 test for what you want.   You can probably break out the test into a
 separate template and make it so you can do
      static if(is(DoSomeCheck!(....))
 but writing that DoSomeCheck is annoying too and starts to clutter your
 source with lots of one-off gobbledy gook templates.
 
 I think "argument-less static if blocks" would handle such cases nicely.
 
 
 There's a slight issue in that it could be pretty hard to debug with
 compiler errors being silently ignored, but that happens currently with
 the expressions inside "static if(is(...))" already.
 
 --bb
This starts to sound a lot like SFINAE from templates. Maybe there's a way to write it up that way or combine a proposal with how to modify how templates are used? IIRC, there was past discussion about modifications to how templates handled errors. I do like the idea of static try{} with else clauses. Would it be practical to make the static try only spill over with static assert failures? (to keep error cases under control)
Feb 26 2008
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Jason House wrote:
 Bill Baxter wrote:
 
 When plugging types together sometimes you find this one is missing a
 member or that one defines it in a different way than you expect or
 maybe a function with some signature isn't supported.

 What you'd like to do is say "compile this block if you can -- otherwise
 leave it out"  It's like try-catch, but compile-time.
 Except you don't really want a 'catch'.  It's more like an 'else'.  So I
 propose a new form of static:

     static if {
        // some code
     } else static if {
        // other code
     } else {
        // last resort code
     }

 The idea is: if "some code" compiles, then use that, otherwise try
 "other code", and finally if none of the other code blocks compiles, use
 the else block.

 And of course it could be mix-n-matched with regular static if()'s.


 ------
 Here's a motivating example taken from real code where I was wrapping
 one struct with another.  The Inner type may or may not support a
 particular signature of the "doSomething" function.

 struct Outer(Inner)
 {
     void doSomething(int a, int b, int c, int d)
     {
        _inner.doSomething(a,b,c,d);
        // do something else here
     }

      ...

     Inner _inner;
 }

 Currently, the best way I've found to conditionalize that is:

     static if(is(typeof(_inner.doSomething(1,1,1,1)))) {
     void doSomething(int a, int b, int c, int d)
     {
        _inner.doSomething(a,b,c,d);
        // do something else here
     }
     }

 Maybe there's a better way to write that one.. I'm not sure.  But the
 signature of the thing that may or may not exist can be arbitrarily
 complex.  It becomes difficult to think of an is-expression that can
 test for what you want.   You can probably break out the test into a
 separate template and make it so you can do
      static if(is(DoSomeCheck!(....))
 but writing that DoSomeCheck is annoying too and starts to clutter your
 source with lots of one-off gobbledy gook templates.

 I think "argument-less static if blocks" would handle such cases nicely.


 There's a slight issue in that it could be pretty hard to debug with
 compiler errors being silently ignored, but that happens currently with
 the expressions inside "static if(is(...))" already.

 --bb
This starts to sound a lot like SFINAE from templates. Maybe there's a way to write it up that way or combine a proposal with how to modify how templates are used? IIRC, there was past discussion about modifications to how templates handled errors.
Yeh, I think that was more about forcing a SFINAE error, which would still be nice to have.
 
 I do like the idea of static try{} with else clauses.  Would it be practical
 to make the static try only spill over with static assert failures? (to
 keep error cases under control)
I don't think so. Having to put the error-causing thing inside an assert would mean you have to make it an expression. If you had such an expression for the condition you want to test, then you could just put it in a static if -- "static if (is(theExpression))". So I don't think there'd be much point of having the feature if it only worked inside static assert(). --bb
Feb 26 2008
prev sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Bill Baxter wrote:
 Currently, the best way I've found to conditionalize that is:
 
    static if(is(typeof(_inner.doSomething(1,1,1,1)))) {
    void doSomething(int a, int b, int c, int d)
    {
       _inner.doSomething(a,b,c,d);
       // do something else here
    }
    }
 
 Maybe there's a better way to write that one.. I'm not sure.  But the 
 signature of the thing that may or may not exist can be arbitrarily 
 complex.  It becomes difficult to think of an is-expression that can 
 test for what you want.  
I don't understand this. Why is it difficult to use an is-expression to test the condition you are checking? If you're just checking to see if a member exists, you can just do static if(is(typeof(_inner.doSomething))) { If you're checking for the existence of a particular overload, then you will need an expression where that function overload is called. Perhaps the problem is code duplication? (like having two function calls, such as "_inner.doSomething(1,1,1,1)" and "_inner.doSomething(a,b,c,d)" ? -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Mar 26 2008
parent reply "Koroskin Denis" <2korden+dmd gmail.com> writes:
On Wed, 26 Mar 2008 16:30:53 +0300, Bruno Medeiros  =

<brunodomedeiros+spam com.gmail> wrote:

 Bill Baxter wrote:
 Currently, the best way I've found to conditionalize that is:
     static if(is(typeof(_inner.doSomething(1,1,1,1)))) {
    void doSomething(int a, int b, int c, int d)
    {
       _inner.doSomething(a,b,c,d);
       // do something else here
    }
    }
  Maybe there's a better way to write that one.. I'm not sure.  But th=
e =
 signature of the thing that may or may not exist can be arbitrarily  =
 complex.  It becomes difficult to think of an is-expression that can =
=
 test for what you want.
I don't understand this. Why is it difficult to use an is-expression t=
o =
 test the condition you are checking? If you're just checking to see if=
a =
 member exists, you can just do
    static if(is(typeof(_inner.doSomething))) {
 If you're checking for the existence of a particular overload, then yo=
u =
 will need an expression where that function overload is called.
 Perhaps the problem is code duplication? (like having two function  =
 calls, such as "_inner.doSomething(1,1,1,1)" and  =
 "_inner.doSomething(a,b,c,d)" ?
Well, I can think of a more sophisticated examples: template invert(real arg) { const real invert =3D 1/arg; } unittest { static try { real success =3D invert!(1); real failure =3D invert!(0); static assert(false); // should not get here } static catch (CT_DivideByZeroException e) // CT for Compile-Time { pragma(msg, e.toString()); // should print DivideByZeroExceptio= n } } template wrong_factorial(int i) { const int wrong_factorial =3D i * wrong_factorial!(i-1); } unittest { static try { const int f =3D wrong_factorial!(1); static assert(false); } static catch (CT_Exception e) { pragma(msg, e.toString()); // should print "CT Stack overflow"= or = "CT Nested loop is too deep" } } or even template SomeFunc(T) { static if (!is(T : Object)) // should only be invoked with referen= ce = types { static throw new CT_Exception("Incompatible Type"); } ... } unittest { static try { alias SomeFunc!(int) SomeFuncInt; SomeFuncInt(); static assert(false); } static catch(CT_AssertionFailureExpection e) { // failure } static catch(CT_Exception e) { pragma(msg, e.toString()); // success } }
Mar 26 2008
next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
Useful though static try/catch would be, I'd like to see static
switch/case, static for, static while, and static foreach first.
Mar 26 2008
parent "Koroskin Denis" <2korden+dmd gmail.com> writes:
On Wed, 26 Mar 2008 18:14:18 +0300, Janice Caron <caron800 googlemail.co=
m>  =

wrote:

 Useful though static try/catch would be, I'd like to see static
 switch/case, static for, static while, and static foreach first.
Or just "static" keyword: static { switch (i) { case 1: doSomeThing(); case 2: doSomeThingElse(); } } static assert(value); <=3D> static { assert(value); } static if (condition) { static { /* ... */ <=3D> if (condition) { /* ... */ } } } Just like debug: debug writefln("%s", value); <=3D> debug { writefln("%s", value); } Fits nicely! :) (Hope, you use fixed-width fonts)
Mar 26 2008
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Koroskin Denis wrote:
 On Wed, 26 Mar 2008 16:30:53 +0300, Bruno Medeiros 
 <brunodomedeiros+spam com.gmail> wrote:
 
 Bill Baxter wrote:
 Currently, the best way I've found to conditionalize that is:
     static if(is(typeof(_inner.doSomething(1,1,1,1)))) {
    void doSomething(int a, int b, int c, int d)
    {
       _inner.doSomething(a,b,c,d);
       // do something else here
    }
    }
  Maybe there's a better way to write that one.. I'm not sure.  But 
 the signature of the thing that may or may not exist can be 
 arbitrarily complex.  It becomes difficult to think of an 
 is-expression that can test for what you want.
I don't understand this. Why is it difficult to use an is-expression to test the condition you are checking? If you're just checking to see if a member exists, you can just do static if(is(typeof(_inner.doSomething))) { If you're checking for the existence of a particular overload, then you will need an expression where that function overload is called. Perhaps the problem is code duplication? (like having two function calls, such as "_inner.doSomething(1,1,1,1)" and "_inner.doSomething(a,b,c,d)" ?
Well, I can think of a more sophisticated examples: template invert(real arg) { const real invert = 1/arg; } unittest { static try { real success = invert!(1); real failure = invert!(0); static assert(false); // should not get here } static catch (CT_DivideByZeroException e) // CT for Compile-Time { pragma(msg, e.toString()); // should print DivideByZeroException } } template wrong_factorial(int i) { const int wrong_factorial = i * wrong_factorial!(i-1); } unittest { static try { const int f = wrong_factorial!(1); static assert(false); } static catch (CT_Exception e) { pragma(msg, e.toString()); // should print "CT Stack overflow" or "CT Nested loop is too deep" } } or even
The divide by zero case actually works with an is-expression (that is, the is-expression returns false if such compile time error occurs there). I don't think it's something that is clearly defined in the spec tough, so I don't know how much this behavior can be relied on. The wrong factorial case on the other hand, doesn't work. If it is in an is-expression there will always be a recursive expansion compiler error. In any case, I see little or no usefulness in being able to detect/catch such extreme compiler errors. It just seems bad structure, and it is something that not even in runtime I would recommend doing (catching Divide by Zero or Stack Overflow errors). -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Mar 27 2008