D编程语言中的Pimpl-idiom

时间:2013-12-09 11:42:41

标签: d pimpl-idiom

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

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

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

并且实现文件(cpp-file)将使用一些有趣的::语法,如下所示:

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

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

3 个答案:

答案 0 :(得分:5)

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文件中的定义是否匹配。

答案 1 :(得分:1)

  

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

为了得到它直接 - 在设计中故意不可能在D中。这是拥有一个可靠的模块系统的直接结果 - 每个符号声明都由它在里面声明的模块名称隐式限定。由于各种原因,您无法将符号劫持到另一个模块“命名空间”。

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

答案 2 :(得分:1)

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

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

所以想法是用pimpl实现OuterClass,生成相应的 di-file,从di-file手动删除OuterClassPrivate的所有定义 并更改pimpl-member的声明。

例如:

共享库的第一个版本

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("?"));
}

共享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

然后编辑生成的文件:

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

    // NOTE this
    private Object mImpl;
}

编译测试itsel

$ 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替换第一版mylib的二进制共享对象(so文件)。运行测试应用程序不得崩溃,但输出将不同。