2012-02-06 45 views
6

C言語で変数の型を自動的に発見する方法はありますか?プログラム内のメカニズムによって、コンパイラのパスを使用して変数を解析し、その型を割り当てたポイントまでのスクリプト?私はこれに関する一般的な提案を探しています。以下は、私が必要とする理由と理由についての背景です。Cコードで変数の型を取得する

OpenMP削減節のセマンティクスを変更したいと思います。この時点では、ソースコード内の節を(スクリプトを介して)関数の呼び出しで置き換えるのが最も簡単なようです。次に、私が望む削減セマンティクスを実装する関数を定義することができます。

以前、私は(例えば)

enum reduction_op {PLUS, MINUS, TIMES, AND, 
    OR, BIT_AND, BIT_OR, BIT_XOR, /* ... */}; 

そしてmy_reductionが署名

void my_reduction(enum reduction_op op, void * var, size_t size); 
を持っている必要があり、
my_reduction(PLUS, &x, sizeof(x)); 
#pragma omp parallel for 

:たとえば、私のスクリプトは、このにこの

#pragma omp parallel for reduction(+:x) 

を変換します

他の中でもmy_reductionは、プログラマが本来意図していた減算変数に加算演算を適用する必要があります。しかし、私の機能はこれを正しく行う方法を知ることができません。特に、操作の種類(​​)、元の変数(var)の位置、および変数の型のサイズを知っていますが、変数の型自体はわかりません。特に、varに整数型または浮動小数点型があるかどうかはわかりません。低レベルのPOVから、これらの2つのクラスの型に対する加算演算は全く異なります。

GCCがサポートしている非標準オペレータtypeofだけが、sizeofの働きをして、ある種の変数を返すなら、この問題を簡単に解決できます。しかし、typeofはsizeofのようなものではありません。l値の宣言でしか使用できません。

コンパイラは、実行可能コードの生成が完了する前に、明らかにxの型を知っています。これにより、私は何とかxの型を取得してスクリプトに渡してから、変更されたソースコードをコンパイルするために、GCCのパーサーを何とか利用できるかどうか疑問に思っています。その後、十分に逆参照し、オペレータを適用する前に適切にキャストでき

enum var_type { INT8, UINT8, INT16, UINT16, /* ,..., */ FLOAT, DOUBLE}; 
void my_reduction(enum reduction_op op, void * var, enum var_type vtype); 

そしてmy_reductionを宣言するのは簡単だろう。

ご覧のとおり、Cで「ディスパッチング」メカニズムを作成しようとしています。なぜ、C++のオーバーロードを使用しないのですか?私のプロジェクトでは、Cで書かれたレガシーソースコードで作業するように制約されているので、スクリプトでコードを自動的に変更できますが、別の言語に書き換えることはできません。

ありがとうございます!

+2

いくつかのツール/スクリプトでソースコードを後処理するのはどうですか?例えば。 clangで解析し、型を見つけ、型固有のコードを挿入/調整し、コンパイルしますか? –

+0

ありがとう、アレックス。それは正しい方向に進んでいるように思えます。 –

+1

私は、ユーザー定義リダクションが3.1または4.0標準の一部であると、どこか読んでいます。 Hm 3.1は次のように言っています: 'reduction({operator | intrinsic_procedure_name}:list)' ..n試みたのはintrinsic_procedure_nameです。あなたのタイプ検出を部分的に解決するに過ぎません。 – Bort

答えて

2

GCCは、typeofの拡張子を提供します。これは標準ではありませんが、一般的です(いくつかの他のコンパイラ、例えばclang/llvmなど)。

あなたの目的に合わせてMELT(GCCを拡張するためのドメイン固有の言語)を拡張することで、GCCをカスタマイズすることもできます。

+1

ありがとう、Basile。あなたが私の投稿から見ることができるように、私は型を認識していますが、型は私に必要なものを与えません。何も返しません。その情報またはその情報を関数に渡すことはできません。これを行うことができます: #define add(x、y、z){\ typeof(x)_x = x; \ typeof(y)_y = y; \ z = _x + _y; \ } オペレータのタイプは、私には非常に限られた有用性しか持たないようです。 –

+1

これは標準ではなく、ほとんどのCコンパイラでは動作しません。私の経験上、プロダクションコードにGCC拡張を使用することは非常に悪い考えです。 – Lundin

