标准库有一些隐藏的宝石,直到我偷看源来回答这个问题之前,我什至都不了解自己:
http://dlang.org/phobos/std_traits.html#Fields
以及它下面的那些。有了这些,我们可以使您的CatStruct 相当简洁。看:
mixin template CatStruct(string name, T...) {
static import std.traits, std.conv;
private string _code_generator() {
string code = "struct " ~ name ~ " {";
foreach(oidx, t; T) {
foreach(idx, field; std.traits.FieldTypeTuple!t)
// this line is a monster, see the end of this answer
code ~= "std.traits.FieldTypeTuple!(T["~std.conv.to!string(oidx)~"])["~std.conv.to!string(idx)~"] "~ std.traits.FieldNameTuple!t[idx] ~ ";";
}
code ~= "}";
return code;
}
mixin(_code_generator());
}
不过,这使用了字符串混合......虽然字符串混合基本上可以做任何事情,但它们基本上也很烂。这很容易变脆,但我认为它基本上可以在基本吸吮的同时工作。
它也不会做结构方法,但我认为这对于任何这些神奇的东西来说太难了,除了opDispatch,正如在另一个答案中看到的那样(顺便说一句,这很好,不要把我的回答当作对那个答案的否定,只是另一个想法)。
如果两个结构之间的名称也有冲突,它们会破坏这一点,并且你会从编译器中得到一个丑陋的错误消息。使用真正的模板 mixin,有一个简单的解决方法 - 一个命名的模板 mixin,它可以让您消除歧义。但这里没有这样的事情。如果你需要的话,我想你可以破解一个。
但无论如何,可能有一种方法可以使用 stdlib 中的 FieldTypeTuple 和 FieldNameTuple 来做得更好,但我认为这或多或少是你现在所要求的。
顺便说一句,我想说如果可以的话,就做普通的作文,一般来说效果最好。 (不要忘记alias this 也可以自动转发到成员变量。)
如果你没有做过很多混入,你可能想问我为什么我在code ~= 部分使用了那个疯狂的字符串,而不是更直接的。 code ~= field.stringof ~ " "~ FieldNameTuple!t[idx] ~ ";";
tl;dr:相信我,始终使用在您生成的代码中运行 mixin() 本身的范围内可用的本地名称。下面是长解释/
这与名称冲突和符号查找有关。我在混合代码中使用了静态导入和完全限定名称 - 包括使用 FieldTypeTuple 的本地符号而不是 field.stringof - 以尽可能保持命名空间整洁。
考虑结构 A 在内部导入其他模块并用它定义字段的情况。
// using my color.d just cuz I have it easily available
// but it could be anything, so don't worry about downloading it
struct A { import arsd.color; Color a; }
AB ab;
import arsd.color;
ab.a = Color.white; ab.b = 2; // we expect this work, should be the same type
由于这是结构 A 中的本地导入,因此名称在混合点处毫无意义。
继续调整 mixin,使其使用简单的行进行编译
// comment fancy line
// code ~= "std.traits.FieldTypeTuple!(T["~std.conv.to!string(oidx)~"])["~std.conv.to!string(idx)~"] "~ std.traits.FieldNameTuple!t[idx] ~ ";";
// paste in simple line
code ~= field.stringof ~ " "~ std.traits.FieldNameTuple!t[idx] ~ ";";
并编译:
$ dmd f.d ~/arsd/color.d
f.d-mixin-31(31): Error: undefined identifier 'Color'
f.d(4): Error: mixin f.CatStruct!("AB", A, B) error instantiating
Zoinks!它不知道字符串“Color”应该指的是什么。如果我们在本地模块中导入了一些 other 类型的 struct Color,它会编译......但它会引用不同的类型:
struct A { import arsd.color; Color a; }
struct B { int b; }
struct Color { static Color white() { return Color.init; } }
mixin CatStruct!("AB", A, B);
AB ab;
import arsd.color;
ab.a = Color.white; ab.b = 2;
编译它并看到一个听起来很愚蠢的错误:
$ dmd f.d ~/arsd/color.d
f.d(12): Error: cannot implicitly convert expression (white()) of type Color to Color
顺便说一句:如果您在野外看到它,请记住这一点 - 编译器错误消息听起来很荒谬,“无法将颜色隐式转换为颜色”,但它确实具有逻辑含义:只有两种不同的类型具有相同的名称在不同的模块中。
不管怎样,这听起来很傻,但有道理,因为两个作用域导入了不同的结构。
对于与本地静态导入一起使用的长格式 FieldTypeTuple,它始终指代传入的实际类型。间接地,当然,但也很明确。
我向那些已经知道字符串混合的陷阱的阅读本文的人道歉,但是任何在搜索中发现它的人可能不知道我为什么使用那个令人费解的代码。我发誓,由于现实世界的实际问题经验,这很复杂! :) 第一次就做对比尝试调试奇怪的废话要容易得多。