英語版
このページの英語版を見る

インポートC

内容
  1. クイックサンプル
  2. ImportCの方言
  3. ImportCを起動する
    1. コマンドラインでCファイルをインポートする
    2. DコードからCファイルをインポートする
  4. プリプロセッサ
    1. プリプロセッサを自動的に実行する
    2. プリプロセッサを手動で実行する
    3. プリプロセッサのマクロ
  5. 定義済みマクロ
  6. プリプロセッサ指令
    1. 行の制御
    2. ラインマーカー
    3. プラグマ
  7. src/__builtins.di
  8. 実装
    1. 列挙型
    2. ビット・フィールド
    3. 暗黙の関数宣言
    4. #プラグマ STDC FENV_ACCESS
  9. 制限事項
    1. 例外処理
    2. 定数
    3. 揮発性
    4. 制限
    5. アトミック
    6. 互換型
  10. インピーダンスの不一致
    1. キーワードの不一致
    2. 同じだが異なる型
    3. 一般的な
  11. 拡張
    1. asmステートメント
    2. 前方参照
    3. C++スタイルのタグ記号
    4. コンパイル時の関数実行
    5. 関数のインライン化
    6. 列挙型の基本型
    7. レジスタ格納クラス
    8. typeof 演算子
    9. インポート宣言
    10. 制御Zはファイルの終わりである
    11. 符号付き整数リテラル longより大きい long
    12. ドット演算子とArror演算子
  12. GnuとClangの拡張機能
    1. __attribute__ 拡張機能
    2. __attribute__((noreturn))
  13. ビジュアルCの拡張
    1. __stdcall 関数呼び出し規約
    2. __declspec 属性拡張
    3. __pragma 属性拡張
  14. Digital MarsC拡張
    1. __stdcall 関数呼び出し規約
    2. __declspec 属性拡張
  15. Dから見たImportC
    1. モジュール名
    2. extern (C)
    3. 列挙型
    4. タグ記号
  16. Cコードをラップする
  17. CコードをDコードに変換する
  18. 警告
  19. __builtins.di
  20. ImportC++を使用する
  21. その他のソリューション
    1. dpp by Atila Neves
    2. DStep by Jacob Carlborg
    3. htod by Walter Bright
  22. ImportCの仕組み

ImportCは、D実装に組み込まれたCコンパイラである。 Cファイルを直接インポートすることができる。 Cファイルの宣言に対応するDファイルを手作業で準備する必要がない。 に対応するDファイルを手動で準備する必要がない。Cファイルをモジュールに直接コンパイルする。 モジュールに直接コンパイルする。次のような使い方ができる。 100%Cプログラムをコンパイル・リンクするCコンパイラとして使用できる。

注釈:ImportCとBetterCは大きく異なる。 ImportCは実際のCコンパイラである。BetterCはDのサブセットで、C標準ライブラリの存在にのみ依存している。 C標準ライブラリの存在にのみ依存する。BetterCのコードはImportCのコードともリンクできる。

簡単な例

ファイルhello.c のCコード:

    #include <stdio.h>
    int main()
    {
        printf("hello world\n");
        return 0;
    }

コンパイルして実行する:

    dmd hello.c
    ./hello
    hello world

ファイルfunctions.c のC関数:

    int square(int i)
    {
        return i * i;
    }
    

ファイルdemo.d のDプログラム:

import std.stdio;
import functions;
void main()
{
    int i = 7;
    writefln("The square of %s is %s", i, square(i));
}

コンパイルして実行する:

    dmd demo.d functions.c
    ./demo
    The square of 7 is 49
    

ImportCダイアレクト

ImportCはISO/IEC 9899:2011の実装である。 ISO/IEC 9899:2011の実装である、 C11 の実装である。 C11規格への参照は、C11の後に段落番号を付ける。 C99、C89、K+R Cなどの以前のバージョンはサポートされていない。

実装定義: ImportC方言の調整は、Cコンパイラの動作に合わせて行われる。 ImportC方言の調整は、Dコンパイラが対応するCコンパイラの動作に合わせるために行われる、 すなわちAssociated C Compilerである。

