2012-03-17 117 views
3

我不确定如何解释这一点,所以请让我澄清任何没有意义的东西。我有一个接口,它返回返回基于编译时的说法匿名内部类函数模板函数:现在为什么我要为一个匿名类“需要opCmp”?

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为对象的默认实现只是抛出。我没有比较工厂功能或任何地方的MyInterface。 I am存储工厂作为字符串索引关联数组的值,但是当我直接在该数组中存储匿名类时,只有当我开始存储函数时,才需要opCmp。如果我插入一个opCmp(使用内存地址),一切似乎都正常工作,但MyInterface并没有真正的可比性,所以我宁愿不这样做,除非必须这样做。 如果可能的话,我想知道为什么/在哪里opCmp被匿名类调用,以及如何防止或解决它。

注意:Object中opCmp的默认实现包含一个模糊的引用bug的注释,注释掉的内存地址比较,然后是引发版本。

谢谢!

编辑:我应该提到,我试过windbg和ddbg来追踪opCmp被调用的位置,但在两种情况下均失败。 Windbg没有提供任何有用的信息,因为它顽固地拒绝加载任何符号,ddbg加载符号,但是在初始化过程中(在静态模块构造函数之后但在main之前)发生异常,并且可能ddbg无法访问druntime符号?

+0

我对这个问题了解不多,但是一种解决方法是将一个通用的opCmp实现放在一个模板mixin中(您可以使用'typeof(this)'来推断这些参数的类型)。然后,您可以用一行代码将它添加到您的课程中。 – 2012-03-17 18:05:45

+0

好点,如果我无法避免定义opCmp,我可能会尽量减少它。谢谢! – Tim 2012-03-17 18:15:18

+0

为什么有一个void返回类型,你返回的东西? – 2012-03-17 23:25:42

回答

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似乎是一个错误。