3

我真的很想使用 D,因为它的语言结构比 C++ 做了很多我关心的事情,但几乎是强制 GC(这里处理的问题 [排序]),稍微不那么强大的运算符重载(除了opDispatch.opDispatch是性感的),以及以下问题有点让我失望。

在 D 中是否可以从定义中拆分方法声明?如果是这样,怎么做?如果不是,为什么?

“如何”的激励示例:在二进制对象旁边提供一个小的接口函数头文件,就像 C 头文件和库一样,以便将实现隐藏在用户代码编写者的视线之外。偏好:不取决于用户代码是否已经破解了垃圾收集器或只是在没有运行时的情况下进行编译(例如在此处的评论中找到的)。

4

3 回答 3

4

如果我写一个像这样的 D 文件:http: //arsdnet.net/dcode/iface/test.d并且你用 dmd -c 编译,你会看到它没有错误;这是一个有效的 D 文件。该语言允许您编写没有实现的函数原型。

.di 文件就是这样,只是文件名不同。

然后给出main:http ://arsdnet.net/dcode/main.d如果你编译dmd main,它会在看到“import iface.test;”时自动搜索iface/test.d,找到那个.d文件,或者 .di 如果你重命名它但同样的东西,并得到你的接口定义。

dmd main 将因链接器错误而失败,因此我们需要实现它:http ://arsdnet.net/dcode/impl.d 注意:impl 不导入模块,因此它从不检查其他文件。保持 .di 和 .d 文件同步是这里的棘手部分之一,除非您自动生成接口文件,因为未定义的方法会产生链接器错误,但方法的顺序很重要:它必须匹配,所以这样做变量列表(如果有任何公共变量)。否则,使用代码和实现代码不会在类的布局上达成一致。

同样,它不会自动检查,实现文件根本不查看“头”文件,因此这与 C++ 的主要区别在于您编写头文件一次,然后在使用程序和实施文件。

您会注意到实现文件也列出了类等。D 不支持void MyClass::add(int a) {}C++ 必须在类外编写方法的语法。

据我所知,没有办法强制实现文件查找头文件,如果将两者都放在命令行上,则会得到:“错误:来自文件 iface/test.d 的模块 iface.test 与另一个模块冲突从文件 impl.d 进行测试”

推荐使用 .di 文件的方式是使用 dmd -H 自动生成它们。这会读取完整的实现文件并去除函数体,只留下定义。当他们说它是编译器的一个功能时,这部分可能是关键 - .di 文件是有效的标准 D,但通过编译器选项生成,不一定需要是其他编译器的一部分。

于 2013-08-06T13:09:05.833 回答
3

您可以使用 声明函数而不指定它们的实现extern,例如:

extern(D):
void foo(string name);

然后简单地提供一个带有链接实现的目标文件/存档。请注意,模块名称是函数重命名的一部分,因此头文件需要与编译模块相同的模块名称,或者您可以使用它extern(C)来禁用重载(排除重载并使用 C 调用约定)。

于 2013-08-05T23:12:47.903 回答
3

亚当已经解释了一切。我将尝试添加一个额外的示例:

假设您正在开发一个名为mylib的库,它具有函数 foo() 和 bar()。您的代码和库测试应用程序在开始时可能如下所示:

mylib.d - 接口文件

module mylib;

void foo();
void bar();

mylib_impl.d - 定义在这里

module mylib;

import std.stdio;

void foo() {
  writeln("foo()");
}

void bar() {
  writeln("bar()");
}

mylib_test.d - 一个测试应用程序

// To compile: dmd mylib_impl.d mylib_test.d
// NOTE: we do not compile the "interface" file mylib.d !!
module mylib_test;

import mylib;

int main() {
  foo();
  bar();

  return 0;
}

现在应该不难理解,我们拥有 .di 文件纯粹是为了方便和清晰。如果我们决定使用接口文件,我们会将 mylib.d 重命名为 mylib.di,将 mylib_impl.d 重命名为 mylib.d。mylib_test.d 将保持不变。

于 2013-08-06T14:48:02.787 回答