2013-12-09 89 views
4

D有一個夢幻般的模塊系統,與C++相比,它大大縮短了編譯時間。根據文獻,D仍然提供了不透明的結構和聯合體,以便實現這個pimpl習語。我的問題是:我怎樣才能在一個模塊中聲明一個嵌套結構(或聯合),並在另一個模塊中定義它?這是什麼語法?D編程語言中的Pimpl-idiom

在C++中的頭看起來像這樣

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

和實現文件(CPP文件)會用一些有趣的前瞻性:: -syntax這樣的:

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

我如何在D中實現相同?

回答

5

D(DMD,至少)使用.di文件進行聲明。它們有點相當於C .h文件,但它們是可選的。 D編譯器可以自動生成.di文件(當指定-H開關時),但我相信目前它所做的只是剝離函數體和單元測試。

下面是實現PIMPL一種方式使用.di文件:

  • mod.di

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

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

請注意,目前您有責任確保S中的字段在.d.di文件中都相同 - 如果它們不同,編譯後的模塊將不同地瞭解字段的佈局方式,這可能導致內存損壞。當前的編譯器實現不會驗證.d.di文件中的定義是否匹配。

+0

這將是鏈接器的責任,以驗證內存佈局的假設,因爲這是唯一知道所有這些 –

+0

我認爲這是不正確的。數據結構不存儲到目標文件。鏈接器不知道類型。 –

+0

但編譯器不需要知道'.d'和'.di'文件 –

1

我的問題是:如何在一個模塊中聲明一個嵌套的結構(或聯合)並在另一個模塊中定義它?

爲了得到它 - 這是故意不可能在D設計。這是擁有可靠模塊系統的直接後果 - 每個符號聲明都由其聲明的模塊名稱隱含地限定。由於各種原因,您不能將符號劫持到另一個模塊「命名空間」中。

這就是說,沒有必要在同一模塊中使用pimpl的方法。有關更多詳細信息,請參閱Cyber​​Shadow答案。

1

另一種方法是基於D的系統類的層次上:

所有對象繼承或明或暗地在對象

這樣的想法是PIMPL實現在OuterClass,生成相應的 二檔,手動從二檔 刪除OuterClassPrivate的所有定義和改變平普爾成員的聲明。

例如:

共享庫的第一版本

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 

然後編輯genereated二檔:

// 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剛建成一個。運行測試應用程序不能崩潰,但輸出會有所不同。