www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to write a counterpart to C++ std::invoke that works with both

reply 60rntogo <60rntogo gmail.com> writes:
I tried this:

---
auto invoke(alias fun, Args...)(Args args)
{
   return fun(args);
}
---

which works with free functions:

---
int add(int a, int b)
{
   return a + b;
}

assert(invoke!add(1, 2) == 3);
---

but with a method:

---
struct Foo
{
   bool isValid(int a)
   {
     return a > 0;
   }
}

auto foo = Foo();
assert(invoke!isValid(foo, 3));
---

I get the error "undefined identifier isValid". How can I make 
this work?
Sep 26 2020
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Saturday, 26 September 2020 at 22:58:44 UTC, 60rntogo wrote:
 I get the error "undefined identifier isValid". How can I make 
 this work?
This part is easy, you need to give the name like assert(invoke!(Foo.isValid)(foo, 3)); Now, the other part is tricky, and a new feature just released this week is there to help: https://dlang.org/changelog/2.094.0.html#add_traits_child So you use this to branch on static vs non-static functions: auto invoke(alias fun, Args...)(Args args) { static if(__traits(isStaticFunction, fun)) return fun(args); else return __traits(child, args[0], fun)(args[1 .. $]); } So, for static functions, you won't be passing a `this`, so it just sends the args straight in. But for non-static functions, you use the now trait to attach `this` to the given function, then pass the rest of the arguments normally. As a result, you `add` works fine, and `assert(invoke!(Foo.isValid)(foo, 3));` now works too!
Sep 26 2020
parent reply 60rntogo <60rntogo gmail.com> writes:
On Saturday, 26 September 2020 at 23:23:13 UTC, Adam D. Ruppe 
wrote:
 As a result, you `add` works fine
I'm afraid not. __traits(isStaticFunction, add) is false, I think it's because it checks if it is a static member function of some struct/class. How would I check if it is actually a free function? I tried isAggregateType!(__traits(parent, add)) but this doesn't even compile since I defined add inside my main function and __traits(parent, add) evaluates to main, but isAggregateType expects a type as an argument.
Sep 26 2020
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Sunday, 27 September 2020 at 05:22:36 UTC, 60rntogo wrote:
 How would I check if it is actually a free function?

 but this doesn't even compile since I defined add inside my 
 main function
aaaaah that's not a free function!! That's a nested function and thus actually has a hidden argument. Of course, you could add `static` to it if it doesn't use other local variables, then it will work again. But to handle nested functions, you can simply add a check for isNested too. Bringing me to this: ----- auto invoke(alias fun, Args...)(Args args) { static if(__traits(isStaticFunction, fun) || __traits(isNested, fun)) return fun(args); else return __traits(child, args[0], fun)(args[1 .. $]); } // I think the above covers all the cases, what follows // are just some tests of various situations. struct Foo { bool isValid(int a) { return a > 0; } struct Bar { void bar() {} } Bar bar; } int add3(int a, int b) { return a + b; } class A { void test() {} int item; class B { void other(int arg) { import std.stdio; writeln(item, " nested ", arg); } } static class C { void cool(string) {} } } void main() { auto a = new A; a.item = 30; invoke!(A.C.cool)(new A.C, "ok"); invoke!(A.B.other)(a.new B, 6); invoke!(A.test)(a); invoke!(Foo.Bar.bar)(Foo.Bar.init); int add(int a, int b) { return a + b; } int add2(int a, int b) { return a + b; } assert(invoke!add(1, 2) == 3); assert(invoke!add2(1, 2) == 3); assert(invoke!add3(1, 2) == 3); auto foo = Foo(); assert(invoke!(Foo.isValid)(foo, 3)); } -------
Sep 28 2020
parent 60rntogo <60rntogo gmail.com> writes:
On Tuesday, 29 September 2020 at 01:19:48 UTC, Adam D. Ruppe 
wrote:
 On Sunday, 27 September 2020 at 05:22:36 UTC, 60rntogo wrote:
 How would I check if it is actually a free function?

 but this doesn't even compile since I defined add inside my 
 main function
aaaaah that's not a free function!!
OK, I see. That makes sense I suppose. Thanks for putting this together, but I'm afraid that I managed to break it again and this time it is really baffling. --- module foo; auto invoke(alias fun, Args...)(Args args) { static if(__traits(isStaticFunction, fun) || __traits(isNested, fun)) return fun(args); else return __traits(child, args[0], fun)(args[1 .. $]); } unittest { import std : approxEqual; assert(invoke!approxEqual(2.0, 2.001)); } --- This gives me these errors: --- source/foo.d(6,48): Error: aggregate or function expected instead of approxEqual(T, U, V)(T value, U reference, V maxRelDiff = 0.01, V maxAbsDiff = 1e-05) source/foo.d(9,41): Error: template std.math.approxEqual cannot deduce function from argument types !()(double), candidates are: /usr/include/dmd/phobos/std/math.d(8479,6): approxEqual(T, U, V)(T value, U reference, V maxRelDiff = 0.01, V maxAbsDiff = 1e-05) source/foo.d(15,28): Error: template instance foo.invoke!(approxEqual, double, double) error instantiating --- Confusingly, these errors disappear if I include the unit test in my main file (i.e., just add it to the code you posted) rather than foo.d or if I use a function like add defined in the module instead of imported approxEqual. Any ideas?
Oct 04 2020