8

当我遇到一个有趣的问题时,我正在为 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!为什么?因为我必须假设它需要的所有成员都存在,除非我可以在运行时证明不存在——这为时已晚。所以程序试图调用front,popFrontemptyon result,使我的程序崩溃。

我想不出办法来解决这个问题。有人有想法吗?

4

5 回答 5

2

您正在尝试使两个根本不同的概念一起工作,即模板和动态类型。模板非常依赖静态类型,isInputRange 通过检查类型具有哪些属性或方法来工作。您的动态类型在编译时被视为具有每个属性或方法,因此它被视为满足每个静态鸭子类型接口。因此,要使 Dynamic 在静态类型的环境中工作,您必须在某些地方提供更多的静态信息。

我可以看到的一些解决方案:

  1. 为大量使用的函数提供您自己的动态类型实现。您遇到的整个问题是由于您尝试使用假设静态类型和动态类型的通用函数。

  2. 显式地使动态范围内的字符,并照顾自己转换为基础数据的字符串。(如果 isInputRange 问题不存在,您无论如何都必须有一个自定义的 toString 方法,否则它的结果将再次是动态类型)。这可能会使 writeln(d); 工作。

  3. 为动态提供包装器,允许您将动态类型传递给各种模板化函数。(那些只会展示一个静态接口并将所有调用转发到动态)。

例如:

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. 将成员模板添加到 Dynamic,它允许静态禁用某些成员函数名称。

例如:

static assert(!isForwardRange!(typeof(d.without!"save")));
于 2011-08-22T19:14:26.380 回答
1

你能为 isInputRange 提供一个重载吗?像这样的东西(注意我没有看过 isInputRange 的实现):

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

如果这是由您的 dynamic.core 提供的,我认为应该在 std lib 之前选择这个重载。

于 2011-08-18T16:39:04.423 回答
1

使用std.variantwhich 实现动态类型所需的一切有什么问题(以及相当多的语法糖)

于 2011-08-18T07:51:41.220 回答
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

于 2011-10-24T18:05:38.560 回答
0

正如您所说,对于一般情况,Dynamic 必须在编译时接受任何方法查找。假设您可以阻止 isInputRange 谓词评估为真,现在当您尝试从输入范围创建动态时将生成错误代码。

我不认为这是可以解决的,至少不能以一般的方式解决。在这种特殊情况下,我能想到的最佳解决方案是 Dynamic 提供它自己的 toString 版本,而 writeln 更喜欢 inputRange 专业化。我相信 writeln 目前不这样做,至少不是针对结构,但它可能应该这样做。

另一个折衷方案是在 opDispatch 约束中禁止一些方法,例如 popFront,而 Dynamic 将提供 opIndex 或成员对象来访问这些特殊情况。这可能不像听起来那么糟糕,因为特殊情况很少见,使用它们会导致明显的编译器错误。

我认为为 Dynamic 挽救这种方法解析的最佳方法是修复 writeln 并接受 Dynamic 不适用于所有模板化代码。

于 2011-08-22T18:04:25.370 回答