【问题标题】:Pimpl-idiom in the D programming languageD 编程语言中的 Pimpl-idiom
【发布时间】:2013-12-09 11:42:41
【问题描述】:

D 有一个很棒的模块系统,与 C++ 相比,它大大减少了编译时间。根据文档 D 仍然提供不透明的结构和联合以启用 pimpl 习惯用法。我的问题是:如何在一个模块中声明嵌套结构(或联合)并在另一个模块中定义它?它的语法是什么?

在 C++ 中,标题看起来像这样

struct S { 
    ... 
    struct Impl; 
    Impl * p; 
};

并且实现文件(cpp-file)会使用一些看起来很有趣的::-syntax,如下所示:

#include "header.h"
struct S::Impl { 
    ... 
};

如何在 D 中实现相同的功能?

【问题讨论】:

    标签: d pimpl-idiom


    【解决方案1】:

    D(至少是 DMD)使用 .di 文件进行声明。它们在某种程度上等同于 C .h 文件,但是它们是可选的。 D 编译器可以自动生成.di 文件(当指定-H 开关时),虽然我相信目前它所做的只是剥离函数体和单元测试。

    这是使用.di 文件实现 PImpl 的一种方法:

    • mod.di:

      struct S
      {
          struct I;
          I* pi;
      }
      
    • mod.d:

      struct S
      {
          struct I
          {
              int v;
          }
      
          I* pi;
      }
      

    请注意,目前您有责任确保S.d.di 文件中的字段相同 - 如果它们不同,则编译的模块对字段的了解会有所不同布局,这可能导致内存损坏。当前的编译器实现不验证 .d.di 文件中的定义是否匹配。

    【讨论】:

    • 链接器有责任验证内存布局假设,因为这是唯一知道所有这些假设的事情
    • 我认为这不对。数据结构不存储到目标文件中。链接器不知道类型。
    • 但编译器并不(需要)知道.d.di 文件
    • 编译.d文件时,编译器可以验证.d文件对应.di文件。我看不出有任何理由说明这在不切实际的情况下是无法实现的。目前它根本不这样做。
    【解决方案2】:

    我的问题是:如何在一个模块中声明嵌套结构(或联合)并在另一个模块中定义它?

    直截了当地说 - 在 D 中设计是故意不可能的。这是拥有可靠模块系统的直接结果——每个符号声明都隐含地由它在内部声明的模块名称限定。并且由于各种原因,您不能将符号劫持到另一个模块“命名空间”中。

    也就是说,没有必要在同一个模块中使用 pimpl 方法。您可以参考Cyber​​Shadow答案了解更多详情。

    【讨论】:

      【解决方案3】:

      另一种方法是基于D的类层次系统:

      所有对象都显式或隐式继承对象

      所以思路是用pimpl实现OuterClass,生成对应的 di-file,手动从 di-file 中删除 OuterClassPrivate 的所有定义 并更改 pimpl 成员的声明。

      例如:

      共享库的第一个版本

      module pimpl.mylib;
      
      class PimplTest
      {
          this()
          {
              mImpl = new PimplTestPrivate();
          }
      
          ~this()
          {
          }
      
          string sayWhat(string what)
          {
              return mImpl.ku ~ " " ~ what;
          }
      
          private class PimplTestPrivate
          {
              string ku = "Ku!!1";
          }
      
          private PimplTestPrivate mImpl;
      }
      

      测试应用程序:

      module main;
      
      import std.stdio;
      import pimpl.mylib;
      
      void main()
      {
          PimplTest t = new PimplTest();
          writeln(t.sayWhat("?"));
      }
      

      Shared mylib 可以通过以下方式构建(在 Linux 下):

      $ dmd -H -c mylib.d -fPIC
      $ dmd -ofmylib.so mylib.o -shared -defaultlib=libphobos2.so -L-rpath=/path/to/where/shared/phobos/library/is
      

      然后编辑生成的di文件:

      // D import file generated from 'mylib.d'
      module pimpl.mylib;
      class PimplTest
      {
          this();
          ~this();
          string sayWhat(string what);
      
          // NOTE this
          private Object mImpl;
      }
      

      编译测试本身

      $ dmd -c main.d /path/to/first/version/of/mylib.di
      $ ln -s /path/to/first/version/of/mylib.so .
      $ dmd main.o -L-l:mylib.so -defaultlib=libphobos2.so -L-rpath=/path/to/where/shared/phobos/library/is:.
      $ ./main
      Say: ?
      

      然后我们改变mylib:

      module pimpl.mylib;
      
      import std.conv;
      
      class PimplTest
      {
          this()
          {
              mImpl = new PimplTestPrivate();
          }
      
          ~this()
          {
          }
      
          string sayWhat(string what)
          {
              return mImpl.getMessage1(mImpl.getValue(), what);
          }
      
          private class PimplTestPrivate
          {
              int getValue()
              {
                  return 42;
              }
      
              string ku = "Ku!!1";
      
              string getMessage1(int x, string y)
              {
                  return "x = " ~ to!(string)(x) ~ ", " ~ y;
              }
      
              double pi = 22.0/7.0;
          }
      
          private PimplTestPrivate mImpl;
      }
      

      编译它并将第一个版本的 mylib 的二进制共享对象(so 文件)替换为刚刚构建的一个。运行测试应用程序不能崩溃,但输出会有所不同。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-11-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-01-21
        • 2014-11-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多