Issue 14196 - opApply and nothrow don't play along
Summary: opApply and nothrow don't play along
Status: NEW
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: All All
: P2 major
Assignee: No Owner
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-02-17 19:44 UTC by Mathias LANG
Modified: 2022-12-17 10:38 UTC (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Mathias LANG 2015-02-17 19:44:02 UTC
It's impossible to have both opApply with type inference and compiler-checked nothrow for a user-defined type.

Say you've got a container of T, and you want to implement opApply. Fine, the signature is, as you know (I only covert the first one, but it applies to both):

int opApply(int delegate(ref T) del);

But the issue is, you can't pass it a nothrow delegate, so you'll have to make it a template:

int opApply(DG)(DG del);

It works, but you loose type deduction, e.g for is(T == string):

foreach (ref elem; arr) // Doesn't compile anymore
foreach (ref string elem; arr) // So you'll have to use this.

Full example:


struct MyArray(T) {
        T[] data;
        int opApply(DG)(DG del) {
                for (size_t idx; idx < data.length; ++idx)
                        if (auto r = del(data[idx]))
                                return r;
                return 0;
        }
}

void neverThrow(string s) nothrow {}
void mightThrow(string s) {}

void testNothrow(T)(ref T arr) nothrow  {
        foreach (ref elem; arr)
                neverThrow(elem);
}
void testThrow(T)(ref T arr) {
        foreach (ref elem; arr)
                mightThrow(elem);
}

void main() {
        // Note that type deduction doesn't work here.
        auto arr = MyArray!string(["1", "2", "3", "4", "5", "42"]);
        testNothrow(arr);
        testThrow(arr);
}
Comment 1 Илья Ярошенко 2015-08-06 19:09:39 UTC
Blocks PR https://github.com/D-Programming-Language/phobos/pull/3397
Comment 2 Bolpat 2020-12-29 01:35:29 UTC
You can overload on attributes:

struct MyArray(T)
{
    int opApply(scope int delegate(ref T)         dg);
    int opApply(scope int delegate(ref T) nothrow dg) nothrow;
}

This is not DRY and if the code inside opApply might throw depending on T, the second overload won't compile.
Using templates breaks generic code that omits the foreach variables' types (which is what generic code usually does).

If your case is only about nothrow (or any other single attribute), you can define a template 
    int opApplyImpl(DG)(scope DG dg)
and alias opApply to specific instantiations:
    alias opApply = opApplyImpl!(int delegate(ref T)        );
    alias opApply = opApplyImpl!(int delegate(ref T) nothrow);
Whether this solution is viable or not depends on details of your case.
Comment 3 Mathias LANG 2020-12-29 10:57:22 UTC
For reference, I mentioned a solution to this problem which I'm working on during DConf: https://www.youtube.com/watch?v=9lOtOtiwXY4