d模板发射

原文
不同的版本定义编译不同的库灾难.
根本原因是编译后的目标文件(或)中内容,与编译器导入时认为的内容不匹配.使用与导入时不同的标志构建模块时,可能会不同:
库模块:

module lib;

version(with_func)
void func() {}

应用模块:

module app;
import lib;

void main() { func(); }

编译:

$ dmd -c -version=with_func lib.d
$ dmd app.d lib.o
//`app.d(5)`:错误:未定义的`func`标识

本例中,函数被编译到目标文件中,但在生成应用时,编译器并不知道它是可用的.
或如下编译并得到链接器错误:

$ dmd -c lib.d
$ dmd -version=with_func app.d lib.o
//未定义`_D3lib4funcFZv`引用.

编译器认为它在那里,但实际上它并不在那里,这是因为库是用一组不同标志构建的,编译器不知道该点.
-version,-debug,-unittest,-dip1000,-checkaction,-check=contracts=x,-release等编译标志,都可这样.
此外,由于限定符是全局应用至模块及导入的库模块.要用库,还必须传递冲突的标志!
-unittest,它设置了全局版本,可能会使编译器错误假定某些测试助手已编译.

特别是模板发射错误

模板发射错误是编译器不知道哪些预编译的,哪些是导入的.事实是需要改变构建模式.

编译器看到需要模板,但认为已编译的目标文件中已有了,跳过了再次输出它.当它是正确的,这节省了一些编译时间.但当它是错误的,会得到链接错误(甚至,由于编译器标志而不是模板实例,使实例的ABI期望的不同).
最简单方法,是在使用点总是发射.因为模板是不存在的,仅在实例化时才生成代码.这也是-allinst开关的作用(输出所有实例),但会很慢.

根本问题编译器看到的源文件导入链接器看到的目标文件之间不匹配,方法就是去掉不匹配.

最激进的是,链接器驱动的构建

略...

接近现状的D索引文件

.di文件从类似"头文件"文件重新设想为更类似索引文件,来紧密耦合生成的目标文件与源码文件,告诉编译器两者中的精确内容.
工作原理:生成目标文件(共享静态库)时,生成.di文件.同时分发他们.di与本次构建紧密耦合.这样,代码中不需要版本块,也不应有.这样,第1例就不可能.
with_func构建进库时,无论传递版本标志,di文件总应公开它.
输出的函数原型应表明,在目标文件中有它.可用extern来完成.不需要函数体.
D索引文件不用来给人阅读,编译器可自由地按CTFE结果缓存它,用字面值替换ctfe调用.也应提供插件函数的原型.示例:

// 源码
int foo() { return 55; }
enum bar = foo();
int baz = foo();
mixin("int m() { return bar; }");

编译为:

// 索引文件
extern int foo();
enum bar = 55; // 在文件中缓存`ctfe`结果
extern int baz; // 初化器的`ctfe`结果在`目标`文件中,因此把它标记为`extern`
extern int m(); // 插件在`目标`文件中,所以只输出它的`引用`

如上,针对函数.模板呢?好吧,模板实例只是另一个函数.

// 源码
T foo(T)(T i) { return i; }

void useit() { int a = foo(5); }

编译为:

extern T foo(T:int)(T i) @safe pure nothrow;
// 要指定推导的属性
extern void useit();

// 或类似:
template foo(T:int) { extern T foo(T i); }

信息放在该索引文件中,这样编译器就可确切知道,在目标文件中,有该特定参数模板实例.C++在这方面做得相当成功.
注意,这里也列举了函数的所有推导属性.即,尽管无可供分析的源码,但仍工作.对特定实例,它们总是相同的,所以这很好用,且需要创建正确的mangle备份.

如果没有可用源码,如何在新类型上实例化模板?!这与CTFE相同,即使已编译源码目标文件中,D也需要源码.
1,已在.di中,2,知道在找.
1)时,闭源库,可根据uda来找,但始终拥有,因为必须要满足CTFE,但可能混淆(加密)源码.
2)时,D索引文件同时链接到目标文件,因此在文件中找不到内容时,编译器不会报错,而是加载模块的原源文件,并从那里取出.注意,因为是同一模块的不同方面,不能导入原始模块.解析di文件可能更快.
D索引文件,也可输出类型定义,输出所有di文件,通过语义,还可加速.

模板发射,最重要的是:
0,从索引文件中去掉版本,这样,只要构建与其他全局标志无关,始终可看到与构建版本相同的文件.
1,在索引文件中显式列举目标文件中的模板实例.
2,仍需要访问源码来创建新实例(在目标相关索引文件中按列举,生成实例时,编译器也需要合并重载集)及运行CTFE(今天的.di文件中,已破坏这些实例).
其他只是提高性能.但这是不同的性能提高.编译器不再猜测了,它只是确定性推迟静态决定了的工作.即使文件更大,缓存ctfemixin输出可产生更快构建.
这可同时,消除版本不匹配,停止发射模板,且可更快的构建.

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