さらに、D 実装の機能の一部を利用するために、さらに調整が加えられている。

注釈:これはCのリファレンス・マニュアルでもプログラミングのチュートリアルでもない。 これは、C言語の方言である ImportC "が持つCの方言の詳細と、それを効果的に使う方法について述べている。

ImportCを起動する

ImportCコンパイラはコマンドラインから直接呼び出すことができる:

コマンドラインでのImportCファイル

ImportC ファイルは.i または.c のいずれかの拡張子を持つ。 拡張子を指定しない場合は、.i が最初に試され、次に.c が試される。

    dmd hello.c
    

hello.c をImportCでコンパイルし、リンクして実行可能ファイルを作成する。 hellohello.exe ファイルを作成する。

ベストプラクティス: コマンドラインでCファイルを指定する場合は、明示的に または 拡張子を使用する。 コマンドラインでCファイルを指定する場合は、明示的に または の拡張子を使用する。.i .c

DコードからCファイルをインポートする

DImportDeclarationを使用する:

import hello;

これは、hello がDファイルではなく、.i または.c という拡張子を持っている場合に実行される、 hello 、ImportCでコンパイルする。

プリプロセッサ

ImportCはプリプロセッサを持たない。これは、C ファイルをコンパイルするように設計されている。 ImportCは自動的に関連するCプリプロセッサを実行することができる。 プリプロセッサを手動で実行することもできる。

プリプロセッサを自動的に実行する

Cファイルの拡張子が.c の場合、ImportCは自動的にプリプロセッサを実行する。 を自動的に実行する。

  1. Windows用に -m32omfスイッチを使用する、 sppn.exe がプリプロセッサとして使用される。
  2. Windows用に -m32mscoff または -m64スイッチを使ってWindows用にコンパイルする場合、cl.exe /P /Zc:preprocessor がプリプロセッサとして使われる。 がプリプロセッサとして使用される。
  3. OSX用にコンパイルする場合は、clang -E プリプロセッサが使用される。
  4. それ以外の場合は、cpp プリプロセッサが使用される。

この -vスイッチは、プリプロセッサを起動するコマンド プリプロセッサを起動する

この -Ppreprocessorflagスイッチは preprocessorflag をプリプロセッサに渡す。

importc.h

druntimeファイル src/importc.h #include importc.h は、ImportCではサポートされていないさまざまなCコンパイラ拡張を考慮したソースコードの調整を提供する。 ImportCではサポートされていないさまざまなCコンパイラー拡張を考慮したソースコードの調整を提供する。

no-builtin-macro-redefined

Posixシステムでは、ImportCはスイッチ-Wno-builtin-macro-redefined を、 および で使用されるCプリプロセッサに渡す。 gcc clang に渡す。 このスイッチ このスイッチは、2008年以前に作られたgcc プリプロセッサには存在しない。 回避策は、プリプロセッサを手動で実行することである。

プリプロセッサを手動で実行する

C ファイルの拡張子が.i の場合、そのファイル はすでにプリプロセスされていると推定される。 プリプロセッサーは手動で実行できる:

Digital MarsCプリプロセッサ sppn.exe

sppn.exeとしてWin32上で実行される:

    sppn file.c
    

前処理された出力は、file.i に書き出される。

Gnu Cプリプロセッサ

Gnu Cプリプロセッサは次のように起動する:

    gcc -E file.c > file.i
    

Clang Cプリプロセッサ

Clangプリプロセッサは、次のように呼び出すことができる:

    clang -E file.c -o file.i
    

Microsoft VCプリプロセッサ

VCプリプロセッサーは次のように呼び出せる:

    cl /P /Zc:preprocessor file.c -Fifile.i
    

そして、プリプロセスされた出力はfile.i に書き出される。

dmpp Cプリプロセッサ

dmppCプリプロセッサは次のように呼び出すことができる:

    dmpp file.c
    

前処理された出力はfile.i に書き出される。

プリプロセッサ・マクロ

