Templates 了解mixin与mixin模板
在学习D语言的过程中,我试图创建一个通用的矩阵类,它支持所包含对象的类型提升 也就是说,当我乘以一个Templates 了解mixin与mixin模板,templates,d,Templates,D,在学习D语言的过程中,我试图创建一个通用的矩阵类,它支持所包含对象的类型提升 也就是说,当我乘以一个矩阵时!(int)到矩阵!(真实)我应该得到一个矩阵!(真实)因此 由于有许多不同的类型,因此为每个可能的组合重新实现opBinary方法将非常繁琐,而且需要大量的样板代码。所以mixin/mixin模板似乎就是答案 我不理解的是为什么第一个代码示例可以工作 import std.stdio; import std.string : format; string define_opbinary
矩阵时!(int)
到矩阵!(真实)
我应该得到一个矩阵!(真实)
因此
由于有许多不同的类型,因此为每个可能的组合重新实现opBinary
方法将非常繁琐,而且需要大量的样板代码。所以mixin/mixin模板似乎就是答案
我不理解的是为什么第一个代码示例可以工作
import std.stdio;
import std.string : format;
string define_opbinary(string other_type) {
return "
Matrix opBinary(string op)(Matrix!(%s) other) {
if(op == \"*\") {
Matrix result;
if(this.columns == other.rows) {
result = new Matrix(this.rows, other.columns);
} else {
result = new Matrix(0,0);
}
return result;
} else assert(0, \"Operator \"~op~\" not implemented\");
}
".format(other_type);
}
class Matrix(T) {
T[][] storage;
size_t rows;
size_t columns;
const string type = T.stringof;
this(size_t rows, size_t columns) {
this.storage = new T[][](rows, columns);
this.rows = rows;
this.columns = columns;
}
void opIndexAssign(T value, size_t row, size_t column) {
storage[row][column] = value;
}
mixin(define_opbinary(int.stringof));
mixin(define_opbinary(uint.stringof));
}
void main()
{
Matrix!int mymat = new Matrix!(int)(2, 2);
mymat[0,0] = 5;
writeln(mymat.type);
Matrix!uint mymat2 = new Matrix!(uint)(2, 2);
writeln(mymat2.type);
auto result = mymat * mymat2;
writeln("result.rows=", result.rows);
writeln("result.columns=", result.columns);
auto result2 = mymat2 * mymat;
writeln("result.type=",result.type);
writeln("result2.type=",result2.type);
}
配音输出:
Performing "debug" build using /usr/bin/dmd for x86_64.
matrix ~master: building configuration "application"...
Linking...
Running ./matrix.exe
50
00
int
uint
result.rows=2
result.columns=2
00
00
result.type=int
result2.type=uint
source/app.d(60,19): Error: cast(Object)mymat is not of arithmetic type, it is a object.Object
source/app.d(60,27): Error: cast(Object)mymat2 is not of arithmetic type, it is a object.Object
source/app.d(64,20): Error: cast(Object)mymat2 is not of arithmetic type, it is a object.Object
source/app.d(64,29): Error: cast(Object)mymat is not of arithmetic type, it is a object.Object
/usr/bin/dmd failed with exit code 1.
但是第二个代码示例不起作用
import std.stdio;
import std.string : format;
mixin template define_opbinary(alias other_type) {
Matrix opBinary(string op)(Matrix!(other_type) other) {
if(op == "*") {
Matrix result;
if(this.columns == other.rows) {
result = new Matrix(this.rows, other.columns);
} else {
result = new Matrix(0,0);
}
return result;
} else assert(0, "Operator "~op~" not implemented");
}
}
class Matrix(T) {
T[][] storage;
size_t rows;
size_t columns;
const string type = T.stringof;
this(size_t rows, size_t columns) {
this.storage = new T[][](rows, columns);
this.rows = rows;
this.columns = columns;
}
void opIndexAssign(T value, size_t row, size_t column) {
storage[row][column] = value;
}
mixin define_opbinary!(int);
mixin define_opbinary!(uint);
}
void main()
{
Matrix!int mymat = new Matrix!(int)(2, 2);
mymat[0,0] = 5;
writeln(mymat.type);
Matrix!uint mymat2 = new Matrix!(uint)(2, 2);
writeln(mymat2.type);
auto result = mymat * mymat2;
writeln("result.rows=", result.rows);
writeln("result.columns=", result.columns);
auto result2 = mymat2 * mymat;
writeln("result.type=",result.type);
writeln("result2.type=",result2.type);
}
配音输出:
Performing "debug" build using /usr/bin/dmd for x86_64.
matrix ~master: building configuration "application"...
Linking...
Running ./matrix.exe
50
00
int
uint
result.rows=2
result.columns=2
00
00
result.type=int
result2.type=uint
source/app.d(60,19): Error: cast(Object)mymat is not of arithmetic type, it is a object.Object
source/app.d(60,27): Error: cast(Object)mymat2 is not of arithmetic type, it is a object.Object
source/app.d(64,20): Error: cast(Object)mymat2 is not of arithmetic type, it is a object.Object
source/app.d(64,29): Error: cast(Object)mymat is not of arithmetic type, it is a object.Object
/usr/bin/dmd failed with exit code 1.
非常奇怪的是,如果我删除mixin define\u opbinary!(int)代码>呼叫,然后我只收到两个算术投诉(只有关于第60行的两个投诉(auto result=mymat*mymat2;
)仍然存在)
我有一种感觉,不知何故,编译器认为这两个mixin调用是不明确的,并同时删除了这两个调用,但我不确定
任何帮助都将不胜感激。哦,关于这一点,我有很多话要说,包括我不会使用这两种类型的mixin,我只会使用普通模板。最后我会再谈这个问题
我会尽量做到相当全面,如果我描述了你已经知道的东西,我表示歉意,另一方面,我可能也会提供一些不相关的材料,以便提供全面的背景材料,以便更深入地理解
首先,mixin与模板mixin。mixin()获取字符串,将其解析为AST节点(AST btw是编译器用于表示代码的内部数据结构,它代表“抽象语法树”。foo()
是类似于FunctionCall{args:[]}
的AST节点,如果(foo){}
是类似于IfStatement{condition:Expression{arg:Variable>的节点{name:foo},body:EmptyStatement}
-基本上是表示代码每个部分的对象)
然后,它将解析的AST节点粘贴到出现mixin
单词的同一个插槽中。您通常可以将其视为复制/粘贴代码字符串,但有一个限制,即字符串必须在此处表示完整的元素,并且必须在mixin没有错误的同一上下文中可替换。因此,您不能这样做int a=bmixin(c)
要生成前面有ab
的变量,mixin必须自己表示一个完整的节点
一旦它粘贴到AST节点,编译器会将其视为代码都是最初在那里编写的。任何引用的名称都会在粘贴的上下文中查找,等等
另一方面,模板mixin实际上在AST中仍然有一个容器元素,用于名称查找。它实际上与编译器中的struct
或class
类似,它们都有一个作为一个单元保留在一起的子声明列表
最大的区别是,模板mixin的内容通常可以从父上下文自动访问。它遵循类似于类继承的规则,其中class Foo:Bar
可以看到Bar的成员,就好像它们是自己的一样,但它们仍然是分开的。您仍然可以像super.method()那样操作;
并独立于子对象的重写调用它
“通常”是因为超载和劫持规则而出现的。这里的深入分析和基本原理:
但它的不足之处在于防止第三方代码在添加新函数时悄悄地改变程序的行为,D要求程序员在使用点合并所有函数重载集,并且对运算符重载特别挑剔,因为它们已经具有默认行为t任何混音都将被修改
mixin template B(T) {
void foo(T t) {}
}
class A {
mixin B!int;
mixin B!string;
}
这与您的代码类似,但使用普通函数。如果您编译并运行,它将正常工作。现在,让我们将一个foo重载直接添加到:
mixin template B(T) {
void foo(T t) {}
}
class A {
mixin B!int;
mixin B!string;
void foo(float t) {}
}
如果您尝试使用字符串参数编译此函数,它实际上会失败!”错误:函数poi.a.foo(float t)不能使用参数类型(字符串)调用。为什么不使用mixin类型
这是模板混合的规则-请记住,编译器仍然将它们视为一个单元,而不仅仅是一组粘贴的声明。外部对象上的任何名称(这里是我们的类a
)都将被使用,而不是查看模板混合的内部
因此,它可以看到A.foo
,而不必费心去查看B
来查找foo。这对于覆盖模板混合中的特定内容很有用,但在尝试添加重载时可能会很麻烦。解决方案是在顶层添加一个别名
行,告诉编译器具体查看内部e需要给mixin一个名称,然后显式转发该名称:
mixin template B(T) {
void foo(T t) {}
}
class A {
mixin B!int bint; // added a name here
mixin B!string bstring; // and here
alias foo = bint.foo; // forward foo to the template mixin
alias foo = bstring.foo; // and this one too
void foo(float t) {}
}
void main() {
A a = new A;
a.foo("a");
}
现在它适用于float、int和string…但它也有点违背了模板混合的目的,即添加重载。您可以使用的一个技巧是在a
中放置顶级模板函数,它只会转发给混合函数…只是它们需要一个不同的名称来注册
这让我回到了你的代码。正如我所说,D对运算符重载特别挑剔,因为它们总是覆盖正常行为(即使正常行为是错误,比如在类中)。你需要在顶层明确它们
考虑以下几点:
import std.stdio;
import std.string : format;
mixin template define_opbinary(alias other_type) {
// I renamed this to opBinaryHelper since it will not be used directly
// but rather called from the top level
Matrix opBinaryHelper(string op)(Matrix!(other_type) other) {
if(op == "*") {
Matrix result;
if(this.columns == other.rows) {
result = new Matrix(this.rows, other.columns);
} else {
result = new Matrix(0,0);
}
return result;
} else assert(0, "Operator "~op~" not implemented");
}
}
class Matrix(T) {
T[][] storage;
size_t rows;
size_t columns;
const string type = T.stringof;
this(size_t rows, size_t columns) {
this.storage = new T[][](rows, columns);
this.rows = rows;
this.columns = columns;
}
void opIndexAssign(T value, size_t row, size_t column) {
storage[row][column] = value;
}
mixin define_opbinary!(int);
mixin define_opbinary!(uint);
// and now here, we do a top-level opBinary that calls the helper
auto opBinary(string op, M)(M rhs) {
return this.opBinaryHelper!(op)(rhs);
}
}
void main()
{
Matrix!int mymat = new Matrix!(int)(2, 2);
mymat[0,0] = 5;
writeln(mymat.type);
Matrix!uint mymat2 = new Matrix!(uint)(2, 2);
writeln(mymat2.type);
auto result = mymat * mymat2;
writeln("result.rows=", result.rows);
writeln("result.columns=", result.columns);
auto result2 = mymat2 * mymat;
writeln("result.type=",result.type);
writeln("result2.type=",result2.type);
}
我粘贴了完整的代码,但实际上只有两个更改:mixin模板现在定义了一个具有不同名称的助手(opBinaryHelper
),顶级类现在定义了一个显式的opBinary
,转发给所述助手。(顺便说一句,如果要添加其他重载,可能需要使用上面的别名
技巧,但在这种情况下,因为它都是在i上调度的。)