Not being able to correctly attribute methods is a common issue when designing interfaces and classes. While it's often still possible to restrict all derived classes to certain attributes, it's a lot harder to restrict all possible callbacks.
void toString(scope void delegate(in char[]) @wildcard) @(wildcard nogc safe);
It would be a great help if such a method could be defined, to that @nogc, @safe, pure, and nothrow can be made dependent on the passed in callback, making it possible to call the method in @nogc code, while also passing in a GC using callback.
Comment #1 by dfj1esp02 — 2017-10-31T16:58:51Z
Apparently even templates don't help here much:
---
import std.typecons:Nullable;
Nullable!V convert(T,V)(Nullable!T v, scope V delegate(T) c)
{
if(v.isNull)return Nullable!V();
return Nullable!V(c(v.get()));
}
void f() pure
{
Nullable!long v;
Nullable!int v1=v.convert!(long,int)((a){ return cast(int)a; });
}
---
Error: pure function 'f' cannot call impure function 'convert!(long, int).convert'
Comment #2 by Bastiaan — 2024-04-05T18:20:20Z
This came up again at the DLF quarterly meeting April 5, 2024, with industry partners, and Walter asked to make sure there is a feature request. As discussed there:
For arguments and return types of various mutability we have the `inout` keyword that saves us from having to define multiple function overloads[1]. A similar solution for other attributes does not exist.
For example, if you have foreach loops over a struct S both in a @nogc context and with an allocating loop body,
```d
void f()
{
S s;
foreach (a; s)
allocating(a);
}
void g() @nogc
{
S s;
foreach (a; s)
nonallocating(a);
}
void allocating(int) {}
void nonallocating(int) @nogc {}
```
then S needs both of these opApply overloads:
```d
int opApply(scope int delegate(int) dg);
int opApply(scope int delegate(int) @nogc dg) @nogc;
```
Similar for @safe, nothrow, pure and their permutations.
Templating opApply[2] can help in many of these cases, but problems arise when classes and inheritance get involved[3].
There is a DIP for argument dependent attributes[4] but work on it has stalled.
[1] https://dlang.org/spec/const3.html#inout
[2] https://dlang.org/spec/statement.html#template-op-apply
[3] https://youtu.be/9lOtOtiwXY4?si=KME_ZddnrecMdWOJ&t=359
[4] https://github.com/dlang/DIPs/pull/198
Comment #3 by qs.il.paperinik — 2024-06-24T11:32:02Z
This can be done using templates:
```d
int f(DG)(scope DG callback)
{
return callback("Hello, World");
}
void main() @safe
{
import std.stdio;
f((string str) { writeln(str); return 0; });
}
```
For opApply, for some reason, this works:
```d
struct S
{
private int opApplyImpl(DG)(scope DG dg)
{
static if (is(DG : int delegate(string)))
return dg("Hello, World!");
else
return dg(0, "Hello, World!");
}
alias opApply = opApplyImpl!(int delegate(string));
alias opApply = opApplyImpl!(int delegate(size_t, string));
}
void main() @safe
{
import std.stdio;
foreach (s; S())
{
static assert(is(typeof(s) == string));
writeln(s);
}
foreach (i, s; S())
{
static assert(is(typeof(i) == size_t));
static assert(is(typeof(s) == string));
writeln(i, " ", s);
}
}
```
Comment #4 by dlang-bot — 2024-06-26T16:49:28Z
@Bolpat created dlang/dlang.org pull request #3859 "Specify `opApply` as an alias to a function template instance" fixing this issue:
- Fix Bugzilla Issues 23666, 17953, 23116, and 24633
https://github.com/dlang/dlang.org/pull/3859
Comment #5 by robert.schadek — 2024-12-13T18:55:04Z