遍历 dlang 结构

Iterate over dlang struct

我有一个看起来像这样的结构:

struct MultipartMessage {
    ubyte[] mime, data;
    Header header;

    void setSender(string sender) {
        header.sender = sender;
    }
    void setId(int id) {
        header.id = id;
    }
}

我想在另一个 class 中迭代它,像这样:

struct Socket {
    ...

    void send(MultipartMessage msg) {
        foreach (part; msg) {
            sendPart(part);
        }
    }

    ...
}

这可能吗?我想在 MultipartMessage 中使用类似于 Python 的 __iter__ 的东西,它可以 return 按特定顺序排列字段,甚至 运行一些额外的代码,比如 header.serialize().

理想情况下,我会向 MultipartMessage 添加一个看起来像这样的函数(伪代码):

ubyte[] __iter__() {
    yield mime;
    yield data;
    yield header.serialize(); //header.serialize returns a ubyte[]
}

使用tupleof:

foreach (ref part; msg.tupleof)
    sendPart(part);

这将调用 sendPartmimedataheader(结构的字段,按照声明的顺序)。您可以通过检查类型来过滤字段,例如static if (!is(typeof(part) == Header)).

要获取字段名称,您可以使用 __traits(identifier):

foreach (i, ref part; msg.tupleof)
    writeln(__traits(identifier, msg.tupleof[i]));

(__traits(identifier, part) 会 return part.)

还有 __traits(allMembers),这也是 return 的方法。

有几种方法可以迭代 D 中的对象。


一个是实现 InputRange API。输入范围类似于迭代器,但具有不同的 API。实现范围接口意味着您可以在对象上使用所有 std.range/std.algorithm 函数,例如 maparrayjoiner 等.

D 没有 __iter__ 函数来从任意集合中获取迭代器,因此您需要实现一个 return 输入范围的函数。

import std.range;

auto bytes() {
    return chain(mime, data, header.serialize);
}

这将 return 一个 ubyte 输入范围,由 mime 中的字节组成,然后是 data 中的字节,然后是 header.serialize 中的字节.


您还可以在结构上实现 opApply 方法。 opApply 仅适用于 foreach,因此您不能对它使用范围方法,但它允许您执行诸如在单独的线程中执行循环体之类的操作。

opApply的要点是D将循环体作为函数传递给opApply;即 foreach(x; myObj) { body } 转化为 myObj.opApply((x) { body }).

void opApply(void delegate(ubyte[] part) loopbody) {
    loopbody(mime);
    loopbody(data);
    loopbody(header.serialize());
}

但是,我建议您在对象上实现一个函数,该函数采用 输出范围 并将数据写入其中,而不是这些选项中的任何一个。

输出范围是接受其他对象并对其执行某些操作的对象。在这种情况下,输出范围应接受 ubytes,使其类似于输出流。

void serialize(Range)(ref Range outRange) if(isOutputRange!(Range, ubyte)) {
    put(outRange, mime); -- `put` simply feeds data into the output range
    put(outRange, data);
    header.serialize(outRange); // No longer have to allocate/return a ubyte array
}

示例用法,将输出存储到 Appender,可以转换成数组:

import std.array;

auto serializedDataAppender = appender!ubyte();
myMsg.serialize(serializedDataAppender);
auto serializedData = serializedDataAppender.data;

如果您在套接字之上实现输出范围,那么这意味着输出范围解决方案不必从堆中分配任何内存


查看 Programming in D 书(具体来说,RangesMore Ranges 部分)以获取有关如何实施的信息你自己的范围。

最接近你想要的可能是opApply

参见 http://dlang.org/spec/statement.html,第 Foreach over Structs and Classes wit opApply

部分

这将起作用:

int opApply(int delegate(ref ubyte[]) dg) {
    int result = 0;
    result = dg(mime);
    result = dg(data);
    ubyte[] header_bytes = header.serialize();
    result = dg(header_bytes);
    return result;
}