2012-03-17 121 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似乎是一個錯誤。