D编程语言中的Pimpl习惯用法 D有一个奇妙的模块系统,它比C++更能大大减少编译时间。根据文档,D仍然提供不透明的结构和联合,以便启用pimpl习惯用法。我的问题是:如何在一个模块中声明嵌套结构(或联合)并在另一个模块中定义它?它的语法是什么 C++中的标题看起来像这个< /P> struct S { ... struct Impl; Impl * p; };

D编程语言中的Pimpl习惯用法 D有一个奇妙的模块系统,它比C++更能大大减少编译时间。根据文档,D仍然提供不透明的结构和联合,以便启用pimpl习惯用法。我的问题是:如何在一个模块中声明嵌套结构(或联合)并在另一个模块中定义它?它的语法是什么 C++中的标题看起来像这个< /P> struct S { ... struct Impl; Impl * p; };,d,pimpl-idiom,D,Pimpl Idiom,实现文件(cpp文件)将使用一些有趣的语法,如: #include "header.h" struct S::Impl { ... }; 如何在D中实现相同的功能?D(至少是DMD)使用.di文件进行声明。它们在某种程度上等同于C.h文件,但它们是可选的。D编译器可以自动生成.di文件(当指定-H开关时),尽管我相信目前它所做的只是剥离函数体和单元测试 下面是使用.di文件实现PImpl的一种方法: mod.di: struct S { struct I; I*

实现文件(cpp文件)将使用一些有趣的语法,如:

#include "header.h"
struct S::Impl { 
    ... 
};
如何在D中实现相同的功能?

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中是故意不可能的。这是拥有一个可靠的模块系统的直接结果——每个符号声明都由其内部声明的模块名隐式限定。由于各种原因,您不能将一个符号劫持到另一个模块“名称空间”中


也就是说,使用pimpl方法不需要在同一个模块中执行。您可以参考CyberShadow的答案了解更多详细信息。

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

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

因此,我们的想法是使用pimpl实现OuterClass,生成相应的 在di文件中,手动从di文件中删除OuterClassPrivate的所有定义 以及pimpl成员的变更声明

例如:

共享库的第一个版本

测试应用:

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

然后编辑生成的di文件:

编译测试itsel

然后我们改变mylib:


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

链接器有责任验证内存布局假设,因为这是唯一知道所有假设的东西,我认为这是不对的。数据结构不会存储到对象文件中。链接器不知道类型。但编译器不(需要)知道
.d
.di
文件编译
.d
文件时,编译器可以验证
.d
文件是否对应于
.di
文件。我看不出有什么理由在不切实际的情况下无法实现。目前它根本没有做到这一点。
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("?"));
}
$ 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;
}
$ 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: ?
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;
}