d亚当替换工厂模式
对象工厂替代方案
一般,需要无需用模块构造器
触发d运行时
的挑剔循环检测
的方法来注册
工厂.很多时候,混合模块
构造器正是想要
方法,但它有全局全开或全闭
的循环
检测算法.
要全局
关闭它,请在Main
文件中,添加以下代码行
:
extern(C) __gshared string[] rt_options = ["oncycle=ignore"];
或运行
程序时传递--DRT-oncycle=ignore
开关.
但是,如果正在制作
库,则可能不想全局
关闭它,因为会破坏
事物.你想要更局部
的方法.
唉,D
当前的模块构造器
没有提供在局部说"这很好,相信我
"的方法.可在模块信息
构造器列表中添加新数组
来实现它,根据是否标记
循环安全
,来分离它们.
目前,编译器组合
模块的所有静态构造器
到一个调用所有构造器
的生成函数
中.然后ModuleInfo
引用此生成函数
,以便初化时druntime
调用它.需要两个
生成函数,一个取
当前行为,另一个顺序无关运行
.
应该添加
它到语言中.
但是今天可做什么呢?
好吧,用ldc
或gdc
,可在自定义链接器节
中放引用函数
,如果看看ModuleInfo
实现,它也是这样.(在某些平台上,它查找可执行
文件中的.minfo
节,在其他平台上,它发出调用特定的D注册
函数的crt构造器
).但是crt构造器
如何实现的呢?通过带指针
的链接器节
!
事实上,用crt构造器
是一个可能.发出其中一个,它会构建一个列表
,然后稍后从普通D模块构造器
调用该列表.不过,必须小心从crt构造器
调用的内容,-druntime
已出来了,甚至C运行时
的其他部分
,可能还没有完全初化,可能还有共享库加载锁
.
有个优先级编号
,但D禁止你设置该编号
.即使你可以设置,它也不能解决
所有问题.我相信当前的实现
确实可让你侥幸调用realloc
,所以这是可相当安全
构建列表方式.只要不乱搞,你没事.
但,我想深入
链接器节.转储代码:
module sections.demo;
import core.internal.elf.io;
import sections.demo2;
//用此注册处理器
mixin template Register(alias fn) {
import ldc.attributes;
@section(".mytest") __gshared void function() registered = &fn;
}
//必须注册`挂名`来欺骗链接器,在此搞一个
void specialCtor() {
import std.stdio;
writeln("hello special ctor");
}
mixin Register!specialCtor;
// impl来取它
alias SectionDataHandler = void function(scope const(ubyte)[] data);
int findSectionInfo(SectionDataHandler handler) {
import core.internal.elf.dl;
SharedObject exe = SharedObject.thisExecutable();
ElfFile file;
if(!ElfFile.open(exe.name.ptr, file))
return 1;
foreach(index, name, sectionHeader; file.namedSections) {
if(name == ".mytest") {
if(auto offset = sectionHeader.shdr.sh_addr) {
auto beg = exe.baseAddress + offset;
auto size = sectionHeader.shdr.sh_size;
auto data = beg[0 .. size];
//愚蠢无用的`NOGC`限制我,仅当体为`NOGC`,可标记`NOGC`
alias BypassAttribute = void function(scope const(ubyte)[]) @nogc nothrow;
(cast(BypassAttribute) handler)(cast(ubyte[]) data);
} else {
// 未映射
}
}
}
return 0;
}
void main() {
import std.stdio;
// 从列表中调用函数
static void handler(scope const(ubyte)[] data) {
//必须引用`神奇节`中的一个变量,否则`--gc-sectionsarg`默认链接器会去掉它!
auto reference = registered;
reference();
auto fns = cast(void function()[]) data;
foreach(fn; fns) {
// 不要重复调用根引用
if(fn is reference)
continue;
fn();
}
}
writeln(findSectionInfo(&handler));
}
还有第二个文件
注册了:
module sections.demo2;
import sections.demo;
void otherCtor() {
import std.stdio;
writeln("here too");
}
mixin Register!otherCtor;
注意,我用了ldc.attributes
,所以该演示只是LDC
,但gdc.attributes
工作方式相同,所以可很容易地支持这两个
编译器.不过,DMD
现在没用.
编译并运行
它(仅在Linux
上测试!也应该在BSD
上运行,但Mac
和微软
等需要进一步调整):
$ ldc2 -i=core.internal.elf sections.d sections2.d && ./sections
hello special ctor
here too
0
注意,我用-i=core.internal.elf
来引入该ElfFile
类型.否则,不会在构建
中包含
它(编译器假定,因为它是druntime
的一部分,它预编译
在库中,但也可能没有这些指定的,加它至构建使它更易工作).
否则,只需基本编译和运行
,就运行了.
在此基础
上创建东西,就像如果注册
了一个变量
,可自动
生成附加它到数组
的函数.或,可设置该节自身为特定类型
而不是函数列表
,且直接不运行
就用它;链接器节
只是提前
连接的静态数据
,因此无需
进一步操作,可切片
并转换为另一个
类型并用它.
甚至可这样注册http
路由等,且知道在可执行
文件中查找
哪个节的特定
检查器,甚至可不运行
程序就打印
出它们!
可用静态数据
做很多很酷的,可执行
文件节是进入
该世界的窗口
.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现