www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Choosing arity of a template function

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
A generic function receives an argument called "partition" by alias. 
That may work in one of the following ways:

partition(range);

partition!less(range);

partition!less(range, n); // n is a number

I tried this:

static if (is(partition == function) || is(partition == delegate))
   partition(r);
else if (__traits(compiles, partition!less(r, n)))
   partition!less(r, n);
else
   partition!less(r);

The first test works very nice. The second does not; the compiler 
attempts to instantiate the template wrongly and spits a bunch of errors 
before giving up, in spite of the whole "let me know silently whether 
this compiles" thing.

So, what's an elegant solution to this? I looked up std.traits but 
nothing seems to help. (Tried arity, no avail.)


Thanks,

Andrei
Feb 26 2016
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 02/26/2016 06:09 PM, Andrei Alexandrescu wrote:
 A generic function receives an argument called "partition" by alias.
 That may work in one of the following ways:

 partition(range);

 partition!less(range);

 partition!less(range, n); // n is a number

 I tried this:

 static if (is(partition == function) || is(partition == delegate))
    partition(r);
 else if (__traits(compiles, partition!less(r, n)))
    partition!less(r, n);
 else
    partition!less(r);

 The first test works very nice. The second does not; the compiler
 attempts to instantiate the template wrongly and spits a bunch of errors
 before giving up, in spite of the whole "let me know silently whether
 this compiles" thing.

 So, what's an elegant solution to this? I looked up std.traits but
 nothing seems to help. (Tried arity, no avail.)
Urgh, forgot the "static" in front of the second "if". It does work now. Nevertheless, I'm still on lookout for a more elegant solution! I have this mindset that using __traits(compiles) is some sort of cheating. Andrei
Feb 26 2016
next sibling parent reply Era Scarecrow <rtcvb32 yahoo.com> writes:
On Friday, 26 February 2016 at 23:11:32 UTC, Andrei Alexandrescu 
wrote:
 Urgh, forgot the "static" in front of the second "if". It does 
 work now.
Perhaps that should be an error instead; Going from a static if to an else if... seems easy enough to spot and insist a fix (much like assignment inside an if statement is illegal).
Feb 26 2016
parent reply cym13 <cpicard openmailbox.org> writes:
On Friday, 26 February 2016 at 23:18:30 UTC, Era Scarecrow wrote:
 On Friday, 26 February 2016 at 23:11:32 UTC, Andrei 
 Alexandrescu wrote:
 Urgh, forgot the "static" in front of the second "if". It does 
 work now.
Perhaps that should be an error instead; Going from a static if to an else if... seems easy enough to spot and insist a fix (much like assignment inside an if statement is illegal).
What about automatically inferring it? It sounds reasonnable, much like saying that "static" actually is for the whole if/elseif block.
Feb 26 2016
parent reply Chris Wright <dhasenan gmail.com> writes:
On Fri, 26 Feb 2016 23:46:11 +0000, cym13 wrote:

 On Friday, 26 February 2016 at 23:18:30 UTC, Era Scarecrow wrote:
 On Friday, 26 February 2016 at 23:11:32 UTC, Andrei Alexandrescu wrote:
 Urgh, forgot the "static" in front of the second "if". It does work
 now.
Perhaps that should be an error instead; Going from a static if to an else if... seems easy enough to spot and insist a fix (much like assignment inside an if statement is illegal).
What about automatically inferring it? It sounds reasonnable, much like saying that "static" actually is for the whole if/elseif block.
static if (oggSupportEnabled) playOggFile(); else if (config.loggingEnabled) info("ogg support not enabled; skipping playback"); So, no, unless you want to make curly braces mandatory for conditional bodies.
Feb 26 2016
parent reply Era Scarecrow <rtcvb32 yahoo.com> writes:
On Friday, 26 February 2016 at 23:53:06 UTC, Chris Wright wrote:
 On Fri, 26 Feb 2016 23:46:11 +0000, cym13 wrote:
 On Friday, 26 February 2016 at 23:18:30 UTC, Era Scarecrow 
 wrote:
 On Friday, 26 February 2016 at 23:11:32 UTC, Andrei 
 Alexandrescu wrote:
 Urgh, forgot the "static" in front of the second "if". It 
 does work now.
