2015-11-30 16 views
6

Ich habe eine Struktur, die etwa wie folgt aussieht:Iterate über dlang struct

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

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

und ich würde darüber iterieren möchten, in einer anderen Klasse mit etwas wie folgt aus:

struct Socket { 
    ... 

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

    ... 
} 

Ist das möglich? Ich möchte etwas analog zu Pythons __iter__ in der MultipartMessage verwenden, die die Felder in einer bestimmten Reihenfolge zurückgeben kann, und idealerweise sogar zusätzlichen Code wie header.serialize() ausführen.

Idealerweise würde ich eine Funktion MultipartMessage hinzufügen, dass so etwas wie dieser (Pseudo-Code) aussehen:

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

Wie in der Dokumentation angegeben (http://dlang.org/spec/statement.html#ForeachStatement) gibt es viele Möglichkeiten, mit foreach Aussagen umgehen. Am einfachsten ist vielleicht der Eingabebereich, aber ohne eine klare Übersicht darüber, was Sie bei jeder Wiederholung wünschen, ist es schwer zu sagen, welcher Weg für Ihren Fall am besten ist. Könntest du präziser sein? – cym13

Antwort

2

Die nächste Sache, was Sie wollen, ist wahrscheinlich opApply.

Siehe http://dlang.org/spec/statement.html Abschnitt Foreach over Structs and Classes wit opApply

Dies funktioniert:

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; 
} 
8

Verwendung tupleof:

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

Dies ruft sendPart mit mime, data und header (die Felder der Struktur, in der Reihenfolge, in der sie deklariert wurden). Sie können Felder filtern, indem Sie deren Typ mit z. static if (!is(typeof(part) == Header)).

Feld des Namens erhalten, Sie __traits(identifier) verwenden können:

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

(__traits(identifier, part) zurückkehren würde part.)

Es gibt auch __traits(allMembers), die auch Methoden zurückgibt.

+0

'tupleof' ist kein gutes Analog zu Pythons' __iter__'. –

+0

Vielleicht habe ich die Frage missverstanden, aber D's '__iter__' ist' opApply'. –

+0

was meinst du mit D's '__iter__' ist' opApply'? Wie könnte das verwendet werden, um über eine Struktur zu iterieren? –

2

Es gibt mehrere Möglichkeiten Iterierte über Objekte in D.


One zu tun ist, um die InputRange API zu implementieren. Eingabebereiche ähneln Iteratoren, haben jedoch eine andere API. Das Implementieren einer Bereichsschnittstelle bedeutet, dass Sie alle std.range/std.algorithm-Funktionen für Ihr Objekt verwenden können, z. B. map, array, joiner und so weiter.

D hat keine __iter__ Funktion, um einen Iterator aus beliebigen Auflistungen zu erhalten. Daher müssen Sie eine Funktion implementieren, die einen Eingabebereich zurückgibt.

import std.range; 

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

Dies wird einen ubyte Eingangsbereich zurückzukehren, in mime den Bytes besteht, in data von dem Bytes gefolgt, dann in header.serialize.


können Sie auch die opApply Methode auf Ihrer Struktur implementieren. opApply funktioniert nur mit foreach, so dass Sie keine Entfernungsmethoden damit verwenden können, aber Sie können Dinge tun, wie den Schleifenkörper in separaten Threads ausführen.

Der Kern von opApply ist, dass D den Schleifenkörper an opApply als eine Funktion übergibt; das heißt, foreach(x; myObj) { body } wird in umgewandelt.

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

Anstatt jedoch eine dieser beiden Optionen, empfehle ich, dass Sie eine Funktion auf Ihrem Objekt zu implementieren, das einen Ausgangsbereich und schreibt die Daten in es dauert.

Ein Ausgabebereich ist ein Objekt, das andere Objekte akzeptiert und ihnen etwas antut. In diesem Fall sollte der Ausgabebereich ubytes annehmen, sodass er einem Ausgabestream ähnelt.

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 
} 

Beispiel für die Verwendung, die die Ausgabe in eine Appender speichert, die in ein Array umgewandelt werden kann:

import std.array; 

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

Wenn Sie einen Leistungsbereich auf Ihre Steckdose implementieren, dann bedeutet das, dass die Ausgabebereich Lösung muss keinen Speicher aus dem Heap zuordnen.


Schauen Sie sich das Programming in D Buch (genauer gesagt, den Rang und Weitere Bereiche Abschnitte) für Informationen darüber, wie Sie Ihre eigenen Bereiche zu implementieren.