遍历 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);
这将调用 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)
会 return part
.)
还有 __traits(allMembers)
,这也是 return 的方法。
有几种方法可以迭代 D 中的对象。
一个是实现 InputRange API。输入范围类似于迭代器,但具有不同的 API。实现范围接口意味着您可以在对象上使用所有 std.range
/std.algorithm
函数,例如 map
、array
、joiner
等.
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());
}
但是,我建议您在对象上实现一个函数,该函数采用 输出范围 并将数据写入其中,而不是这些选项中的任何一个。
输出范围是接受其他对象并对其执行某些操作的对象。在这种情况下,输出范围应接受 ubyte
s,使其类似于输出流。
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 书(具体来说,Ranges 和 More 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;
}
我有一个看起来像这样的结构:
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);
这将调用 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)
会 return part
.)
还有 __traits(allMembers)
,这也是 return 的方法。
有几种方法可以迭代 D 中的对象。
一个是实现 InputRange API。输入范围类似于迭代器,但具有不同的 API。实现范围接口意味着您可以在对象上使用所有 std.range
/std.algorithm
函数,例如 map
、array
、joiner
等.
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());
}
但是,我建议您在对象上实现一个函数,该函数采用 输出范围 并将数据写入其中,而不是这些选项中的任何一个。
输出范围是接受其他对象并对其执行某些操作的对象。在这种情况下,输出范围应接受 ubyte
s,使其类似于输出流。
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 书(具体来说,Ranges 和 More 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;
}