ImportCは、プリプロセッサが自動的に実行されたときに、実行されたプリプロセッサから#define マクロをすべて収集する。 宣言として解釈することで、Dコードで利用できるようになるものもある。 D宣言として解釈できるマクロの種類は増えるかもしれない、 しかし、Cのマクロのメタプログラミング的な使い方をすべて網羅することはないだろう。

マニフェスト定数

のような明示定数のように見えるマクロは、D明示定数宣言として解釈される:

#define COLOR 0x123456
#define HELLO "hello"

などのように見えるマクロは、この形式のD明示定数宣言として解釈される:

enum COLOR = 0x123456;
enum HELLO = "hello";

関数に似たマクロ

多くのマクロは関数のように見え、テンプレート化された関数として扱うことができる:

#define ABC a + b
#define DEF(a) (a + x)
auto ABC() { return a + b; }
auto DEF(T)(T a) { return a + x; }

しかし、マクロの定式化によっては同じ結果が得られないものもある:

#define ADD(a, b) a + b
int x = ADD(1, 2) * 4; // xを9に設定する
auto ADD(U, V)(U a, V b) { return a + b; }
int x = ADD(1, 2) * 4; // xを12に設定する
ベストプラクティス: 引数や式全体を括弧で囲む:
#define ADD(a, b) ((a) + (b))

もう1つの問題は、引数の副作用である:

#define DOUBLE(x) ((x) + (x))
int i = 0;
DOUBLE(i++);
assert(i == 2);  // Dの結果は1、Cの結果は2となる

また、引数を参照として扱うこともある:

#define INC(x) (++x)
int i = 0;
INC(i);
assert(i == 1); // Dの結果は0、Cの結果は1となる

定義済みマクロ

ImportCはマクロを定義していない。

ImportCコンパイルと他のCコンパイラを区別するには、次のように使う:

#if __IMPORTC__

__IMPORTC__ で定義されている。 src/importc.h で定義されている。 プリプロセッサが実行されたときに自動的にインクルードされる。importc.h 。 の定義があり、様々なCソースコードの曖昧さをImportCに適応させるために使用される。

プリプロセッサ指令

ImportCはこれらのプリプロセッサ指令をサポートしている:

行制御

C11 6.10.4

ラインマーカー

ラインマーカー ディレクティブは通常Cプリプロセッサの出力に埋め込まれる。

プラグマ

以下のプラグマがサポートされている:

src/__builtins.di

プリプロセスが完了したときにコンパイラが最初に行うことは、インポートすることである。 src/__builtins.di. これは、他のCコンパイラが提供するさまざまなビルトインのサポートを提供する。 __builtins.di はDファイルである。

実装

ImportCの処理系定義:" は以下の通りである:

列挙型

列挙定数は常にint として型付けされる。

挙定数の値を定義する式は整数型でなければならない。 は積分型で、int に収まる整数値に評価されなければならない。

enum E { -10, 0x81231234 }; // OK
enum F {  0x812312345678 }; // エラー、int型に収まらない
enum G { 1.0 };             // エラー、整数型ではない

列挙型はint である。

ビットフィールド

C11のビット・フィールドには多くの処理系定義がある。 ImportC "の動作は、ターゲッ ト・プラットフォームの関連するCコンパイラの動作に合わせて調整される。 ImportCの動作は、ターゲット・プラットフォーム上の関連するCコンパイラの動作に合わせて調整される。

暗黙の関数宣言

暗黙の関数宣言:

    int main()
    {
        func();  // func()の暗黙の宣言
    }
    

K+R CとC89では認められていたが、C99とC11では無効になった。多くの 多くのCコンパイラはまだ暗黙的関数宣言をサポートしているが、ImportCはサポートしていない。

根拠: 暗黙の関数宣言は非常にエラーを起こしやすく、バグを見つけにくい。 バグを見つけるのが難しい。

#プラグマ STDC FENV_ACCESS

これはC11 7.6.1に記述されている。

#pragma STDC FENV_ACCESS on-off-switch
on-off-switch: ON OFF DEFAULT

完全に無視される。

制限事項

例外処理

ImportCは例外をスローしないと仮定されている。setjmplongjmp はサポートされていない。

