2011-08-18 78 views
8

當我遇到一個有趣的問題時,我正在爲D實現一個動態類型庫。在靜態類型語言D中使用動態類型輸入

現在,我成功地創建了一個名爲dynamic()的函數,該函數返回對象的動態版本。

例如:

import std.stdio, std.dynamic.core; 

class Foo 
{ 
    string bar(string a) { return a ~ "OMG"; } 
    int opUnary(string s)() if (s == "-") { return 0; } 
} 

void main(string[] argv) 
{ 
    Dynamic d = dynamic(new Foo()); 
    Dynamic result = d.bar("hi"); 
    writeln(result); // Uh-oh 
} 

我整個運行的問題是,writeln嘗試使用編譯時反射弄清楚如何對待result的事實。

它嘗試的第一件事是什麼? isInputRange!(typeof(result))

問題是,它返回true!爲什麼?因爲我必須假設它需要的所有成員都存在,除非我能在運行時證明,否則這太遲了。所以程序試圖在result上調用front,popFrontempty,導致我的程序崩潰。

我想不出一種解決方法。有人有想法嗎?

回答

1

什麼是錯用std.variant它實現了你所需要的動態類型(以及相當多的語法糖)

+1

'std.variant'不支持具有任意字段類型。 –

+0

@cyber你是什麼意思? –

+0

OP想要創建一個對象,其中'obj.anything'在編譯時有效(即使它在運行時可能不是有效的)。正如我所見,'std.variant'中的任何內容都不允許這樣做。 –

1

你能提供isInputRange過載?像這樣的東西(請注意,我沒有看過isInputRange執行):

template isInputRange(T : Dynamic) { 
    enum isInputRange = false; 
} 

如果這是你的dynamic.core提供的,我想這應該超載的標準庫前一個選擇。

+1

沒問題,但問題是這需要提前瞭解各種預防措施。顯然,它不適用於最終使用我的圖書館的人...... – Mehrdad

+0

不幸的是,這個技巧不起作用,std.stdio無法挑選專業化。 – Lutger

0

對於一般情況,Dynamic必須在編譯時接受任何方法查找,正如您所說的。假設您可以暫時阻止isInputRange謂詞評估爲true,那麼當您嘗試從輸入範圍創建Dynamic時,將生成錯誤的代碼。

我不認爲這是可以修復的,至少不是一般的方式。在這種特殊情況下,我能想到的最佳解決方案是Dynamic提供了它自己的toString版本,並且writeln會優先考慮inputRange專業化。我相信writeln此刻不這樣做,至少不是爲了結構,但它可能應該。

另一種折衷辦法是不允許在opDispatch約束的一些方法,如popFront,而不是動態將提供opIndex或成員對象訪問這些特殊情況。這可能並不像聽起來那麼糟糕,因爲特殊情況很少發生,使用它們會導致明顯的編譯器錯誤。

我認爲最好的辦法挽救這種方法分辨率的動態是修復writeln和接受,動態不會與所有的模板代碼工作。

+0

使writeln更喜歡toString over isInputRange的問題是,每個類都從Object繼承了一個通用的toString方法,它只輸出類名稱。因此,如果writeln被改變了,它將不得不對結構和類進行不同的處理。 – tgehr

2

你試圖讓兩個根本不同的概念一起工作,即模板和動態類型。模板非常依賴靜態類型,isInputRange通過檢查類型具有的屬性或方法來工作。您的動態類型在編譯時被視爲具有每個屬性或方法,但它被視爲每靜態鴨子鍵入界面。 因此,要使Dynamic在靜態類型的環境中工作,必須在某些地方提供更多的靜態信息。

一些解決方案,我可以看到:

  1. 對頻繁使用的功能,提供自己的動態類型實現。您遇到的全部問題都是由於您嘗試使用假設使用靜態類型的靜態類型的泛型函數造成的。

  2. 明確地使動態範圍的字符,並關心自己轉換爲底層數據的字符串。 (如果isInputRange問​​題不存在,你必須有一個自定義的toString方法,否則它的結果將再次是動態類型)。這可能會寫(d);工作。

  3. 提供動態包裝,允許您將動態類型傳遞到各種模板化函數中。 (那些只會展示一個靜態接口並將所有調用轉發給Dynamic)。

例如:

Dynamic d; 
// wrap d to turn it into a compile-time input range (but NOT eg a forward range) 
Dynamic d2=dynamic(map!q{a*2}(dynInputRange(d))); 
// profit 

4。將成員模板添加到動態,從而允許靜態禁用某些成員函數名稱。

如:

static assert(!isForwardRange!(typeof(d.without!"save"))); 
0

你有沒有看着std.variant?

import std.stdio, std.variant; 

class Foo { 
    string Bar(string a) { 
     return a ~ " are Cool!"; 
    } 
} 

void main() { 
    Variant foo = new Foo(); 
    Variant result = foo.peek!Foo.Bar("Variants"); 

    writeln(result); // Variants are Cool! 
} 

http://www.d-programming-language.org/phobos/std_variant.html