Re: Calling template member function?

2022-04-19 Thread Steven Schveighoffer via Digitalmars-d-learn

On 4/19/22 8:44 PM, Andrey Zherikov wrote:

On Tuesday, 19 April 2022 at 20:29:01 UTC, Steven Schveighoffer wrote:
You can work around the dual context, if you are OK with passing the 
second context explicitly.


The easiest way is to move the member function to a UFCS function. an 
example:


```d
struct X
{
   int x;
   void applyToX(alias fn)() {fn(x);}
}

void applyToX_alt(alias fn)(ref X xval) {
   fn(xval.x);
}

void main()
{
   auto s = X(5);
   int y = 6;
   void fn(ref int x) { x += y; }
   s.applyToX!fn; // error, dual context needed
   s.applyToX_alt!fn; // fine, only single context needed
}
```


I used struct to understand the problem. I don't actually have an object 
context to pass like in your example, the only context I have is 
template parameters.




That's because your code is not an example of the dual context problem 
-- only one context is needed. Your code is triggering an actual bug in 
the compiler.


-Steve


Re: Calling template member function?

2022-04-19 Thread Andrey Zherikov via Digitalmars-d-learn
On Tuesday, 19 April 2022 at 20:29:01 UTC, Steven Schveighoffer 
wrote:
You can work around the dual context, if you are OK with 
passing the second context explicitly.


The easiest way is to move the member function to a UFCS 
function. an example:


```d
struct X
{
   int x;
   void applyToX(alias fn)() {fn(x);}
}

void applyToX_alt(alias fn)(ref X xval) {
   fn(xval.x);
}

void main()
{
   auto s = X(5);
   int y = 6;
   void fn(ref int x) { x += y; }
   s.applyToX!fn; // error, dual context needed
   s.applyToX_alt!fn; // fine, only single context needed
}
```


I used struct to understand the problem. I don't actually have an 
object context to pass like in your example, the only context I 
have is template parameters.




Re: Calling template member function?

2022-04-19 Thread Andrey Zherikov via Digitalmars-d-learn

On Tuesday, 19 April 2022 at 19:07:37 UTC, Ali Çehreli wrote:

On 4/19/22 11:18, Andrey Zherikov wrote:

> Is there a way/workaround to achieve the desired behavior?

Can you describe the goal a little more. For example, I assume 
you really need a more useful lambda although the example you 
show seems unnecessary:


  enum dg = () => func();

That lambda looks suspiciously trivial. Will it actually need 
its context?


Ali


I have [old API 
function](https://github.com/andrey-zherikov/argparse/blob/master/source/argparse.d#L1228) that gets a function as a parameter and I want it to call (for backward compatibility) [new API function](https://github.com/andrey-zherikov/argparse/blob/remove-main/source/argparse.d#L1630) which accepts a function as a template parameter.


Re: Calling template member function?

2022-04-19 Thread Steven Schveighoffer via Digitalmars-d-learn

On 4/19/22 2:18 PM, Andrey Zherikov wrote:

On Tuesday, 19 April 2022 at 16:38:42 UTC, Steven Schveighoffer wrote:

On 4/19/22 11:46 AM, Paul Backus wrote:
If you remove `static` from `f_new`, you get an error message talking 
about this explicitly:


Interesting that `static` does anything there, since it's a no-op.



I put `static` because it fixes "dual context" error.


So you saw a "dual context" error, and added static, and you got a 
different error?


This is a bit telling. I think the compiler is maybe thinking that it is 
inside an aggregate, where it needs a context pointer, and it doesn't 
actually need one.




Is there a way/workaround to achieve the desired behavior? Those two 
bugs pointed above were reported in 2017 and 2011 (!) which makes me 
think that they won't be fixed any time soon (I'm not sure that they are 
actually the same issue as I faced here).




The dual-context problem is old, and has actually been solved, but then 
reverted because the mechanism used is not portable to LDC and GDC (i.e. 
some current code compiles on DMD, but not LDC/GDC).


The other problem is just a straight-up bug.

You can work around the dual context, if you are OK with passing the 
second context explicitly.


The easiest way is to move the member function to a UFCS function. an 
example:


```d
struct X
{
   int x;
   void applyToX(alias fn)() {fn(x);}
}

void applyToX_alt(alias fn)(ref X xval) {
   fn(xval.x);
}

void main()
{
   auto s = X(5);
   int y = 6;
   void fn(ref int x) { x += y; }
   s.applyToX!fn; // error, dual context needed
   s.applyToX_alt!fn; // fine, only single context needed
}
```

