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,但对该简单的要求,编译器错误是非常不直观(一大堆错误,略了).
浙公网安备 33010602011771号