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编译时反射中概念.
另一篇参考
可见模板插件类似自带域的插入构.有时非常方便.

posted @   zjh6  阅读(13)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示