Thread overview | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
August 10, 2016 Best way of checking for a templated function instantiation | ||||
---|---|---|---|---|
| ||||
Hi, I'm trying to check at compilation time if a given type implements some operator (let's assume it's '+' in this case), without caring about the type of the parameters it accepts. Since operator overloading is expressed in D through templated functions, what is the preferred way of checking if a template is / can be instantiated with a given parameter list? So far I've come with a solution using __trait(compiles, ...), but perhaps it's not 100% reliable -I'm no expert in template wizardry-, or there are better options. I also tried with hasMember, but it apparantly only shows that "opBinary" is indeed present, but nothing more: --- void main() { struct S { int opBinary(string op)(int i) if (op == "+") { return 0; } } static assert(__traits(compiles, S.opBinary!"+")); static assert(!__traits(compiles, S.opBinary!"-")); } --- |
August 10, 2016 Re: Best way of checking for a templated function instantiation | ||||
---|---|---|---|---|
| ||||
Posted in reply to Arafel | On Wednesday, 10 August 2016 at 12:36:14 UTC, Arafel wrote:
> Hi,
>
> I'm trying to check at compilation time if a given type implements some operator (let's assume it's '+' in this case), without caring about the type of the parameters it accepts. Since operator overloading is expressed in D through templated functions, what is the preferred way of checking if a template is / can be instantiated with a given parameter list?
>
> [...]
static assert(is(typeof(S()+42)));
static assert(!is(typeof(S()-42)));
|
August 10, 2016 Re: Best way of checking for a templated function instantiation | ||||
---|---|---|---|---|
| ||||
Posted in reply to Arafel | On Wednesday, 10 August 2016 at 12:36:14 UTC, Arafel wrote: > Hi, > > I'm trying to check at compilation time if a given type implements some operator (let's assume it's '+' in this case), without caring about the type of the parameters it accepts. Since operator overloading is expressed in D through templated functions, what is the preferred way of checking if a template is / can be instantiated with a given parameter list? > > So far I've come with a solution using __trait(compiles, ...), but perhaps it's not 100% reliable -I'm no expert in template wizardry-, or there are better options. I also tried with hasMember, but it apparantly only shows that "opBinary" is indeed present, but nothing more: > > --- > void main() { > struct S { > int opBinary(string op)(int i) if (op == "+") { > return 0; > } > } > > static assert(__traits(compiles, S.opBinary!"+")); > static assert(!__traits(compiles, S.opBinary!"-")); > } > --- __traits(compiles) is pretty much the only way to do it. For example, if you want to check that S supports opBinary!"+"(int): static assert(__traits(compiles, auto _ = S.init.opBinary!"+"(int.init)); And if you want to check that the return type is int or implicitly converts to int, you can change the `auto _ = ...` to `int _ = ...`. However, if you want to be more explicit about it, you can do the following: import std.traits; static assert(is(ReturnType!(S.opBinary!"+") == int)); //Change to `... : int` for implicit conversion checking |
August 10, 2016 Re: Best way of checking for a templated function instantiation | ||||
---|---|---|---|---|
| ||||
Posted in reply to Meta | On Wednesday, 10 August 2016 at 13:37:47 UTC, Meta wrote:
> static assert(__traits(compiles, auto _ = S.init.opBinary!"+"(int.init));
Made a typo, this should be:
static assert(__traits(compiles, { auto _ = S.init.opBinary!"+"(int.init); }));
|
August 10, 2016 Re: Best way of checking for a templated function instantiation | ||||
---|---|---|---|---|
| ||||
Posted in reply to Meta | On Wednesday, 10 August 2016 at 13:40:30 UTC, Meta wrote:
> On Wednesday, 10 August 2016 at 13:37:47 UTC, Meta wrote:
>> static assert(__traits(compiles, auto _ = S.init.opBinary!"+"(int.init));
>
> Made a typo, this should be:
>
> static assert(__traits(compiles, { auto _ = S.init.opBinary!"+"(int.init); }));
Hi!
Thanks, that would do! Just out of curiosity, would there be any way to check just that the function is defined, like what "hasMember" would do, without caring about argument number, types, etc.? Ideally something like:
__traits(hasMember, S, "opBinary!\"+\"")
|
August 10, 2016 Re: Best way of checking for a templated function instantiation | ||||
---|---|---|---|---|
| ||||
Posted in reply to Arafel | On Wednesday, August 10, 2016 12:36:14 Arafel via Digitalmars-d-learn wrote:
> Hi,
>
> I'm trying to check at compilation time if a given type implements some operator (let's assume it's '+' in this case), without caring about the type of the parameters it accepts. Since operator overloading is expressed in D through templated functions, what is the preferred way of checking if a template is / can be instantiated with a given parameter list?
>
> So far I've come with a solution using __trait(compiles, ...), but perhaps it's not 100% reliable -I'm no expert in template wizardry-, or there are better options. I also tried with hasMember, but it apparantly only shows that "opBinary" is indeed present, but nothing more:
>
> ---
> void main() {
> struct S {
> int opBinary(string op)(int i) if (op == "+") {
> return 0;
> }
> }
>
> static assert(__traits(compiles, S.opBinary!"+"));
> static assert(!__traits(compiles, S.opBinary!"-"));
> }
> ---
I'd advise against checking for opBinary in general, because it'll only work with user-defined types, whereas a built-in type may define the operator. It's usually better to test that the operator works rather than that opBinary exists. So, you end up with checks like __traits(compiles, lhs + rhs) or __traits(compiles, T.init + T.init). If you're picky about the return type, you can do stuff like __traits(compiles, T t = T.init + T.init) (that might require braces around the statement along with a semicolon; I don't remember). If you test for it enough, you can create an eponymous template that wraps the test so that you just have to do something like hasAdd!T or hasBinaryOp!("+", T). That's basically what you get with traits like isInputRange. They're eponymous templates that wrap tests that use is(typeof(...)) or __traits(compiles, ...) to test that a particular block of code or expression compiles, but wrapping that in an eponymous template makes it more idiomatic and makes it so that you don't have to duplicate the implementation of the check everywhere (which is important if the check isn't simple).
- Jonathan M Davis
|
August 10, 2016 Re: Best way of checking for a templated function instantiation | ||||
---|---|---|---|---|
| ||||
Posted in reply to Arafel | On Wednesday, August 10, 2016 13:57:54 Arafel via Digitalmars-d-learn wrote:
> On Wednesday, 10 August 2016 at 13:40:30 UTC, Meta wrote:
> > On Wednesday, 10 August 2016 at 13:37:47 UTC, Meta wrote:
> >> static assert(__traits(compiles, auto _ =
> >> S.init.opBinary!"+"(int.init));
> >
> > Made a typo, this should be:
> >
> > static assert(__traits(compiles, { auto _ =
> > S.init.opBinary!"+"(int.init); }));
>
> Hi!
>
> Thanks, that would do! Just out of curiosity, would there be any way to check just that the function is defined, like what "hasMember" would do, without caring about argument number, types, etc.? Ideally something like:
>
> __traits(hasMember, S, "opBinary!\"+\"")
__traits(allMembers, S) would give "opBinary", so I don't think so. You can check that the type defined an overloaded binary operator but not which one.
- Jonathan M Davis
|
August 10, 2016 Re: Best way of checking for a templated function instantiation | ||||
---|---|---|---|---|
| ||||
Posted in reply to Arafel | On Wednesday, 10 August 2016 at 13:57:54 UTC, Arafel wrote:
> On Wednesday, 10 August 2016 at 13:40:30 UTC, Meta wrote:
>> On Wednesday, 10 August 2016 at 13:37:47 UTC, Meta wrote:
>>> static assert(__traits(compiles, auto _ = S.init.opBinary!"+"(int.init));
>>
>> Made a typo, this should be:
>>
>> static assert(__traits(compiles, { auto _ = S.init.opBinary!"+"(int.init); }));
>
> Hi!
>
> Thanks, that would do! Just out of curiosity, would there be any way to check just that the function is defined, like what "hasMember" would do, without caring about argument number, types, etc.? Ideally something like:
>
> __traits(hasMember, S, "opBinary!\"+\"")
Unfortunately you're stuck using __traits(compiles) as `opBinary!"+"` is not a symbol.
static assert(__traits(compiles, { alias _ = S.opBinary!"+"; }));
|
Copyright © 1999-2021 by the D Language Foundation