d的两个编译时

d的编译时

两个阶段:astctfe,ast为生成树,ctfe为编译时求值.其实很简单啦.ast树,必须通过语义给树加上意义,然后求值函数才能求值,对不对?不然,光是树,是没用的.
其中,模板变函数,遇见了,先造一个ast的模板,然后实例化时,就填充这个ast,然后加入整个生成树里面.
ctfe,在语义分析代码生成阶段.编译时求值就是把一些编译时已知可以求值的东西,求出来,这样运行时就不必再求了.
因而,ctfe不能访问ast阶段功能,同样,ast无法访问ctfe时功能.

模板变函数

首先变成ast(代表程序结构,及所有可知道的东西),此时的ast不含变量,内存,输入,输出.模板变函数,在ast操作阶段完成.因此必须知道编译时模板参数,另一个是静如,用于选择ast子树.

struct S(bool b)
{
    static if (b)//根据b选择
        int x;
    else
        float y;
}

ctfe(编译时求值)

其发生在语义赋值完成后,想想,这些树语义都没有,你拿什么去求值?
常量折叠,更复杂的函数,d都可以执行.基本上对纯函数都非常好.ctfe可简化一大堆没必要的动作.甚至字节码解释器来操作.
强制编译时求值

int complicatedComputation(int x, int y)
{
    return ...;//复杂计算
}

void main()
{
    int i = complicatedComputation(123, 456);
    //编译器可根据计算复杂度选择是否在编译时求值
    enum j = complicatedComputation(123, 456);
    //强制编译时求值
}

案例

int ctfeFunc(bool b)
{
    static if (b)
        return 1;
    else
        return 0;
}

enum myInt = ctfeFunc(true);

解决方法:
1:int ctfeFunc(bool b)().
2:

template Value(bool b)
{
    static if (b)
        enum Value = 1;
    else
        enum Value = 0;
}

3:

int ctfeFunc(bool b)
{
    if (b)//关键在后面的`enum`看见后要`编译时求值`

CTFE引擎实质是模拟运行时先算,这样运行时就不必再算了,ast->ctfe,然后生成简化的ast.可以一个个子树的ast,当然在所有可能语义相关的东西都准备好时,再ctfe.

案例

template MyTemplate(T)
{
    pragma(msg, "用T实例化=" ~ T.stringof);
    //没问题
}
//有问题的下个
int ctfeFunc(int x)
{
    if (x < 100)
        return x;
    else
    {
        pragma(msg, "bad value passed in");
        return -1;
    }//按道理,根本就没生成这个else块
    //但打印了两下,说明pragma(msg,...)是在ast构造阶段,还未赋值语义.因而不考虑其周围块是什么东西.运行,就在该阶段运行.而枚举编译时求值后,则可能没有了(该else块ast子树被删了).
}
enum y = ctfeFunc(50); 

案例

著名的__ctfe,在ctfe求值阶段为1,在运行时为0.

int[] buildArray()
{
    static if (__ctfe)  //错误例子,应改为下行,
    if (__ctfe)  //正确,`ctfe执行`
    {//在ctfe外部,其值恒假,编译器会删掉.
        int[] result;
        foreach (i; 0 .. 1_000_000)
            result ~= i;//ctfe中,~=即可附加
        return result;
    }//相当于我生成了一个快速版本,但后面我删掉了自己,ctfe是重量级的代码
    else
    {
        import std.array : appender;
        //运行时,用appender更快
        auto app = appender!(int[]);
        foreach (i; 0 .. 1_000_000)
            app.put(i);
        return app.data;
    }
}

出错原因:statif if 发生在ast构造时,__ctfe发生在语义赋值后

案例

void func(Args...)(Args args)
{
    foreach (a; args)//在ast操作阶段循环展开
    {//编译时,对每份参数生成一个子树,有N份ast.
        static if (is(typeof(a) == int))
        {
            pragma(msg, "is an int");//始终要打印
            continue;//ast操作阶段,未解释
        }
        pragma(msg, "not an int");
        else pragma(msg, "not an int");//解决方法.
        //始终要打印
    }//pragma,无视周围
}
void main()
{
    func(1);//1,ifti(隐式模板实例化,即推导出模板类型了)
}

每一类型列表时,不解释.

最后

import std.stdio;
void func(Args...)(Args args)
{
    foreach (arg; args)    //foreach类型列表
    {
        static if (is(typeof(arg) == int))
            continue;

        writeln(arg);

        static if (is(typeof(arg) == string))
            break;

        writeln("参到达循环尾", arg);
    }
}
void main()
{
    func(1, "abc", 3.14159);
}
//再看,比较:
import std.stdio;
void func(Args...)(Args args)
{
    foreach (arg; args) 
    {
        static if (is(typeof(arg) == int))
            continue;
        writeln(arg);
        static if (is(typeof(arg) == string))
            break;
        //因为串打断循环了,该为真?
        static assert(!is(typeof(arg) == string));
        //如`断`工作,则不应有这句,不应,应真?
        //要静断
    }
}
void main()
{
    func(1, "abc", 3.14159);
}
//这是编译器的输出,即经过静断这句话
test.d(16): Error: static assert  (!true) is false
test.d(21):        instantiated from here: func!(int, string, double)

foreachast操作阶段,未解释,简单视为节点,输出:

@safe void func(int _param_0, string _param_1, double _param_2)//代码生成阶段
{
        import std.stdio;/*展开*/ 
{
    {
        int arg = _param_0;
        continue;//下
        writeln(arg);
    }
    {
        string arg = _param_1;
        writeln(arg);
        break;//断
    }
    {//删掉了
        double arg = _param_2;
        writeln(arg);
    }
    }
}
//变成
void func!(int, string, double)(int __arg0, string __arg1, double __arg2)
{//代码生成期优化掉了,
    writeln(__arg1);
}

你搞懂了吗?

posted @   zjh6  阅读(14)  评论(0编辑  收藏  举报  
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示