Thread overview | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
September 17, 2016 Ah, simple solution to unittests inside templates | ||||
---|---|---|---|---|
| ||||
Recall the discussion a few days ago about unittests inside templates being instantiated with the template. Often that's desirable, but sometimes not - for example when you want to generate nice ddoc unittests and avoid bloating. For those cases, here's a simple solution that I don't think has been mentioned: /** Awesome struct */ struct Awesome(T) { /** Awesome function. */ void awesome() {} /// static if (is(T == int)) unittest { Awesome awesome; awesome.awesome; } } The unittest documentation is nicely generated. The unittest code itself is only generated for one instantiation. Andrei |
September 17, 2016 Re: Ah, simple solution to unittests inside templates | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Saturday, 17 September 2016 at 17:22:52 UTC, Andrei Alexandrescu wrote:
> The unittest documentation is nicely generated. The unittest code itself is only generated for one instantiation.
I had a similar thought, but I didn't really like verbosity of the static if.
I think at some point someone suggested we could implement explicit support for such unittests via `static unittest`:
/** Awesome struct */
struct Awesome(T)
{
/** Awesome function. */
void awesome() {}
///
static unittest
{
Awesome!int awesome;
awesome.awesome;
}
}
A static unittest would not have implicit access to anything internal in the aggregation (this is different to your solution where the unittest has access to all fields of the Awesome!int instance).
Note the difference inside the unittest: it uses `Awesome!int awesome` instead of the previous `Awesome awesome`. In your case it implicitly refers to the Awesome!int instantiation, however you're actually presenting uncompilable code to the user! The user might copy-paste it into a main function but it would fail to build.
So the feature might have some merit as it would avoid generating docs with code which may not even work.
|
September 18, 2016 Re: Ah, simple solution to unittests inside templates | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrej Mitrovic | On Saturday, September 17, 2016 21:23:58 Andrej Mitrovic via Digitalmars-d wrote: > On Saturday, 17 September 2016 at 17:22:52 UTC, Andrei > > Alexandrescu wrote: > > The unittest documentation is nicely generated. The unittest code itself is only generated for one instantiation. > > I had a similar thought, but I didn't really like verbosity of the static if. It also has the downside that the unit tests still end up in the code of anyone using the template. It's now only when they use the template with that particular instantiation, so it's definitely not as bad as just putting the unittest block inside the templated type like you would if it weren't templated, but it's still definitely worse than what you get with a non-templated type. > I think at some point someone suggested we could implement explicit support for such unittests via `static unittest`: > > /** Awesome struct */ > struct Awesome(T) > { > /** Awesome function. */ > void awesome() {} > > /// > static unittest > { > Awesome!int awesome; > awesome.awesome; > } > } > > A static unittest would not have implicit access to anything internal in the aggregation (this is different to your solution where the unittest has access to all fields of the Awesome!int instance). > > Note the difference inside the unittest: it uses `Awesome!int awesome` instead of the previous `Awesome awesome`. In your case it implicitly refers to the Awesome!int instantiation, however you're actually presenting uncompilable code to the user! The user might copy-paste it into a main function but it would fail to build. > > So the feature might have some merit as it would avoid generating docs with code which may not even work. Yes. That's DIP 82: http://wiki.dlang.org/DIP82 I need to go over it again and then introduce it into the new DIP process. But I really think that that's where we should go to fix this problem. It makes for a clean solution that allows you to easily choose whether a unittest block inside of a template is treated as part of the template or whether it's just syntactically inside the template so that it can be used for documentation and so that it can be next to what it's testing, but it's not actually part of the template semantically. - Jonathan M Davis |
September 18, 2016 Re: Ah, simple solution to unittests inside templates | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Saturday, 17 September 2016 at 17:22:52 UTC, Andrei Alexandrescu wrote:
> ///
> static if (is(T == int)) unittest
> {
> Awesome awesome;
> awesome.awesome;
> }
> }
>
> The unittest documentation is nicely generated. The unittest code itself is only generated for one instantiation.
Besides the other comments, we still have to instantiate Awesome!int somewhere for the tests to run, which could be forgotten or improperly done, failing silently. (Also int is arbitrary, unhelpful for the uninitiated).
|
September 18, 2016 Re: Ah, simple solution to unittests inside templates | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Saturday, 17 September 2016 at 17:22:52 UTC, Andrei Alexandrescu wrote: > Recall the discussion a few days ago about unittests inside templates being instantiated with the template. Often that's desirable, but sometimes not - for example when you want to generate nice ddoc unittests and avoid bloating. For those cases, here's a simple solution that I don't think has been mentioned: > > /** Awesome struct */ > struct Awesome(T) > { > /** Awesome function. */ > void awesome() {} > > /// > static if (is(T == int)) unittest > { > Awesome awesome; > awesome.awesome; > } > } > > The unittest documentation is nicely generated. The unittest code itself is only generated for one instantiation. > > > Andrei This solution is used extensively by ndslice [1] and I agree that it's quite flexible. [1] http://forum.dlang.org/post/mailman.166.1472923003.2965.digitalmars-d@puremagic.com#post-psrgjdlvsiukkuhrekoo:40forum.dlang.org |
September 18, 2016 Re: Ah, simple solution to unittests inside templates | ||||
---|---|---|---|---|
| ||||
Posted in reply to ZombineDev | On Sunday, 18 September 2016 at 11:10:22 UTC, ZombineDev wrote: > On Saturday, 17 September 2016 at 17:22:52 UTC, Andrei Alexandrescu wrote: >> [...] > > This solution is used extensively by ndslice [1] and I agree that it's quite flexible. > > [1]: http://forum.dlang.org/post/mailman.166.1472923003.2965.digitalmars-d@puremagic.com#post-psrgjdlvsiukkuhrekoo:40forum.dlang.org I meant: http://forum.dlang.org/post/psrgjdlvsiukkuhrekoo@forum.dlang.org |
September 18, 2016 Re: Ah, simple solution to unittests inside templates | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrej Mitrovic | On 9/17/16 5:23 PM, Andrej Mitrovic wrote:
> I think at some point someone suggested we could implement explicit
> support for such unittests via `static unittest`:
That suggests the unittest shall be evaluated during compilation. -- Andrei
|
September 18, 2016 Re: Ah, simple solution to unittests inside templates | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | On 9/18/16 6:00 AM, Jonathan M Davis via Digitalmars-d wrote:
> Yes. That's DIP 82:
>
> http://wiki.dlang.org/DIP82
>
> I need to go over it again and then introduce it into the new DIP process.
> But I really think that that's where we should go to fix this problem.
Just a thought: things that we can't do have high priority. Things that we can do with a modest cost are much less attractive. Consider:
struct Awesome(A, B, C)
{
private enum ut = is(A == int) && is(B == int) && is(C == int);
static if (ut) unittest
{
...
}
}
You're looking at an overhead with a small fixed cost plus a few characters ("if (ut)") per unittest.
Andrei
|
September 18, 2016 Re: Ah, simple solution to unittests inside templates | ||||
---|---|---|---|---|
| ||||
Posted in reply to Nick Treleaven | On 9/18/16 6:23 AM, Nick Treleaven wrote:
> Besides the other comments, we still have to instantiate Awesome!int
> somewhere for the tests to run, which could be forgotten or improperly
> done, failing silently. (Also int is arbitrary, unhelpful for the
> uninitiated).
I don't see that as much of a hurdle seeing as any template written has a few "obvious" types it'll work with. To encapsulate that if needed:
struct Awesome(A, B, C)
{
private enum ut = is(A == int) && is(B == int) && is(C == int);
unittest { alias Awe = Awesome!(int, int, int); }
static if (ut) unittest
{
...
}
}
Andrei
|
September 18, 2016 Re: Ah, simple solution to unittests inside templates | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Sunday, 18 September 2016 at 12:02:47 UTC, Andrei Alexandrescu wrote: > On 9/17/16 5:23 PM, Andrej Mitrovic wrote: >> I think at some point someone suggested we could implement explicit >> support for such unittests via `static unittest`: > > That suggests the unittest shall be evaluated during compilation. -- Andrei static as in static function. Not much better imo. Your solution with static if is fine (ish) , but ugly and a pain for some templates that only accept relatively complicated types. Also requires an external unittest to be kept in sync to ensure that the template is ever instantiated with a those arguments. What would be really good would be to have a way to make ddoc associate a unittest with a particular symbol, regardless of location. See e.g. https://github.com/dlang/phobos/pull/4043 where I have to pull all the unittests out of the template in order to get the win 32 tester to pass (fails saying "too many symbols" otherwise), but that ruins the documentation. |
Copyright © 1999-2021 by the D Language Foundation