On Friday, 20 January 2023 at 03:41:02 UTC, Walter Bright wrote:
> On 1/19/2023 7:19 PM, H. S. Teoh wrote:
> This is a flaw in the language: there is no way to express that
process
's attributes inherit from the passed-in delegate. This has
been a pain point for years.
void process()(void delegate() userData) {}
Er, no. The template process
will be inferred @system thorw @gc impure
because the delegate is annotated @system thorw @gc impure
implicitly – unless you don’t actually call it. The correct version is this:
void process(DG : void delegate())(DG callback) { callback(); }
This has two drawbacks:
process
cannot be virtual.
- the argument bound to
callback
cannot have its parameter types inferred.
By 2. I mean the little more interesting example when the delegate takes parameters, say int
:
/*A*/ void process(void delegate(int) callback) { callback(); }
/*B*/ void process(DG : void delegate(int))(DG callback) { callback(); }
(The constraint is optional, but useful.)
For version A, when process
is called, like process((x){})
, the compiler can statically infer that x
is of type int
. For version B, it cannot, but it does not matter practically for functions like process
, however it does matter for opApply
because programmers using a foreach
loop really want the type of the iteration variable inferred (especially in meta programming); this inference only works when opApply
is not a template. opApply
may still be a template instance (or an alias to a template instance), which we can put to use. The shortest I could get to:
import std.meta : AliasSeq;
import std.traits : SetFunctionAttributes, functionLinkage, functionAttributes, FunctionAttribute;
template WithAnyCombinationOfAttributes(DG)
{
alias WithAnyCombinationOfAttributes = AliasSeq!();
static foreach (safety; [ FunctionAttribute.system, FunctionAttribute.safe ])
static foreach (purity; [ FunctionAttribute.none, FunctionAttribute.pure_ ])
static foreach (gcness; [ FunctionAttribute.none, FunctionAttribute.nogc ])
static foreach (exness; [ FunctionAttribute.none, FunctionAttribute.nothrow_ ])
{
WithAnyCombinationOfAttributes = AliasSeq!(WithAnyCombinationOfAttributes, SetFunctionAttributes!(DG, functionLinkage!DG, (functionAttributes!DG & ~ FunctionAttribute.system) | safety | purity | gcness | exness));
}
}
mixin template opApplyFromImpl(alias impl, protoDG, alias _WithAnyCombinationOfAttributes = WithAnyCombinationOfAttributes)
{
static foreach (DG; _WithAnyCombinationOfAttributes!protoDG)
alias opApply = impl!DG;
}
You can find the code in action here. In my opinion, something like this should be in Phobos.
The drawbacks are lots of template instantiations and that it always generates 16 overloads.