Perhaps that should be an error instead; Going from a static if to an else if... seems easy enough to spot and insist a fix (much like assignment inside an if statement is illegal).
What about automatically inferring it? It sounds reasonnable, much like saying that "static" actually is for the whole if/elseif block.
static if (oggSupportEnabled) playOggFile(); else if (config.loggingEnabled) info("ogg support not enabled; skipping playback"); So, no, unless you want to make curly braces mandatory for conditional bodies.
Only for switching between static and non-static code. Besides with the static if's, 1 level of bracing doesn't make a new scope anyways (if i remember correctly).
Feb 26 2016
parent Chris Wright <dhasenan gmail.com> writes:
On Sat, 27 Feb 2016 01:25:31 +0000, Era Scarecrow wrote:

 On Friday, 26 February 2016 at 23:53:06 UTC, Chris Wright wrote:
 On Fri, 26 Feb 2016 23:46:11 +0000, cym13 wrote:
 On Friday, 26 February 2016 at 23:18:30 UTC, Era Scarecrow wrote:
 On Friday, 26 February 2016 at 23:11:32 UTC, Andrei Alexandrescu
 wrote:
 Urgh, forgot the "static" in front of the second "if". It does work
 now.
Perhaps that should be an error instead; Going from a static if to an else if... seems easy enough to spot and insist a fix (much like assignment inside an if statement is illegal).
What about automatically inferring it? It sounds reasonnable, much like saying that "static" actually is for the whole if/elseif block.
static if (oggSupportEnabled) playOggFile(); else if (config.loggingEnabled) info("ogg support not enabled; skipping playback"); So, no, unless you want to make curly braces mandatory for conditional bodies.
Only for switching between static and non-static code. Besides with the static if's, 1 level of bracing doesn't make a new scope anyways (if i remember correctly).
This would be a great thing for a lint tool to check, but for a language change, it's breaking, and the justification is a bit short.
Feb 26 2016
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, 26 February 2016 at 23:11:32 UTC, Andrei Alexandrescu 
wrote:
 Urgh, forgot the "static" in front of the second "if". It does 
 work now. Nevertheless, I'm still on lookout for a more elegant 
 solution! I have this mindset that using __traits(compiles) is 
 some sort of cheating.
There's nothing cheating about using __traits(compiles). It's basically the same as using is(typeof(foo)) and is(typeof({statement;})), albeit arguably a bit more explicit about the fact that it's testing what compiles. And there are plenty of cases where one of those is exactly what code should be doing. Now, if it's a test that needs to be done frequently, then it makes sense to create a wrapper for it that makes using it cleaner, so I think that you're right in the sense that we should be looking to have reusable traits to test with rather than using __traits(compiles) or is(typeof(blah)) heavily, but they're still fine to use when the occasion calls for it - especially if the test in question isn't something that's going to need to be done in much code. So, to a great extent, the question is whether what you're trying to do here is something that very many folks are going to want to do, and if it is, then we should find a way to do it without __traits(compiles), but if not, then I'm not sure that I'd worry much about it. - Jonathan M Davis
Feb 26 2016
next sibling parent Meta <jared771 gmail.com> writes:
It isn't cheating but IMO it's bad form. If you can do it without 
__traits(compiles) (or is(typeof()), etc.) you should, because 
there are many reasons why something will not compile, and only 
one of those reasons is the one you want to know. If there's no 
way around using it, you should still try to limit what is passed 
to __traits(compiles).

enum hasFront(T) = __traits(compiles, { auto _ = T.init.font; });

It's fairly easy to spot such a typo when it's only one line, but 
the risk of having some other compiler error being the reason 
your static if branch isn't taken grows very quickly with each 
additional line or bit of complexity.
Feb 26 2016
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 27.02.2016 01:03, Jonathan M Davis wrote:
 There's nothing cheating about using __traits(compiles). It's basically
 the same as using is(typeof(foo)) and is(typeof({statement;})), albeit
 arguably a bit more explicit about the fact that it's testing what compiles.
