7

我想这样做this支持),但我遇到了一个小问题(因为你不那么令人头疼的不愉快而淡化了)。

假设我是一个库编写者,我在 D 文件中有这些函数:

module mod_a;
import std.stdio;
void run(T)(T v) { writeln("Jigglypuff!"); }
void runrun(T)(T v) { run(v); }

我在另一个模块中有客户端代码,我在其中尝试重载run和调用runrun

import mod_a;
void run(T:double)(T v) { writeln("Wigglytuff!"); }
void main() { runrun(1.0); }

此代码导致“Jigglypuff!” 被打印而不是' Wigglytuff!',这是有道理的,因为定义runrun只能在其模块中看到它可用的未进化的非专业形式。但是,我(和客户端代码)希望看到“Wigglytuff”而不是“Jigglypuff”。

在 C++ 中,我会namespace mod_a { ... }围绕 run 的专门化进行说明,当试图确定runrun调用的定义时,应该检查客户端代码的运行以及我的库代码,欢迎伴随这种行为而来的蠕虫罐头。

是否有一种惯用的 D 方式来组织此功能,以便该功能run可能被故意劫持?具体来说,我想模仿 C++ 的全局函数在特殊特化下的行为方式。

4

3 回答 3

2
//untested

module mod_a;
import std.stdio;
void run(T)(T v) if (!is(T : double)) { writeln("Jigglypuff!"); }
void runrun(T)(T v) { run(v); }

import mod_a;
void run(T)() if (is(T : double)) { writeln("Wigglytuff!"); }
void main() { runrun(1.0); }
于 2013-08-20T19:50:43.597 回答
1

在此示例中,您是库的创作者,mod_a因此修改它相对容易。但我不禁想到你不是图书馆作者的情况。

在这种情况下,图书馆的实际作者可能会很高兴你不能只做你想做的事情......或者积极地想要支持你想做的事情。

假设库作者希望您能够“劫持”他/她在其实现中使用的函数。他或她可能会采取不同的方式;我会。

这是我相信你所链接的封装故事的一个领域,我刚刚读到的正是描述了如何在此处实现与你想要的相反的情况。这种类型的东西尖叫它需要合同编程。

作为库作者,我可能会为您提供一个接口,也可能是一个抽象类,甚至可能是一两个具体的实现,您可以使用它们来完成您的工作。其他人可能会添加需要特定实现作为参数的模板或运行时参数。然而,其他人可以在混合中添加一个惰性字符串委托。

(我的)结论:作为图书馆作者,有多种选择可以使您想要的成为可能。如果您首选的库无法实现,您可能最终会提交功能请求。

于 2013-08-20T20:53:09.473 回答
0

我发现了一种使用 mixins 将搜索到的命名空间从库代码移动到用户代码的奇怪方法。从库编写者的角度来看,这是一种额外的功能和一种额外的模板类型;从用户的角度来看,这是一个额外的(有点烦人的)代码行以及一个额外的函数调用,每个实例在其模块之外具有全局“方法”。考虑到这是一种显式覆盖语言功能的方式,我认为这非常好。

module wrappers;
mixin template wrapmix()
{
  struct Wrap(T)
  {
    T* val;
    auto opDispatch(string Name, A...) { return mixin("(*val)."~Name~"(a);"); }
  }
  auto wrap(T)(ref T val) { return Wrap!T(&val); }
}

然后使用它创建一个类型,该类型在它为调度声明的任何模块中搜索。

import mod_a;
import wrappers;

mixin wrapmix; ///< magic

void main() {
  double val = 1.0;
  runrun(wrap(val)); ///< note the 'wrap' call
}

虽然上面的代码没有经过明确的测试,但我在 GDC 上测试了通用方法,它似乎可以工作。

哦,还有一个缺点,写 this 只处理左值引用,但可以通过wrapin module的重载来解决wrappers

于 2013-09-16T18:22:30.133 回答