定数

C11では、const は局所的にしか適用されないと規定されている。ImportCのconst は推移的に適用される、 つまり、

int *const p;
はC11ではpがintへのconstポインタであることを意味するが、ImportCではpがintへのconstポインタであることを意味する、 ImportCでは、pがintへのconstポインタであることを意味する。

揮発性

volatile 型修飾子(C11 6.7.3)は無視される。volatileAtomicはそのためのものである。 volatile をデバイス・レジスタとして使用するには、別途コンパイルされた関数を呼び出す、 またはインラインアセンブラを使用する。

制限

restrict 型修飾子(C11 6.7.3)は無視される。

原子

_Atomic 型修飾子(C11 6.7.3)は無視される。 アトミック演算を行うには、外部でコンパイルされた関数を使用するか、インライン・アセンブラを使用する。

互換型

互換型(C11 6.7.2)は、ImportCでは同一の型である。

インピーダンスの不一致

C言語とD言語が"ただ動く"ようにするためにあらゆる努力が払われているが、両言語には時折現れる根本的な違いがある。 根本的な違いが時々現れる。

キーワードの不一致

DとCはほとんど同じキーワードを使うが、CにはDにないキーワードがあり、その逆もある。これは これはC言語のコードのコンパイルには影響しないが、C言語の変数や型にD言語からアクセスする際に困難が生じることがある。 例えば、Dのversion キーワードがCの構造体メンバとして使われることは珍しくない:

ファイルdefs.c のCコード:

struct S { int version; };

Dからアクセスする:

import defs;
int tech(S* s) {
    return s.version; // 失敗する。なぜなら、versionはDキーワードだからだ
}

回避策がある:

import defs;
int tech(S* s) {
    return __traits(getMember, *s, "version");
}

同じだが型が違う

プラットフォームによっては、Cのlongunsigned long は、それぞれintunsigned int と同じサイズである。 他のプラットフォームでは、Clongunsigned longlong longunsigned long long と同じサイズである。 long double また、long double _Complex は、double およびdouble _Complex と同じサイズになる。 ImportCでは、同じサイズと符号を持つこれらの型は同じ型として扱われる。

一般的な

Generic selection 式(C11 6.5.1.1)はImportCとは異なる。 Same only Different Typesの型は、型において区別できない。 の型は、generic-associationの 型名部分で区別できない。 C11 6.5.1.1-2に従って型が重複している場合にエラーを出す代わりに、ImportC はgeneric-assoc-listの最初の互換性のある型名を選択する。

拡張

asm文

D言語では、asm は標準キーワードであり、その構成はImportCと共有されている。 ImportCと共有されている。C言語では、asm は拡張である(J.5.10)、 であり、代わりに__asm__ を使うことが推奨されている。すべての代替 asm の代替キーワードはすべてdruntimeファイルによって翻訳される。src/importc.h によって翻訳される。

asm キーワードはアセンブラ命令を埋め込むために使用できる。 構文は処理系定義による。Digital MarsDコンパイラは のドキュメントで説明されているインライン・アセンブラの方言のみをサポートしている。 D x86 インライン・アセンブラ" のドキュメントで説明されているインライン・アセンブラの方 法のみをサポートしている。

asm 関数や変数宣言の中で、シンボルのマングル名を指定するのに使うことができる。 を指定することができる。この使い方は に似ている。

char **myenviron asm("environ") = 0;
int myprintf(char *, ...) asm("printf");

asm 、レジスタと変数の関連付けは無視される。

前方参照

スコープ内のすべての宣言にアクセスできる。 スコープ内のすべての宣言にアクセスできる。

Ta *p;  // Taは前方参照されている
struct Sa { int x; };
typedef struct Sa Ta; // Taは定義されている
struct S s;
int* p = &s.t.x;  // A構造体の定義は前方参照されている
struct S { int a; struct T t; }; // Tはまだ前方参照されている
struct T { int b; int x; }; // 構造体Tの定義

C++ スタイルのタグ記号

