Итерация по структуре dlang

у меня есть структура, которая выглядит примерно так:

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

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

и я хотел бы повторить это в другом классе с чем-то вроде этого:

struct Socket {
    ...

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

    ...
}

это возможно? Я хотел бы использовать что-то аналогичное Python __iter__ на MultipartMessage который может возвращать поля в определенном порядке и в идеале даже запускать дополнительный код, например header.serialize().

в идеале я бы добавил функцию в MultipartMessage это будет выглядеть примерно так (псевдокод):

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

3 ответов


ближе всего к тому, что вы хотите, вероятно,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;
}

использовать tupleof:

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

этой sendPart С mime, data и header (поля структуры, в порядке их объявления). Вы можете фильтровать поля, проверяя их тип, например static if (!is(typeof(part) == Header)).

получить имя поля, вы можете использовать __traits(identifier):

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

(__traits(identifier, part) вернутся part.)

там же __traits(allMembers), который также возвращает методов.


есть несколько способов сделать итерацию над объектами в D.


один должен реализовать InputRange API-интерфейс. Входные диапазоны похожи на итераторы, но имеют другой API. Реализация интерфейса диапазона означает, что вы можете использовать все std.range/std.algorithm функции на вашем объекте, такие как map, array, joiner и так далее.

D не имеет __iter__ функция для получения итератора из произвольных коллекций, поэтому вам нужно будет реализуйте функцию, возвращающую входной диапазон.

import std.range;

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

это вернет 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;

если вы реализуете выходной диапазон поверх своего сокета, то это означает, что решение выходного диапазона не нужно выделять память из кучи.


Проверьте Программирование В D книги (в частности,диапазоны и Больше Диапазонов разделы) для получения информации о том, как реализовать свои собственные диапазоны.