The two are subtly different and only __traits(compiles,...) reliably checks for compilability. Never use is(typeof(...)) unless you know exactly what you are doing. void main(){ int x; static void foo(){ static assert(is(typeof({return x;}))); static assert(!__traits(compiles,{return x;})); //auto a={return x;}; // error } }
Feb 27 2016
prev sibling parent reply Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
On Friday, 26 February 2016 at 23:11:32 UTC, Andrei Alexandrescu 
wrote:
 On 02/26/2016 06:09 PM, Andrei Alexandrescu wrote:
 static if (is(partition == function) || is(partition == 
 delegate))
    partition(r);
 else if (__traits(compiles, partition!less(r, n)))
    partition!less(r, n);
 else
    partition!less(r);
Nevertheless, I'm still on lookout for a more elegant solution! I have this mindset that using __traits(compiles) is some sort of cheating.
I don't see it as cheating; in fact it is more elegant, because it checks for exactly the thing you depend on, namely that `partition` is callable. Your explicit check above rejects structs with opCall(), for example, which is probably not what you intended.
Feb 27 2016
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 02/27/2016 07:38 AM, Marc Sch├╝tz wrote:
 I don't see it as cheating; in fact it is more elegant, because it
 checks for exactly the thing you depend on, namely that `partition` is
 callable. Your explicit check above rejects structs with opCall(), for
 example, which is probably not what you intended.
Good point. Unrelated: I also noticed that if the template has an error inside, the error messages when using it with __traits(compiles) are mightily confusing. -- Andrei
Feb 27 2016
prev sibling next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 2/26/2016 3:09 PM, Andrei Alexandrescu wrote:
 (Tried arity, no avail.)
Get the type of the function, the tuple of its parameter types, and the .length of that tuple.
Feb 27 2016
prev sibling next sibling parent Dicebot <public dicebot.lv> writes:
On 02/27/2016 01:09 AM, Andrei Alexandrescu wrote:
 static if (is(partition == function) || is(partition == delegate))
   partition(r);
 else if (__traits(compiles, partition!less(r, n)))
   partition!less(r, n);
 else
   partition!less(r);
 
 The first test works very nice. The second does not; the compiler
 attempts to instantiate the template wrongly and spits a bunch of errors
 before giving up, in spite of the whole "let me know silently whether
 this compiles" thing.
 
 So, what's an elegant solution to this? I looked up std.traits but
 nothing seems to help. (Tried arity, no avail.)
Untested: bool hasArityOverload ( alias F ) ( ) { import std.traits; if (!isSomeFunction!(F!less)) return false; alias ParamTypes = Parameters!(F!less); if (ParamTypes.length != 2) return false; return isInputRange!(ParamTypes[0]) && is(ParamTypes[0] : uint); } Much more verbose but kess chance of accidental passing/failing by unrelated reasons. In "casual" application code I'd still probably go with `__traits(compiles)`.
Feb 29 2016
prev sibling parent reply Meta <jared771 gmail.com> writes:
On Friday, 26 February 2016 at 23:09:52 UTC, Andrei Alexandrescu 
wrote:
 So, what's an elegant solution to this? I looked up std.traits 
 but nothing seems to help. (Tried arity, no avail.)
There's this[1] PR which implements exactly what you want, but it's currently not passing the auto-tester. 1. https://github.com/D-Programming-Language/dmd/pull/5201
Mar 03 2016
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 03/03/2016 02:54 PM, Meta wrote:
 On Friday, 26 February 2016 at 23:09:52 UTC, Andrei Alexandrescu wrote:
 So, what's an elegant solution to this? I looked up std.traits but
 nothing seems to help. (Tried arity, no avail.)
There's this[1] PR which implements exactly what you want, but it's currently not passing the auto-tester. 1. https://github.com/D-Programming-Language/dmd/pull/5201
Thanks! That will push introspection considerably further. I toggled auto-pull. -- Andrei
Mar 03 2016
next sibling parent Meta <jared771 gmail.com> writes:
On Thursday, 3 March 2016 at 21:06:12 UTC, Andrei Alexandrescu 
wrote:
 Thanks! That will push introspection considerably further. I 
 toggled auto-pull. -- Andrei
I would recommend cancelling that as it's currently failing on Linux and FreeBSD.
Mar 03 2016
prev sibling parent Meta <jared771 gmail.com> writes:
On Thursday, 3 March 2016 at 21:06:12 UTC, Andrei Alexandrescu 
wrote:
 Thanks! That will push introspection considerably further. I 
 toggled auto-pull. -- Andrei
I made the simplest change possible to get it to compile, PR here[1]. It adds an extra allocation so any suggestions on eliminating that are welcome. 1. https://github.com/D-Programming-Language/dmd/pull/5496
Mar 03 2016