C++では、structunionenum のタグ記号は、 を先頭につけなくてもアクセスできる。 タグ記号は、structunionenum キーワードを先頭につけなくてもアクセスできる。 ただし、同じスコープに同じ名前の宣言がない場合に限る。 ImportCも同じように動作する。

例えば、以下のコードはC++でもImportCでも受け入れられる:

struct s { int a; };
void g(int s) { struct s* p = (struct s*)malloc(sizeof(struct s)); p->a = s; }

一方、これは同じ理由でC++とImportCの両方で拒否される。

struct s { int a; };
void g(int s) { s* p = (s*)malloc(sizeof(s)); p->a = s; }

コンパイル時の関数実行

定数式の評価には、D言語のCTFと同じように関数を実行することも含まれる。 定数式の評価には、DのCTFEと同じように関数を実行することも含まれる。 定数式はCTFEを呼び出す。

例: CTFEを呼び出す:

_Static_assert("\x1"[0] == 1, "failed");
int mint1() { return -1; } _Static_assert(mint1() == -1, "failed");
const int a = 7; int b = a; // bを7に設定する

関数のインライン化

関数本体が存在する関数は、ImportCによってインライン化することができる。 関数本体が存在する関数は、それを呼び出すDコードだけでなく、ImportCによってもインライン化できる。

列挙型の基本型

列挙型はオプションのEnumBaseType:

EnumDeclaration:
    enum Identifier : EnumBaseType EnumBody
EnumBaseType: Type

で拡張され、これが指定されると、列挙型のメンバは暗黙的に 暗黙的にキャストされる。

enum S : byte { A };
_Static_assert(sizeof(A) == 1, "A should be size 1");

ストレージクラスの登録

register 記憶クラスを持つオブジェクトは、auto 宣言として扱われる。

register storage class を持つオブジェクトは、アドレスを取られる可能性がある。C11 6.3.2.1-2

配列はregister 記憶クラスを持つことができ、コンパイラによって登録されることがある。C11 6.3.2.1-3

typeof 演算子

typeof 演算子は型指定子として使用できる:

type-specifier:
    typeof-specifier
typeof-specifier: typeof ( expression ) typeof ( type-name )

インポート宣言

モジュールはCImportDeclaration でインポートできる:

CImportDeclaration:
    __import ImportList ;

Importによって、ImportCのコードはD宣言や関数に直接アクセスできるようになる。 これらの宣言を表す.h.h 、D 宣言で ファイルを最新に保つことの面倒さともろさは解消される。 D関数はインライン化できる。

Importはまた、ImportCコードが他の"Import C"ファイルを直接インポートすることを可能にする。 .hファイルを作成する必要もない。 インポートされたC関数はインライン化できるようになる。

ImportListはDと同じように機能する。

CImportDeclarationsの順序に意味はない。

ImportCファイルはインポートすることができ、インポートされるCファイルの名前はモジュール名から派生する。 インポートされるCファイルの名前はモジュール名から派生する。

ImportCファイル内のすべてのグローバルシンボルは、インポートするモジュールで利用可能になる。 インポートするモジュールが使用できるようになる。

インポート・ファイルで参照される名前が見つからない場合、インポートされる各ファイルのグローバル・シンボルが利用可能になる、 インポートされた各ファイルのグローバルシンボルが検索される。 その名前がちょうど1つのモジュールで見つかれば、それがその名前の解決策となる。 の解決となる。複数のモジュールで見つかった場合はエラーとなる。

注釈:ImportCにはスコープ解決演算子がないため、グローバルシンボルしか見つけることができない。 のみを見つけることができる。 を指定する修飾子は付けられない。

インポートされたモジュールのプリプロセッサー・シンボルは、インポートしたモジュールでは利用できない。 また、インポート・ファイル内のプリプロセッシング・シンボルはインポートされたモジュールでは使用できない。 インポートされたモジュールでは利用できない。

Dモジュールは、ImportDeclarationと同じ方法でインポートすることができる。 インポートすることができる。

インポートは循環させることができる。

