views:

143

answers:

2

Hi, how can I make a class usable in a foreach statement?

The class contains a associative array (e.g. string[string]). So the foreach statement use this array as source.

So this is what I want:

auto obj = new Obj();
foreach (key, value; obj)
{
    ...
}

Do I need to implement a interface someting like that?

EDIT:

The solution:

public int opApply(int delegate(ref string, ref Type) dg)
{
    int result = 0;

    foreach (ref key, ref value; data)
    {
        result = dg(key, value);
        if (result != 0)
        {
            break;
        }
    }

    return result;
}

Same is done for public int opApply(int delegate(ref Type) dg).

+3  A: 

D1:

class Foo
{
    uint array[2];

    int opApply(int delegate(ref uint) dg)
    {
        int result = 0;

        for (int i = 0; i < array.length; i++)
        {
            result = dg(array[i]);
            if (result)
                break;
        }
        return result;
    }
}

D2:

Iteration over struct and class objects can be done with ranges, which means [a set of] properties must be defined:

CyberShadow
I don't think ranges-based foreach support unpacking yet. And `opApply` can be used in D2 as well.
KennyTM
opApply was the solution, thnx!
VDVLeon
+1  A: 

The solution posted by the OP is a valid solution, but in D2 there's another one with a different set of tradeoffs. Iteration in D can be divided into internal iteration, which is handled by opApply, and external iteration, which is handled by ranges.

Internal iteration gives the object being iterated over control of the call stack. This allows, for example, recursion to be used by the object being iterated over without maintaining an explicit stack, but makes iterating over multiple structures in lockstep impossible. External iteration does the opposite.

External iteration is accomplished via ranges. A range is any class or struct that defines three methods: front() gives access to the first element in the range, popFront() advances the range and empty() returns true if the range is empty. If the range is infinite, empty can be declared as a constant instead of a member function. In this way, the caller has control over the call stack, which is a tradeoff that can be good or bad depending on the situation.

Here's an example of using ranges for iteration:

/**This struct lazily produces all Fibonacci numbers.*/
struct Fibonacci {
    ulong num1 = 0;
    ulong num2 = 1;

    ulong front() {
        return num1 + num2;
    }

    void popFront() {
        auto newNum2 = num1 + num2;
        num1 = num2;
        num2 = newNum2;
    }

    // A range of Fibonacci numbers is infinite.
    enum bool empty = false; 
}
dsimcha