You might ask, what is the drawback? I mean, it works exactly the same, 
same usage syntax.


The difference is in what happens when you take a delegate of the 
function. `!fn` is very much different than 
`_alt!fn`. The latter isn't even possible. But most of the 
time, there is no intention to take a delegate, so you can get away with 
the UFCS version.


-Steve


Re: Calling template member function?

2022-04-19 Thread Ali Çehreli via Digitalmars-d-learn

On 4/19/22 11:18, Andrey Zherikov wrote:

> Is there a way/workaround to achieve the desired behavior?

Can you describe the goal a little more. For example, I assume you 
really need a more useful lambda although the example you show seems 
unnecessary:


  enum dg = () => func();

That lambda looks suspiciously trivial. Will it actually need its context?

Ali



Re: Calling template member function?

2022-04-19 Thread Andrey Zherikov via Digitalmars-d-learn

I get the same error even with just `struct`:
```d
struct S
{
static void f_new(alias func)()
{
func();
}
}

void f_new(alias func)()
{
func();
}

void f(FUNC)(FUNC func)
{
f_new!(() => func());   // works
// S.f_new!(() => func());  // doesn't work
}
```


Re: Calling template member function?

2022-04-19 Thread Andrey Zherikov via Digitalmars-d-learn

On Tuesday, 19 April 2022 at 18:18:26 UTC, Andrey Zherikov wrote:
Is there a way/workaround to achieve the desired behavior? 
Those two bugs pointed above were reported in 2017 and 2011 (!) 
which makes me think that they won't be fixed any time soon 
(I'm not sure that they are actually the same issue as I faced 
here).


I tried to change the code to this:
```d
void f(FUNC)(FUNC func)
{
enum dg = () => func();
T!int.f_new!dg;
}
```

But I get this error:
```
onlineapp.d(7): Error: delegate `onlineapp.f!(void function() 
@safe).f.__lambda2` is a nested function and cannot be accessed 
from `onlineapp.T!int.f_new!(delegate () @safe

{
(*func)();
return ;
}
).f_new`
```


Re: Calling template member function?

2022-04-19 Thread Andrey Zherikov via Digitalmars-d-learn
On Tuesday, 19 April 2022 at 16:38:42 UTC, Steven Schveighoffer 
wrote:

On 4/19/22 11:46 AM, Paul Backus wrote:
If you remove `static` from `f_new`, you get an error message 
talking about this explicitly:


Interesting that `static` does anything there, since it's a 
no-op.


-Steve


I put `static` because it fixes "dual context" error.

Is there a way/workaround to achieve the desired behavior? Those 
two bugs pointed above were reported in 2017 and 2011 (!) which 
makes me think that they won't be fixed any time soon (I'm not 
sure that they are actually the same issue as I faced here).




Re: Calling template member function?

2022-04-19 Thread Steven Schveighoffer via Digitalmars-d-learn

On 4/19/22 11:46 AM, Paul Backus wrote:

On Tuesday, 19 April 2022 at 13:36:26 UTC, Andrey Zherikov wrote:
I want to migrate my library API from standalone function that takes 
delegate as argument to a template member function that takes delegate 
as a template parameter but compiler errors out. Here is code example:

```d
import std.stdio;

template T(P)
{
    static void f_new(alias func)()
    {
    func();
    }
}

void f(FUNC)(FUNC func)
{
    T!int.f_new!(() => func());
}

void main()
{
    f(function () { __LINE__.writeln; });
}
```

Compiler error:
```
onlineapp.d(7): Error: `static` function `onlineapp.f!(void function() 
@safe).f.f_new!(delegate () @safe

{
(*func)();
return ;
}
).f_new` cannot access delegate `__lambda2` in frame of function 
`onlineapp.f!(void function() @safe).f`

onlineapp.d(13):    `__lambda2` declared here
onlineapp.d(13): Error: template instance `onlineapp.f!(void 
function() @safe).f.f_new!(delegate () @safe

{
(*func)();
return ;
}
)` error instantiating
onlineapp.d(18):    instantiated from here: `f!(void function() 
@safe)`

```

What confuses me a lot is that if I remove `template T` then 
everything works perfectly:


The message is different, but I think this error is probably related to 
the "dual context" issue:


https://issues.dlang.org/show_bug.cgi?id=5710

Basically, `f_new!(() => func())` has two scopes that it "wants" to be 
nested in: `T!int` and `f!(void function() @safe)`. When you remove 
`template T`, there's only one scope, so it's not a problem.


