d插件与模板插件
原文
作者:亚当.
mixin()
带串,解析串
为ast
节点.
foo()
是类似FunctionCall { args: [] }
这样的ast
节点.而if(foo) {}
类似IfStatement { condition: Expression { arg: Variable { name: foo }, body : EmptyStatement }
.
然后将解析后的ast节点
粘贴到mixin
出现位置.但要求该串
在此必须表示
完整元素,并且在上下文
不会出错.插件
必须单独是个完整节点
.这样int a = bmixin(c)
是不行的.
粘贴后,编译器视为原本
在那写的,因而在相应上下文
中查找引用名
.
而模板插件
,另一方面,在ast
中仍然有个查找名字
的容器元素(a)
,(a)
与编译器中的构/类
类似,都有按单元
在一起的子声明
列表.
最大区别是,可自动从父上下文
中访问模板插件
内容.它遵循类似class Foo:Bar
中子可见父
的类继承
的规则,也可调用独立于子覆盖
的.super.method();
还有重载和劫持
,原理在此
但为了避免第三方代码
加新函数时默默
改变程序行为,D要求程序员
在使用点
合并所有重载函数集
,并且对插件
重载操作符
更严格,因为操作符
有默认行为.
mixin template B(T) {
void foo(T t) {}
}
class A {
mixin B!int;
mixin B!string;
}
上面是普通函数,现在加个重载
:
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)",
它会失败,为什么不用插件
呢.这是模板插件
的规则,编译器把它们视为一个单元
,而不仅是一组粘贴的声明.查看模板插件
前,先用外部对象(这里为A)
的名字
.
这里,他看见A.foo
了,就不再查找模板插件
里面了.这对覆盖
模板插件特定内容
很有用,但加重载
就麻烦了.解决方法是在顶级
用alias
告诉编译器
去查找模板插件
里面.先给插件一个名字
,然后显式转发该名字
给它.
mixin template B(T) {
void foo(T t) {}
}
class A {
mixin B!int bint; // 加个名字
mixin B!string bstring; //同上
alias foo = bint.foo; // 转发
alias foo = bstring.foo; //转发
void foo(float t) {}
}
void main() {
A a = new A;
a.foo("a");
}
现在对float,int和string
都管用,但有点违背
了模板mixin
加重载
的目的.你可在A
中放个顶级模板函数
,然后就转发至插件
了.只是需要不同名来注册
.
D
特别挑剔重载运算符,它们总是覆盖
正常行为,你要在顶级
明确说明.
import std.stdio;
import std.string : format;
mixin template define_opbinary(alias other_type) {
//重命名为opBinaryHelper,因为不直接用它
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);
// 现在我们做调用`帮助器`的顶级`opBinary`
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);
}
实际上只有两个更改,如果加重载
,得利用上面别名
方法.这里在if
处分发.它自动合并
帮助器.
而串插件
不需要这些,只是复制/粘贴
.
模板插件
有自己的域
来允许选择性覆盖等,这样更严格.
//`MatrixType`:接受任何矩阵,并从中提取其他类型的`矩类型`
//写代表类型模式,用逗号分隔占位符.
auto opBinary(string op, MatrixType : Matrix!Other_Type, Other_Type)(MatrixType other) {
alias PromotedType = typeof(T.init * Other_Type.init);
//让编译器来提升,我们只获取两种`类型乘积`的类型,只关注类型,由于不能`乘类型`,所以`乘值`.
static if(op == "*") {
Matrix!PromotedType result;
//编译时参数,可用`静如`.更灵活,如可在每个操作中改返回值.
if(this.columns == other.rows) {
result = new Matrix!PromotedType(this.rows, other.columns);
} else {
result = new Matrix!PromotedType(0,0);
}
return result;
} else static assert(0, "Operator "~op~" not implemented");
}
现在opBinary
可处理所有情况,不需要列举类型,不需要插件
.除非需要用子类
来虚覆盖
.可用static foreach
.
模板
中模式
是d
中更高级的D编译时反射
中概念.
另一篇参考
可见模板插件
类似自带域的插入构
.有时非常方便.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现