Jump to page: 1 2
Thread overview
Calling template member function?
Apr 19, 2022
Andrey Zherikov
Apr 19, 2022
Paul Backus
Apr 19, 2022
Andrey Zherikov
Apr 19, 2022
Andrey Zherikov
Apr 19, 2022
Andrey Zherikov
Apr 19, 2022
Ali Çehreli
Apr 20, 2022
Andrey Zherikov
Apr 20, 2022
Andrey Zherikov
April 19, 2022

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:

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:

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?

April 19, 2022

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:

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:

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

April 19, 2022

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:

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`
April 19, 2022

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:

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

April 19, 2022

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).

April 19, 2022

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:

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`
April 19, 2022

I get the same error even with just struct:

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
}
April 19, 2022
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

April 19, 2022

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:

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. &s.applyToX!fn is very much different than &s.applyToX_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

April 20, 2022

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 that gets a function as a parameter and I want it to call (for backward compatibility) new API function which accepts a function as a template parameter.

« First   ‹ Prev
1 2