__import core.stdc.stdarg; // va_listのD宣言を取得する
__import mycode;           // mycode.cをインポートする
int foo() { va_list x; // from core.stdc.stdargからva_listを取得する return 1 + A; // 4を返す }

mycode.c のように見える:

enum E { A = 3; }
ベストプラクティス: #define A 3 のようなプリプロセッサ の使用は避けること。 上の例で示した列挙型を使う。 sよりも 宣言を優先する。 関数スタイルのプリプロセッサ・マクロをインライン関数として書き直す。 #define #define const

コントロールZはファイルの終わりである

ソース・テキスト中の control-Z 文字\x1A は、End Of File を意味する。

long longより大きい符号付き整数定数

long long 型より大きい、接尾辞のない符号付き整数定数、 よりも大きいが、unsigned long long 型に収まる接尾辞のない符号付き整数定数は、unsigned long long として受け入れられ、型付けされる。 これはDやいくつかのCコンパイラの動作と一致する。

ドット演算子とアラー演算子

. 演算子は、構造体や共用体の値のメンバを指定するのに使われる。 -> 演算子は、ポインタが指す構造体または共用体値のメンバを指定するために使用される。 のメンバを指定する。この拡張は、.-> を値やポインタに対して互換的に使用できるということである。 値とポインタに対して互換的に使用できるということである。これは. に対する D の動作と一致する。

GnuとClangの拡張

gcc と は、拡張に対して同じ動作をすると仮定されている、 そのため、ここで使われている は両方を指している。clang gcc

__attribute__ 拡張機能

以下の__attribute__ :

  1. __attribute__((aligned(N)))
  2. __attribute__((always_inline))
  3. __attribute__((deprecated))
  4. __attribute__((dllexport))
  5. __attribute__((dllimport))
  6. __attribute__((naked))
  7. __attribute__((noinline))
  8. __attribute__((noreturn))
  9. __attribute__((nothrow))
  10. __attribute__((pure))
  11. __attribute__((vector_size(N)))
  12. その他は無視される

__attribute__((noreturn))

__attribute__((noreturn)) 関数の属性として設定する。 これは関数の属性として設定する。 関数の型には含まれない。Dでは、決して戻らない関数 は戻り値の型がgcc noreturnである。この違いは コードで見ることができる:

    attribute((noreturn)) int foo();
    size_t x = sizeof(foo());
    

このコードは、gcc では受け入れられるが、Dでは意味をなさない、 ImportCでは動作するが、Dコードとしては表現できない、 つまり、Cの 関数とインターフェイスするための.diファイルを作成する際には、判断力を働かせなければならない。 つまり、.diファイルを作成する際には、Cのnoreturn 関数とのインターフェイスを判断しなければならない。

さらに、Dコンパイラーはnoreturn 関数を利用する。 を利用している。このような到達不可能な コードは有効なC11であり、ImportCコンパイラはそれを受け入れる。

ベストプラクティス: noreturn 。 少なくとも戻り値の型を に設定しなければならない。void

ビジュアルC拡張

すべてのDigital Mars C拡張。

__stdcall 関数呼び出し規約

__stdcall 関数の呼び出し規約をWindows APIの呼び出し規約に設定する。

int __stdcall foo(int x);

__declspec 属性拡張

以下の__declspec 拡張:

  1. __declspec()
  2. __declspec(align(N))
  3. __declspec(deprecated)
  4. __declspec(dllexport)
  5. __declspec(dllimport)
  6. __declspec(naked)
  7. __declspec(noinline)
  8. __declspec(noreturn)
  9. __declspec(nothrow)
  10. __declspec(thread)
  11. その他は無視される

__pragma 属性拡張

以下の __pragma 拡張子は無視される:

  1. __pragma(pack(N))
  2. その他は無視される

Digital MarsCの拡張

__stdcall 関数呼び出し規約

__declspec 属性の拡張

以下の__declspec :

  1. __declspec(dllexport)
  2. __declspec(dllimport)
  3. __declspec(naked)
  4. __declspec(thread)

Dから見たImportC

CのコンストラクトとDのコンストラクトを一対一に対応させることはできない。 非常に近い。以下は、インポートされるC宣言をD側がどのように見るかについての説明である。 以下は、インポートされるC宣言をD側がどのように見ているかの説明である。

モジュール名

ImportCファイルに割り当てられるモジュール名は、ファイル名からパスと拡張子を取り除いたものである。 からパスと拡張子を取り除いたものである。これは これは、モジュール宣言を持たないDモジュールに割り当てられるデフォルトのモジュール名と同じである。

extern (C)

すべてのCシンボルはextern (C)

列挙型

Cの列挙型:

enum E { A, B = 2 };

としてDコードに表示される:

enum E : int { A, B = 2 }
alias A = E.A;
alias B = E.B;

.min.max プロパティが利用できる:

static assert(E.min == 0 && E.max == 2);

タグ記号

タグ・シンボルは、structunion 、およびenum キーワードの後に表示される識別子である(C11 6.7.2.3)。C言語では、タグ・シンボルは他の識別子とは別のシンボル・テーブルに配置される。 Cでは、他の識別子とは別のシンボル・テーブルに置かれる。つまり、2つの異なるシンボルが同じ名前を使うことができる:

    int S;
    struct S { int a, b; };
    S = 3;
    struct S *ps;
    

Dではこの区別はない。Dではこのような区別はしない。 タグ・シンボルが識別子の唯一の宣言である場合、Dコンパイラーはそれを認識する。識別子を共有するタグ・シンボルと非タグ・シンボル が識別子を共有する場合、Dコンパイラーはタグ以外のシンボルを認識する。これは通常 のように、typedef を適用する一般的なC言語の慣行があるため、通常は問題にはならない:

    typedef struct S { int a, b; } S;
    

DコンパイラはS に適用されたtypedef を認識し、コードは期待通りにコンパイルされる。しかし、typedef がない場合は、次のようになる:

    int S;
    struct S { int a, b; };
    

最も実用的な回避策は、Cコードにtypedef を追加することである:

    int S;
    struct S { int a, b; };
    typedef struct S S_t;    // このtypedefを追加する
    

そうすればDコンパイラーは、S_t を介して構造体タグ・シンボルにアクセスできる。

Cコードのラッピング

CコードをImportCに適応させる際の多くの困難は、Cコード自体を編集することなく解決できる。 Cコード自体を編集することなく行うことができる。Cコードを別のCファイルでラップし、それを

#include
にラップする。次のような問題のあるCファイルfile.c を考えてみよう:

    void func(int *__restrict p);
    int S;
    struct S { int a, b; };
    

問題は、

__restrict
がImportC(またはC11)で認識される型修飾子ではないことである。 (またはC11)によって認識されない、 また、S 構造体は、
int S;
という宣言によってDから隠されている。 file.c を修正でラップするには、その内容でファイルfile_ic.c を作成する:

    #define __restrict restrict
    #include "file.c"
    typedef struct S S_t;
    

そして、import file; の代わりにimport file_ic; を使用し、

struct S
が必要な場合はS_t を使用する。

CコードをDコードに変換する

Cコードをインポートするだけでなく、実際にCソースからDソースに変換したい場合もある。 Dソースに変換する。その理由は以下の通りである:

これはDコンパイラーで-Hf :

dmd -c mycode.c -Hf=mycode.di

これは、mycode.c のCソース・コードをmycode.di のDソース・コードに変換する。 -inline スイッチも使用すると、関数のプロトタイプだけでなく、Cの関数本体も出力される。 関数のプロトタイプだけでなく、Cの関数本体も出力される。

インピーダンスの不一致

CのセマンティクスをDソース・コードに正確にマッピングすることは、そのすべての奇妙な点を考慮すると、必ずしも現実的ではない。 必ずしも実用的ではない。ImportC "は、意味解析にCのセマンティクスを使うことで、Cのセマンティクスにかなり近づけることができる。 Dソースコードで表現できるよりもはるかに正確なCセマンティクスに近づける。したがって Dソース・コードへの翻訳は完璧ではない。例:":

    int S;
    struct S { int a, b; };
    int foo(struct S s)
    {
        return S + s.a;
    }
    

はImportCでは問題なく動作するが、これはint Sstruct S が異なるシンボルテーブルにあるからである。 シンボルテーブルにあるからだ。しかし、生成されたDコードでは、両方のシンボルが同じシンボル・テーブルにあり、衝突してしまう。 このようなCから翻訳されたDソースコードは、ユーザーが調整する必要がある。

とはいえ、現場からの報告によれば、この変換機能は、C言語からD言語に変換する必要があるユーザーにとって、非常に大きな時間節約になるという。 現場からの報告では、この変換機能は既存のCコードを扱う必要のあるユーザーにとって非常に大きな時間節約になるという。

警告

不審なC言語の構文の多くは、一般的なコンパイラではデフォルトで警告を発する。 典型的なコンパイラーは、次のような警告を出す:

int *p = 3; // 警告: 整数型が暗黙的にポインタ型に変換された

ImportCは警告を発しない。これは、ユーザーが他のCコンパイラで開発された既存のCコードをインポートすることを想定しているためである。 コードをインポートするものであり、それは意図したとおりに書かれている。 C11が合法だと言えば、ImportCはそれを受け入れる。

__builtins.di

ImportCはDを使っていくつかの機能を実装している。これらは __builtins.di ファイルに実装されており、ImportCのコンパイルごとに自動的にインポートされる。

ImportC++

ImportCはC++コードをコンパイルしない。その場合は、dpp

その他のソリューション

dpp by Atila Neves

dppコード

dpp 記事

記事から

dppは、拡張子.dppのDソースファイルを解析し、#includeディレクティブの代わりに#includeディレクティブを展開するコンパイラ・ラッパーである。 拡張子が.dppのDソース・ファイルを解析し、#includeディレクティブに遭遇した場合はそれを展開する。 ディレクティブを展開し、CまたはC++のシンボルをすべてDに変換する。 その結果をDコンパイラ(デフォルトではDMD)に渡す。

DStepと同様、dppもlibclangに依存している。

Jacob CarlborgによるDStep

DStepコード

DStepの記事

記事より

DStepは、CやObjective-CライブラリのD言語バインディングを自動生成するツールだ。 バインディングを自動生成するツールである。これは CまたはObjective-Cのヘッダーファイルを処理し、Dモジュールを出力する。 DStepはClangコンパイラーをライブラリー(libclang)として使い、ヘッダーファイルを処理する。

htod by Walter Bright

htodは、Cの.h 。 Dコードへのインポートに適したDソースファイルに変換する。 htod htodは、Digital MarsのCおよびC++コンパイラのフロントエンドからビルドされている。 出力がオブジェクトではなくDモジュールのソースコードであることを除けば、CまたはC++コンパイラと同じように動作する。 オブジェクト・コードではなく、Dモジュールのソース・コードを出力する。

ImportCの仕組み

ImportCの実装は、D言語のセマンティクスがC言語と非常に似ているという考えに基づいている。 という考えに基づいている。ImportCは独自のパーサーを持ち、Cの構文をCと同じAST(抽象構文木)に変換する。 (抽象構文木)に変換する。ImportCのレキサーはDと同じだが、以下のような変更が加えられている。 ImportCのレキサーはDと同じだが、キーワードや整数リテラルが異なるなど、あちこちに変更が加えられている。 CのセマンティクスがDと異なる場合は、Dコンパイラのセマンティクス解析コードで調整が行われる。 コードに調整が加えられる。

このようにDのセマンティック実装を利用することで、ImportCは、前方参照やCTFの処理など、以下のことができるようになる。 前方参照の処理、CTFE(コンパイル時関数実行)、C関数のDコードへのインライン化などである。 をDコードにインライン化することができる。前方参照を扱えるということは、.hファイルを書く必要さえないことを意味する。 CTFEを実行できることは、ImportCがDにインポートされていることをテストするのに非常に便利である。 CTFEを実行できることは、実行ファイルを生成することなくImportCが動作していることをテストするのに非常に便利である。 しかし、一般的には、D言語の機能をImportCに追加したいという強い誘惑に抵抗してきた。

オプティマイザーとコードジェネレーターは、もちろんDが使用しているものと同じである。

"