我不确定如何解释这一点,所以请让我澄清任何没有意义的事情。我有一个接口和一个模板函数,它返回基于编译时参数返回匿名内部类的函数:
interface MyInterface {
void getName();
}
MyInterface function() getMyInterfaceFactory(string name)() {
return function() {
return new class MyInterface {
void getName() { //Do something involving name here }
};
};
}
现在,getMyInterfaceFactory()
曾经是getMyInterface()
,它曾用于直接返回匿名对象。一切都很好。当我添加工厂函数时,我从Object:
object.Exception.....(102): need opCmp for class mymodule.getMyInterfaceFactory!("someargument").getMyInterfaceFactory.__funcliteral14.__anonclass13
所以,我查看了druntime源代码中的抛出线,它看起来像是opCmp for Object的默认实现。我不是在任何地方比较工厂功能或MyInterface
。我是将工厂存储为字符串索引关联数组的值,但是当我将匿名类直接存储在该数组中时,只有在我开始存储函数时才需要opCmp。如果我插入一个opCmp(使用内存地址),一切似乎都能正常工作,但MyInterface并没有真正的可比性,所以除非必须这样做,否则我不愿意这样做。
如果可能的话,我想知道为什么/在匿名类上调用opCmp,以及如何防止或解决它。
注意:Object中opCmp的默认实现包括隐含引用错误的注释,注释掉的内存地址比较,然后是抛出版本。
谢谢!
编辑:我应该提一下,我尝试了windbg和ddbg来准确追踪opCmp被调用的位置,但两种情况都失败了。 Windbg没有提供任何有用的信息,因为它固执地拒绝加载任何符号,ddbg加载符号,但是在初始化期间发生异常(在静态模块构造函数之后但在main之前)并且可能ddbg无法访问druntime符号?
答案 0 :(得分:0)
更新:我在玩具示例中特别难以复制opCmp错误,但我想我已经弄清楚发生了什么。
似乎创建匿名内部类继承匿名函数内部的接口是错误的(图)。具体来说,匿名类在虚函数方面表现不佳。即使定义了opCmp,我也遇到了toString和默认构造函数的错误,并且成员只是什么都不做(但是在调用时不抛出或者错误)。 __traits(allMembers, MyInterface)
返回预期信息,__traits(allMembers, typeof(anonInstance))
也是如此,但调用频繁列出的成员不起作用。奇怪的。
但是,如果我使用抽象方法将接口更改为类,则会解决opCmp错误,匿名类的行为与预期一致等等。我对编译器不太了解,但我认为在编译期间会构建一个符号表,将虚函数名映射到存储在vtbl中的内存地址。我认为发生的事情是,返回从接口派生的匿名类时生成的映射会有所不同。这是可能的,因为接口支持多重继承,因此不能规定绝对的vtbl映射。但是,类可能要求所有继承者都遵循相同的映射方案(我不知道他们是否这样做,但他们可以),因此匿名类不能以不同的映射结束。
再说一次,我真的不确定,但它似乎符合症状,即使我没有在任何地方使用它,也会调用opCmp。我认为这不是特定的opCmp问题,我认为Object中定义的所有虚函数都是易受攻击的。我能够通过以下方式支持这一点:
testopcmphelper.d
interface TestInterface {
string helloWorld();
}
class TestClass {
abstract string helloWorld();
}
testopcmp.d
import testopcmphelper;
import std.stdio;
void invokeFn(TestInterface function() f) {
auto t = f();
auto s = t.helloWorld();
writeln(s);
}
unittest {
auto f = function() {
return new class TestInterface {
string helloWorld() {
return "Hello World!";
}
};
};
invokeFn(f);
}
void invokeFn(TestClass function() f) {
auto t = f();
auto s = t.helloWorld();
writeln(s);
}
unittest {
auto f = function() {
return new class TestClass {
string helloWorld() {
return "Goodbye World!";
}
};
};
invokeFn(f);
}
打印哪些:
src.utilities.testopcmp.__unittest2.__funcliteral1.__anonclass10
Goodbye World!
表示invokeFn(TestInterface)
正在调用Object.toString
而不是TestInterface.helloWorld
。
如果我犯了错误,我将把这个问题暂时搁置一天。我可能会将此报告为DMD中的错误。我将通过仅使用匿名工厂函数基类型的抽象类来解决此问题。 TL; DR似乎是一个错误。