2011-08-18 50 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