2

また、GCCのプラグインをカスタマイズしたり、必要に応じてMELTの拡張機能をカスタマイズすることもできます。しかし、これは複雑なGCCの内部表現(Gimple、Tree)の理解を必要とします。

しかし、タイプはCのコンパイル専用のものです。これらは、reifiedされていません。

+1

これは多かれ少なかれ私がやったことです - プラグインではなく、GCC自体の中でいくつかのコードがハッキングしています。コンパイル時にGimple tree_nodesから型情報を取得する方法を知りました。それはかなりの掘り出し物を取った。ありがとう! –

4

マクロのフラッドを書き込まない限り、Cは実際には事前コンパイル時にこれを実行する方法がありません。これは非常に悪い習慣ですし、下手に書かれたレガシーコードを維持する場合も、最後の手段としてのみ使用されるべきであること

void int_reduction (enum reduction_op op, void * var, size_t size); 

#define reduction(type,op,var,size) type##_reduction(op, var, size) 

... 
reduction(int, PLUS, &x, sizeof(x)); // function call 

注:私はないは、それは基本的に次のように行くだろう、マクロ・アプローチの洪水をお勧めします次に。このアプローチでは、タイプセーフなどの保証はありません。

void reduction (enum type, enum reduction_op op, void * var, size_t size) 
{ 
    switch(type) 
    { 
    case INT_TYPE: 
     int_reduction(op, var, size); 
     break; 
    ... 
    } 
} 

int_reductionがインライン化され、他のさまざまな最適化が行われている場合は、このランタイム評価:

より安全なアプローチは、明示的に呼び出し側からint_reduction()を呼び出すために、またはランタイムでタイプを決定する一般的な関数を呼び出すことです必ずしも難読化されたマクロよりもはるかに遅いわけではありませんが、はるかに安全です。

+1

はい、あなたの考えに感謝します。しかし、どのようにタイプを見つけるのですか?それは私の大きな課題です。残りは単なる構文です。もちろん、私は最良の構文についてのあなたのコメントに同意します。上記の場合、たとえば、与えられた変数がINT_TYPEであることをどのように知ることができますか?私は "reduce(+:x)"を別の呼び出し "reduce(INT_TYPE、PLUS、&var、sizeof(x));に移動するように書いています。 xはINT_TYPE型を持っていることを知っていなければなりませんが、どうやって?xの型を取得するためにパーサ全体を書くのに足りないのですか? –

+1

@AmittaiAviramどのように変数を 'int'として宣言するのですか?私はあなたのデータの性質やどこから来ているのか分からないので、その質問に答えることはできません。それが何らかの外部生データであれば、計算を行う前にデータ型を決定する必要があります。それは、Cプログラミング構文とは何の関係もないアルゴリズムの問​​題です。 – Lundin

+1

@ Lundin - いいえ、いいえ。写真はこれに似ています。上に 'int x'宣言がある' main'関数があるとします。それから100行を下ろして、 '#pragma omp parallel reduction(+:x)'をしてください。現在のスコープで宣言されているので、 'x'には' int '型があります。プログラマはこれを知っていて、関数呼び出しに自分自身を注釈を付けることができますが、私はこれを自動的に行うことができます。 –

7

C11 _Genericは直接の解決策ではありませんが、のようにすべてのタイプをコーディングする患者であれば、それはあなたが所望の結果を達成することができない:種類のトンとの良好な出発点

#define typename(x) _Generic((x), \ 
    int:  "int", \ 
    float: "float", \ 
    default: "other") 

int i; 
float f; 
void* v; 
assert(strcmp(typename(i), "int") == 0); 
assert(strcmp(typename(f), "float") == 0); 
assert(strcmp(typename(i), "other") == 0); 

in this answerを見つけることができます。

GCCのみが4.9でサポートを追加しました。

4

関数の型sizeofを使用して、不明な型の変数をvarとすることができます。二つ以上の主要なタイプが同じサイズを持っている可能性がある場合 は、それ汝

if(sizeof(var)==sizeof(char)) 
     printf("char"); 
    else if(sizeof(var)==sizeof(int)) 
     printf("int"); 
    else if(sizeof(var)==sizeof(double)) 
     printf("double"); 

は合併症につながっます。

関連する問題