No, there is no context pointer necessary for a template instantiation, 
without one being artificially introduced via an alias parameter. 
`T!int` is essentially just a namespace, especially since the `P` 
parameter isn't even used.




If you remove `static` from `f_new`, you get an error message talking 
about this explicitly:


Interesting that `static` does anything there, since it's a no-op.

-Steve


Re: Calling template member function?

2022-04-19 Thread Paul Backus via Digitalmars-d-learn

On Tuesday, 19 April 2022 at 13:36:26 UTC, Andrey Zherikov wrote:
I want to migrate my library API from standalone function that 
takes delegate as argument to a template member function that 
takes delegate as a template parameter but compiler errors out. 
Here is code example:

```d
import std.stdio;

template T(P)
{
static void f_new(alias func)()
{
func();
}
}

void f(FUNC)(FUNC func)
{
T!int.f_new!(() => func());
}

void main()
{
f(function () { __LINE__.writeln; });
}
```

Compiler error:
```
onlineapp.d(7): Error: `static` function `onlineapp.f!(void 
function() @safe).f.f_new!(delegate () @safe

{
(*func)();
return ;
}
).f_new` cannot access delegate `__lambda2` in frame of 
function `onlineapp.f!(void function() @safe).f`

onlineapp.d(13):`__lambda2` declared here
onlineapp.d(13): Error: template instance `onlineapp.f!(void 
function() @safe).f.f_new!(delegate () @safe

{
(*func)();
return ;
}
)` error instantiating
onlineapp.d(18):instantiated from here: `f!(void 
function() @safe)`

```

What confuses me a lot is that if I remove `template T` then 
everything works perfectly:


The message is different, but I think this error is probably 
related to the "dual context" issue:


https://issues.dlang.org/show_bug.cgi?id=5710

Basically, `f_new!(() => func())` has two scopes that it "wants" 
to be nested in: `T!int` and `f!(void function() @safe)`. When 
you remove `template T`, there's only one scope, so it's not a 
problem.


If you remove `static` from `f_new`, you get an error message 
talking about this explicitly:


```
onlineapp.d(5): Deprecation: function `onlineapp.f!(void 
function() @safe).f.f_new!(delegate () @safe

{
(*func)();
return ;
}
).f_new` function requires a dual-context, which is deprecated
onlineapp.d(13):instantiated from here: `f_new!(delegate 
() @safe

{
(*func)();
return ;
}
)`
onlineapp.d(18):instantiated from here: `f!(void 
function() @safe)`
onlineapp.d(13): Error: function `onlineapp.f!(void function() 
@safe).f.f_new!(delegate () @safe

{
(*func)();
return ;
}
).f_new` is a nested function and cannot be accessed from 
`onlineapp.f!(void function() @safe).f`

```


Re: Calling template member function?

2022-04-19 Thread Steven Schveighoffer via Digitalmars-d-learn

On 4/19/22 9:36 AM, Andrey Zherikov wrote:
I want to migrate my library API from standalone function that takes 
delegate as argument to a template member function that takes delegate 
as a template parameter but compiler errors out. Here is code example:

```d
import std.stdio;

template T(P)
{
     static void f_new(alias func)()
     {
     func();
     }
}

void f(FUNC)(FUNC func)
{
     T!int.f_new!(() => func());
}

void main()
{
     f(function () { __LINE__.writeln; });
}
```

Compiler error:
```
onlineapp.d(7): Error: `static` function `onlineapp.f!(void function() 
@safe).f.f_new!(delegate () @safe

{
(*func)();
return ;
}
).f_new` cannot access delegate `__lambda2` in frame of function 
`onlineapp.f!(void function() @safe).f`

onlineapp.d(13):    `__lambda2` declared here
onlineapp.d(13): Error: template instance `onlineapp.f!(void function() 
@safe).f.f_new!(delegate () @safe

{
(*func)();
return ;
}
)` error instantiating
onlineapp.d(18):    instantiated from here: `f!(void function() @safe)`
```

What confuses me a lot is that if I remove `template T` then everything 
works perfectly:

```d
import std.stdio;

void f_new(alias func)()
{
     func();
}

void f(FUNC)(FUNC func)
{
     f_new!(() => func());
}

void main()
{
     f(function () { __LINE__.writeln; });
}
```

Is there an issue in template processing in compiler?


Something similar just recently came up on discord. Maybe related: 
https://issues.dlang.org/show_bug.cgi?id=17063


-Steve