digitalmars.D.learn - Calling template member function?
- Andrey Zherikov (61/61) Apr 19 2022 I want to migrate my library API from standalone function that
- Steven Schveighoffer (4/72) Apr 19 2022 Something similar just recently came up on discord. Maybe related:
- Paul Backus (36/81) Apr 19 2022 The message is different, but I think this error is probably
- Steven Schveighoffer (7/69) Apr 19 2022 No, there is no context pointer necessary for a template instantiation,
- Andrey Zherikov (7/13) Apr 19 2022 I put `static` because it fixes "dual context" error.
- Andrey Zherikov (20/25) Apr 19 2022 I tried to change the code to this:
- Andrey Zherikov (19/19) Apr 19 2022 I get the same error even with just `struct`:
- =?UTF-8?Q?Ali_=c3=87ehreli?= (7/8) Apr 19 2022 Can you describe the goal a little more. For example, I assume you
- Andrey Zherikov (3/12) Apr 19 2022 I have [old API
- Steven Schveighoffer (40/55) Apr 19 2022 So you saw a "dual context" error, and added static, and you got a
- Andrey Zherikov (5/27) Apr 19 2022 I used struct to understand the problem. I don't actually have an
- Steven Schveighoffer (5/37) Apr 19 2022 That's because your code is not an example of the dual context problem
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?
Apr 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: ```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
Apr 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: ```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` ```
Apr 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: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.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:Interesting that `static` does anything there, since it's a no-op. -Steve
Apr 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: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).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
Apr 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: ```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` ```
Apr 19 2022
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 } ```
Apr 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
Apr 19 2022
On Tuesday, 19 April 2022 at 19:07:37 UTC, Ali Çehreli wrote:On 4/19/22 11:18, Andrey Zherikov wrote:I have [old API function](https://github.com/andrey-zherikov/argparse/blob/master/sour e/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/sour e/argparse.d#L1630) which accepts a function as a template parameter.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
Apr 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: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.On 4/19/22 11:46 AM, Paul Backus wrote:I put `static` because it fixes "dual context" error.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.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. `&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
Apr 19 2022
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.
Apr 19 2022
On 4/19/22 8:44 PM, Andrey Zherikov wrote:On Tuesday, 19 April 2022 at 20:29:01 UTC, Steven Schveighoffer wrote: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. -SteveYou 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.
Apr 19 2022