digitalmars.D.learn - Best way of checking for a templated function instantiation
- Arafel (23/23) Aug 10 2016 Hi,
- Nicholas Wilson (3/11) Aug 10 2016 static assert(is(typeof(S()+42)));
- Meta (12/35) Aug 10 2016 __traits(compiles) is pretty much the only way to do it. For
- Meta (4/6) Aug 10 2016 Made a typo, this should be:
- Arafel (7/13) Aug 10 2016 Hi!
- Jonathan M Davis via Digitalmars-d-learn (4/19) Aug 10 2016 __traits(allMembers, S) would give "opBinary", so I don't think so. You ...
- Meta (4/19) Aug 10 2016 Unfortunately you're stuck using __traits(compiles) as
- Jonathan M Davis via Digitalmars-d-learn (18/41) Aug 10 2016 I'd advise against checking for opBinary in general, because it'll only ...
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!"-")); } ---
Aug 10 2016
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)));
Aug 10 2016
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
Aug 10 2016
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); }));
Aug 10 2016
On Wednesday, 10 August 2016 at 13:40:30 UTC, Meta wrote:On Wednesday, 10 August 2016 at 13:37:47 UTC, Meta wrote: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!\"+\"")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); }));
Aug 10 2016
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:__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 DavisOn Wednesday, 10 August 2016 at 13:37:47 UTC, Meta wrote: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!\"+\"")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); }));
Aug 10 2016
On Wednesday, 10 August 2016 at 13:57:54 UTC, Arafel wrote:On Wednesday, 10 August 2016 at 13:40:30 UTC, Meta wrote:Unfortunately you're stuck using __traits(compiles) as `opBinary!"+"` is not a symbol. static assert(__traits(compiles, { alias _ = S.opBinary!"+"; }));On Wednesday, 10 August 2016 at 13:37:47 UTC, Meta wrote: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!\"+\"")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); }));
Aug 10 2016
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
Aug 10 2016