digitalmars.D - Trouble creating a formatted assert wrapper
- Peter Alexander (25/25) Sep 02 2012 One thing that's always bothered me is that I have to use
- Simen Kjaeraas (13/37) Sep 02 2012 void myAssert(int line = __LINE__, string file = __FILE__, Args...)(lazy...
- Peter Alexander (3/12) Sep 02 2012 Nice. I thought of that, but for some reason I thought that you
- Simen Kjaeraas (5/17) Sep 02 2012 tbh, I was unsure myself. Sometimes you should just try. :p
- timotheecour (22/24) Sep 02 2012 Won't that create "template code bloat" ? Ie everytime myAssert
- Peter Alexander (5/9) Sep 02 2012 You can easily counter that by delegating the body to a
- Nick Treleaven (14/31) Sep 03 2012 Do you mean:
- Nick Treleaven (10/12) Sep 03 2012 Oops, that is unsafe, fixed:
- Philippe Sigaud (5/18) Sep 02 2012 IIRC, you can even have them before standard template args. Template
- Timon Gehr (3/27) Sep 02 2012 I don't think there is a deep reason why IFTI shouldn't succeed in this
- Peter Alexander (5/47) Sep 02 2012 Consider:
- Chris Nicholson-Sauls (6/10) Sep 03 2012 Easy: the variadic arguments are not, themselves, optional.
- Peter Alexander (6/19) Sep 03 2012 The variadic arguments are optional, as you can have zero
- Chris Nicholson-Sauls (23/43) Sep 03 2012 They are not really optional. That the language fails to enforce
- Chris Nicholson-Sauls (11/13) Sep 03 2012 Turns out, I was quite wrong, and I'm happy to be. Empty array
- Don Clugston (8/20) Sep 04 2012 It was a special-case hack to fix a special-case need. It was extremely
- Chris Nicholson-Sauls (11/21) Sep 04 2012 How difficult would hidden params, triggered by usage, be? Ie:
- Don Clugston (14/37) Sep 04 2012 I don't know how that could be done. You need to know the function
- Chris Nicholson-Sauls (6/20) Sep 05 2012 Fair enough; I hadn't considered the case of having only the
- Jonathan M Davis (11/33) Sep 03 2012 You can use a template constraint if you want to require that there be a...
One thing that's always bothered me is that I have to use std.string.format to produce useful assert messages: assert(x == 1, format("x doesn't equal 1, it is %d", x)); Of course, I tried to solve this by producing my own assert-like function wrapper: void myAssert(Args...)(lazy bool condition, lazy Args args) { assert(condition, format(args)); } That's all good, but the problem now is that I get the wrong file and line in the assert message, so I tried to use the default arg __FILE__ and __LINE__ trick: void myAssert(Args...)(lazy bool condition, lazy Args args, int line = __LINE__, string file = __FILE__) { if (!condition) { writeln("Assertion failed %s:%d", file, line); writefln(args); exit(1); } } But I can't have default arguments because of the variadic arguments! Can anyone think of a way around this?
Sep 02 2012
On Sun, 02 Sep 2012 19:14:07 +0200, Peter Alexander <peter.alexander.au gmail.com> wrote:One thing that's always bothered me is that I have to use std.string.format to produce useful assert messages: assert(x == 1, format("x doesn't equal 1, it is %d", x)); Of course, I tried to solve this by producing my own assert-like function wrapper: void myAssert(Args...)(lazy bool condition, lazy Args args) { assert(condition, format(args)); } That's all good, but the problem now is that I get the wrong file and line in the assert message, so I tried to use the default arg __FILE__ and __LINE__ trick: void myAssert(Args...)(lazy bool condition, lazy Args args, int line = __LINE__, string file = __FILE__) { if (!condition) { writeln("Assertion failed %s:%d", file, line); writefln(args); exit(1); } } But I can't have default arguments because of the variadic arguments! Can anyone think of a way around this?void myAssert(int line = __LINE__, string file = __FILE__, Args...)(lazy bool condition, lazy Args args) { if (!condition) { writefln("Assertion failed %s:%d", file, line); writefln(args); exit(1); } } -- Simen
Sep 02 2012
On Sunday, 2 September 2012 at 18:23:48 UTC, Simen Kjaeraas wrote:void myAssert(int line = __LINE__, string file = __FILE__, Args...)(lazy bool condition, lazy Args args) { if (!condition) { writefln("Assertion failed %s:%d", file, line); writefln(args); exit(1); } }Nice. I thought of that, but for some reason I thought that you couldn't have default args before variadic args.
Sep 02 2012
On Sun, 02 Sep 2012 20:46:50 +0200, Peter Alexander <peter.alexander.au gmail.com> wrote:On Sunday, 2 September 2012 at 18:23:48 UTC, Simen Kjaeraas wrote:tbh, I was unsure myself. Sometimes you should just try. :p -- Simenvoid myAssert(int line = __LINE__, string file = __FILE__, Args...)(lazy bool condition, lazy Args args) { if (!condition) { writefln("Assertion failed %s:%d", file, line); writefln(args); exit(1); } }Nice. I thought of that, but for some reason I thought that you couldn't have default args before variadic args.
Sep 02 2012
Won't that create "template code bloat" ? Ie everytime myAssert is used a new function is created. There was if I remember some discussion regarding not passing __LINE__,__FILE__ in assert related functions because of that. If backtrace worked properly we should be able to go up the stack and get line number information when the assert fails (no time penalty when assert doesn't fail). Another option is to use a lazy tuple argument inside myAssert instead of variadic arguments, which allows to pass line and file AFTER, without template bloat. A related question: In C++ we can stringify arguments and use it to provide informative asserts without duplicating code specifying a string version of the condition: #define assert( isOK ) ( (isOK) ? (void)0 : (void)myAssert(#isOK,__LINE__,__PRETTY_FUNCTION__, __FILE__) ) Likewise for related quick debug functions: #define DEBUG(val) myDEBUG(#val,val) There seems to be no way of doing this currently (besides the ugly mixin(myAssertString("i==0")) which would parse the condition at CT). Would that be possible to add?void myAssert(int line = __LINE__, string file = __FILE__, Args...)(lazy bool condition, lazy Args args) {
Sep 02 2012
On Sunday, 2 September 2012 at 19:31:24 UTC, timotheecour wrote:You can easily counter that by delegating the body to a non-template function. Even then, it's only in non-release builds and it's a small function anyway.Won't that create "template code bloat" ? Ie everytime myAssert is used a new function is created.void myAssert(int line = __LINE__, string file = __FILE__, Args...)(lazy bool condition, lazy Args args) {
Sep 02 2012
On 02/09/2012 20:31, timotheecour wrote:...Won't that create "template code bloat" ? Ie everytime myAssert is used a new function is created.void myAssert(int line = __LINE__, string file = __FILE__, Args...)(lazy bool condition, lazy Args args) {Another option is to use a lazy tuple argument inside myAssert instead of variadic arguments, which allows to pass line and file AFTER, without template bloat.Do you mean: myAssert(condition, tuple("%d", 5)); That might be useful if myAssert was more complex than just wrapping format(), but in this case it doesn't seem much better. Peter Alexander's solution seems good, i.e. using the compile-time default arguments but with the body forwarding to a non-template function: myAssertBody(condition, format("Assertion failed %s:%d: ", file, line, args)); Presumably the compiler can then optimize out each myAssert instantiation.A related question: In C++ we can stringify arguments and use it to provide informative asserts without duplicating code specifying a string version of the condition: #define assert( isOK ) ( (isOK) ? (void)0 : (void)myAssert(#isOK,__LINE__,__PRETTY_FUNCTION__, __FILE__) ) Likewise for related quick debug functions: #define DEBUG(val) myDEBUG(#val,val) There seems to be no way of doing this currently (besides the ugly mixin(myAssertString("i==0")) which would parse the condition at CT).Maybe we could have something like: void fun(bool condition, string expr = __STRINGIFY(condition)); fun(i==0); // expr = "i==0"
Sep 03 2012
On 03/09/2012 13:51, Nick Treleaven wrote:myAssertBody(condition, format("Assertion failed %s:%d: ", file, line, args));Oops, that is unsafe, fixed: void myAssert(string file = __FILE__, int line = __LINE__, Args...)( lazy bool condition, lazy string messageFormat, lazy Args args) if (args.length > 0) { myAssertBody(condition, xformat("Assertion failed %s:%d: %s", file, line, xformat(messageFormat, args))); }
Sep 03 2012
On Sun, Sep 2, 2012 at 8:46 PM, Peter Alexander <peter.alexander.au gmail.com> wrote:On Sunday, 2 September 2012 at 18:23:48 UTC, Simen Kjaeraas wrote:IIRC, you can even have them before standard template args. Template arguments are much more versatile (or hard-working) than function arguments.void myAssert(int line = __LINE__, string file = __FILE__, Args...)(lazy bool condition, lazy Args args) { if (!condition) { writefln("Assertion failed %s:%d", file, line); writefln(args); exit(1); } }Nice. I thought of that, but for some reason I thought that you couldn't have default args before variadic args.
Sep 02 2012
On 09/02/2012 07:14 PM, Peter Alexander wrote:One thing that's always bothered me is that I have to use std.string.format to produce useful assert messages: assert(x == 1, format("x doesn't equal 1, it is %d", x)); Of course, I tried to solve this by producing my own assert-like function wrapper: void myAssert(Args...)(lazy bool condition, lazy Args args) { assert(condition, format(args)); } That's all good, but the problem now is that I get the wrong file and line in the assert message, so I tried to use the default arg __FILE__ and __LINE__ trick: void myAssert(Args...)(lazy bool condition, lazy Args args, int line = __LINE__, string file = __FILE__) { if (!condition) { writeln("Assertion failed %s:%d", file, line); writefln(args); exit(1); } } But I can't have default arguments because of the variadic arguments! Can anyone think of a way around this?I don't think there is a deep reason why IFTI shouldn't succeed in this case.
Sep 02 2012
On Sunday, 2 September 2012 at 20:30:26 UTC, Timon Gehr wrote:On 09/02/2012 07:14 PM, Peter Alexander wrote:Consider: myAssert(false, "%d", 1); What is Args? "%d", 1 could refer to the optional arguments, or the variadic arguments. Both match.One thing that's always bothered me is that I have to use std.string.format to produce useful assert messages: assert(x == 1, format("x doesn't equal 1, it is %d", x)); Of course, I tried to solve this by producing my own assert-like function wrapper: void myAssert(Args...)(lazy bool condition, lazy Args args) { assert(condition, format(args)); } That's all good, but the problem now is that I get the wrong file and line in the assert message, so I tried to use the default arg __FILE__ and __LINE__ trick: void myAssert(Args...)(lazy bool condition, lazy Args args, int line = __LINE__, string file = __FILE__) { if (!condition) { writeln("Assertion failed %s:%d", file, line); writefln(args); exit(1); } } But I can't have default arguments because of the variadic arguments! Can anyone think of a way around this?I don't think there is a deep reason why IFTI shouldn't succeed in this case.
Sep 02 2012
On Sunday, 2 September 2012 at 23:40:01 UTC, Peter Alexander wrote:Consider: myAssert(false, "%d", 1); What is Args? "%d", 1 could refer to the optional arguments, or the variadic arguments. Both match.Easy: the variadic arguments are not, themselves, optional. Match the variadic. This kind of problem is already solved in every language with "scattering" assignments, and other such features.
Sep 03 2012
On Monday, 3 September 2012 at 09:15:08 UTC, Chris Nicholson-Sauls wrote:On Sunday, 2 September 2012 at 23:40:01 UTC, Peter Alexander wrote:The variadic arguments are optional, as you can have zero arguments count as variadic. Also, how can I override the optional arguments in this function if all arguments are matched as variadic?Consider: myAssert(false, "%d", 1); What is Args? "%d", 1 could refer to the optional arguments, or the variadic arguments. Both match.Easy: the variadic arguments are not, themselves, optional. Match the variadic. This kind of problem is already solved in every language with "scattering" assignments, and other such features.
Sep 03 2012
On Monday, 3 September 2012 at 09:24:42 UTC, Peter Alexander wrote:On Monday, 3 September 2012 at 09:15:08 UTC, Chris Nicholson-Sauls wrote:They are not really optional. That the language fails to enforce this (or provide an eforcement, rather) is a shortcoming. Pretending for a moment that D supports one of two things: 1) Empty array stands in for empty variadic. Then, given arguments like func(R, V..., O=D) you would call it with func(foo, [], bar). Since a typesafe variadic is effectively a sugar-laden array (well a tuple really), I see no reason this shouldn't be feasible. First-class tuples would likely be even better. 2) Named parameters. Then it's func(foo, O: bar), and the variadic argument can indeed be optional, although the meaning is no different than in (1). In reality, though, we have neither of these things. Even so, how often does this actually arise in practice (never has for me)? And given the OP's specific case (__FILE__ and __LINE__ params) I don't foresee any useful case for overriding the defaults. Honestly, I sometimes think that the special treatment of __FILE__ and __LINE__ when used as defaults was not the best way to go. It might've been better implemented as hidden params, only generated by the compiler when actually used in the body.On Sunday, 2 September 2012 at 23:40:01 UTC, Peter Alexander wrote:The variadic arguments are optional, as you can have zero arguments count as variadic. Also, how can I override the optional arguments in this function if all arguments are matched as variadic?Consider: myAssert(false, "%d", 1); What is Args? "%d", 1 could refer to the optional arguments, or the variadic arguments. Both match.Easy: the variadic arguments are not, themselves, optional. Match the variadic. This kind of problem is already solved in every language with "scattering" assignments, and other such features.
Sep 03 2012
On Monday, 3 September 2012 at 11:17:39 UTC, Chris Nicholson-Sauls wrote:1) Empty array stands in for empty variadic. [snip] In reality, though, we have neither of these things. [snip]Turns out, I was quite wrong, and I'm happy to be. Empty array is accepted for typesafe variadics just fine ... I'm not sure now why I thought otherwise. Of course, our tangent is really sort of moot, since the brackets would be required for any pattern with a suffix matching the optional arguments anyhow, at which point there's little point in having the variadic argument. I reiterate my impression that magical __FILE__ and __LINE__ should be provided by some other means.
Sep 03 2012
On 03/09/12 23:48, Chris Nicholson-Sauls wrote:On Monday, 3 September 2012 at 11:17:39 UTC, Chris Nicholson-Sauls wrote:It was a special-case hack to fix a special-case need. It was extremely easy to implement (about 2 hours work) and has been very successful in fixing that need. Everything else that anyone has talked about is at least ten times as complicated, and doesn't seem to offer significant advantages. It's really easy to come up with over-engineered solutions to these sorts of things.1) Empty array stands in for empty variadic. [snip] In reality, though, we have neither of these things. [snip]Turns out, I was quite wrong, and I'm happy to be. Empty array is accepted for typesafe variadics just fine ... I'm not sure now why I thought otherwise. Of course, our tangent is really sort of moot, since the brackets would be required for any pattern with a suffix matching the optional arguments anyhow, at which point there's little point in having the variadic argument. I reiterate my impression that magical __FILE__ and __LINE__ should be provided by some other means.
Sep 04 2012
On Tuesday, 4 September 2012 at 09:24:26 UTC, Don Clugston wrote:On 03/09/12 23:48, Chris Nicholson-Sauls wrote:How difficult would hidden params, triggered by usage, be? Ie: my function makes use of __CALL_FILE and __CALL_LINE variables by name, therefore they are tacked on (as const, presumably) and always passed, thanks to diabolic compiler sorcery. The current solution is fine in a majority of cases, at least so far, but there are going to be moments like what the OP had, and these moments are discouraging especially to newcomers. I won't pretend to know if it would be easy or not; you're a heck of a lot more familiar with the compiler's code than I am. But it certainly seems straightforward.I reiterate my impression that magical __FILE__ and __LINE__ should be provided by some other means.It was a special-case hack to fix a special-case need. It was extremely easy to implement (about 2 hours work) and has been very successful in fixing that need. Everything else that anyone has talked about is at least ten times as complicated, and doesn't seem to offer significant advantages. It's really easy to come up with over-engineered solutions to these sorts of things.
Sep 04 2012
On 04/09/12 11:43, Chris Nicholson-Sauls wrote:On Tuesday, 4 September 2012 at 09:24:26 UTC, Don Clugston wrote:I don't know how that could be done. You need to know the function signature whenever you call the function. If you make the signature dependent on the function body, it cannot work unless you have the source code of the function. The compiler is able to add hidden variables for things like 'this' because it can work out if it is required just by looking at the types involved in the function declaration. But in this case, it can't get it from the signature. The default argument method we are currently using is a nice trick, because even though default arguments aren't part of the function type, the compiler still sees them in the function signature, so it knows to do a bit of magic. It's also easy to get nasty forward reference errors when you do that sort of thing.On 03/09/12 23:48, Chris Nicholson-Sauls wrote:How difficult would hidden params, triggered by usage, be? Ie: my function makes use of __CALL_FILE and __CALL_LINE variables by name, therefore they are tacked on (as const, presumably) and always passed, thanks to diabolic compiler sorcery. The current solution is fine in a majority of cases, at least so far, but there are going to be moments like what the OP had, and these moments are discouraging especially to newcomers.I reiterate my impression that magical __FILE__ and __LINE__ should be provided by some other means.It was a special-case hack to fix a special-case need. It was extremely easy to implement (about 2 hours work) and has been very successful in fixing that need. Everything else that anyone has talked about is at least ten times as complicated, and doesn't seem to offer significant advantages. It's really easy to come up with over-engineered solutions to these sorts of things.I won't pretend to know if it would be easy or not; you're a heck of a lot more familiar with the compiler's code than I am. But it certainly seems straightforward.
Sep 04 2012
On Tuesday, 4 September 2012 at 11:28:23 UTC, Don Clugston wrote:I don't know how that could be done. You need to know the function signature whenever you call the function. If you make the signature dependent on the function body, it cannot work unless you have the source code of the function. The compiler is able to add hidden variables for things like 'this' because it can work out if it is required just by looking at the types involved in the function declaration. But in this case, it can't get it from the signature. The default argument method we are currently using is a nice trick, because even though default arguments aren't part of the function type, the compiler still sees them in the function signature, so it knows to do a bit of magic. It's also easy to get nasty forward reference errors when you do that sort of thing.Fair enough; I hadn't considered the case of having only the signature to go by. Maybe an attribute for it? ;) Okay okay, I give. For now there's making file/line template params, which is fine for something like a custom assertion meant for debug builds, although horrid for release builds.
Sep 05 2012
On Monday, September 03, 2012 11:25:05 Peter Alexander wrote:On Monday, 3 September 2012 at 09:15:08 UTC, Chris Nicholson-Sauls wrote:You can use a template constraint if you want to require that there be at least a certain number of arguments. Also, if you know that there always needs to be at least a certain number of arguments, then you can simply make it so that the required ones are non-variadic. e.g. void func(A, B, C...)(A a, B b, C cs) { ... }On Sunday, 2 September 2012 at 23:40:01 UTC, Peter Alexander wrote:The variadic arguments are optional, as you can have zero arguments count as variadic.Consider: myAssert(false, "%d", 1); What is Args? "%d", 1 could refer to the optional arguments, or the variadic arguments. Both match.Easy: the variadic arguments are not, themselves, optional. Match the variadic. This kind of problem is already solved in every language with "scattering" assignments, and other such features.Also, how can I override the optional arguments in this function if all arguments are matched as variadic?Use template constraints. - Jonathan M Davis
Sep 03 2012