d模板化转串和类继承
至少在应用
的开发和调试
阶段,toString
是一种转发
有关对象文本信息
给用户
的,至关重要的方法
.(向该线程道歉,我再次考虑该问题,并借用了类名
):
class Animal {
override string toString() {
return "Animal()";
}
}
class Dog : Animal {
override string toString() {
return "Dog()";
}
}
class Bulldog : Dog {
bool sunglasses;
override string toString() {
return format("Bulldog(cool: %s)", sunglasses);
}
}
现在,即使是最顽固的GC
爱好者,也应该同意,可能会出现此情况
:在高强度应用
循环中,可能每帧
会数百次
调用toString
,每次分配
是非常不可取
的.我不会费心讨论缓存值
问题,因为指定的用例
和复杂性
,在此不重要.幸好,std.format
提供了非[必要]
分配的替代方案:
import std.format;
import std.range.primitives;
// 强制,请见附注.
class Animal {
void toString(W)(ref W writer) if (isOutputRange!(W, char)) {
writer.formattedWrite("Animal()");
}
}
class Dog : Animal {
void toString(W)(ref W writer) if (isOutputRange!(W, char)) {
writer.formattedWrite("Dog()");
}
}
class Bulldog : Dog {
bool sunglasses;
void toString(W)(ref W writer) if (isOutputRange!(W, char)) {
writer.formattedWrite("Bulldog(cool: %s)", sunglasses);
}
}
但是,有个问题:
void main() {
auto animals = [new Animal, new Dog, new Bulldog];
foreach (animal; animals) {
animal.writeln;
}
}
由于该函数
是模板化的,所以不可能是虚的
,因此即,当仅在运行时
已知类型时,要确定
调用正确的toString
.
当前方法有些笨拙
,如涉及手动注册
所有相关类
,并使用通过运行时typeid``标识
对象的处理器函数
来代理toString
,还要查找
要调用的相关正确函数
.很难自动
确定要注册的类
,尤其是,跨多个
文件相互继承
的类,及以后添加
的类,并且增加了复杂性
和维护量
.
考虑到toString
的重要性
,如果编译器可用某种特例或路径
来辅助它,就太好了(尽管更通用
的方法也会很有趣).
我还想过,如果TypeInfo_Class
可存储匹配,可在运行时
调用的toString
函数的类的引用,不是很好吗?,但考虑到它是模板
,这是不行的.
我不知道如下
是否最佳.最终会冗余
检查.由于需要考虑两个级别
的间接寻址(正在传递什么OutputRange
模板?及正在编写
对象的运行时类型
?),我必须发挥一点创意.
由于无法清楚
地预测toString(W)
的哪些匹配项.使用isOutputRange
(且可能是隐藏类型
),我已直接把注册类
整合到toString
处理器中,但如果继承类
分散到不同的文件中且不是首先
调用,这仍然可能会失败
.
module util.tostringmaster;
import std.string;
import std.traits;
import std.format;
import std.range.primitives;
//待办 :
//确保toString模板与期望的W版兼容
//如果不存在匹配的`toString`,则进行适当的`错误处理/回退`
abstract final class ToStringMaster {
private:
static abstract class ToStringBase {}
static abstract class ToStringWriter(W) : ToStringBase {
void writeWith(ref W writer, Object obj);
}
static final class ToStringHolder(W, T) : ToStringWriter!W {
override void writeWith(ref W writer, Object obj) {
T t = cast(T) obj;
t.toString(writer);
}
}
static ToStringBase[string][string] table;
static void[0][string] writerTable;
static void registerAll(W, string MOD)() {
enum WNAME = fullyQualifiedName!W;
enum MNAME = WNAME~":"~MOD;
//pragma(msg, "REGISTERALL "~MNAME);
if (MNAME in writerTable)
return;
writerTable.require(MNAME);
{
mixin(`import `~MOD~`;`);
mixin(`alias ALL = __traits(allMembers, `~MOD~`);`);
static foreach (sym; ALL) {{
mixin(`alias SYM = __traits(getMember, `~MOD~`, "`~sym~`");`);
static if (is(SYM == class)) {
alias CLASS = SYM;
alias TOSTRING = __traits(getMember, CLASS, "toString");
static if (__traits(isTemplate, TOSTRING)) {
register!(W, CLASS);
}
}
}}
// 显式注册
//register!(W, Animal);
//register!(W, Dog);
//register!(W, Bulldog);
}
}
static void register(W, T)() {
enum WNAME = fullyQualifiedName!W;
enum TNAME = fullyQualifiedName!T;
table.require(WNAME);
table[WNAME][TNAME] = new ToStringHolder!(W, T);
}
static void redirect(W, T)(ref W writer, T obj) {
enum WNAME = fullyQualifiedName!W;
static if (!(WNAME.indexOf("__lambda") >= 0)) { // 不要注册hasToString等
registerAll!(W, moduleName!(T));
scope auto tname = typeid(obj).name;
if (auto wp = WNAME in table) {
if (auto p = tname in *wp) {
auto tsh = cast(ToStringWriter!W) *p;
assert(tsh, "无效ToStringWriter: "~WNAME);
tsh.writeWith(writer, obj);
return;
}
}
writer.formattedWrite("<Unknown:%s>", tname);
}
}
}
// 插件替代项而不是闭包
/* enum string ToStringReal = q{
static if (!(fullyQualifiedName!W.indexOf("__lambda") >= 0)) { // hasToString
static assert(__FUNCTION__.endsWith(".toString"), "在toString中仅包含该插件");
if (typeid(this) !is typeof(this).classinfo) {
ToStringMaster.redirect(writer, this);
return;
}
}
}; */
void realToStringOr(T,W)(T obj, ref W writer, scope void delegate() origDG = null) if (isOutputRange!(W, char)) {
if (typeid(obj) !is T.classinfo) {
ToStringMaster.redirect(writer, obj);
return;
}
if (origDG !is null) origDG();
}
class Animal {
void toString(W)(ref W writer) if (isOutputRange!(W, char)) {
realToStringOr(this, writer, {
writer.formattedWrite("Animal()");
});
}
}
class Dog : Animal {
void toString(W)(ref W writer) if (isOutputRange!(W, char)) {
realToStringOr(this, writer, {
writer.formattedWrite("Dog()");
});
}
}
final class Bulldog : Dog {
bool sunglasses;
void toString(W)(ref W writer) if (isOutputRange!(W, char)) {
//如果确定不会继承该类,就不需要realToStringOr,但...
writer.formattedWrite("Bulldog(cool: %s)", sunglasses);
}
}
void main() {
auto animals = [new Animal, new Dog, new Bulldog];
foreach (animal; animals) {
animal.writeln;
}
}
//输出:
Animal()
Dog()
Bulldog(cool: false)
附注:要使用模板化toString
版本,必需导入std.range.primitives
,但对该简单的要求,编译器错误
是非常不直观(一大堆错误,略了).
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现