Cのテンプレートはい。 純粋なC。C++ではない

なんで?



いくつかのキーワードだけが互いに異なる一連の関数を記述する必要があることを想像してみましょう(そして、原則として、そのうちの1つは型名です)。 さて、ここで、たとえば、異なるタイプの配列の要素の合計を計算する関数を見てみましょう (単純化のため、ゼロへの不等式へのポインターのチェックは省略されます/ *単純化のため、intのオーバーフローの可能性も考慮されません-約レーン* /)



void sum_float(int n, float *a, float *b) { /* computes a:=a+b where a and b are two arrays of length n */ int i; for(i=0;i<n;i++) a[i]+=b[i]; } void sum_double(int n, double *a, double *b) { /* computes a:=a+b where a and b are two arrays of length n */ int i; for(i=0;i<n;i++) a[i]+=b[i]; } void sum_int(int n, int *a, int *b) { /* computes a:=a+b where a and b are two arrays of length n */ int i; for(i=0;i<n;i++) a[i]+=b[i]; }
      
      





さて、同意して、関数の本体を一度記述し、受け入れられた(および返された)型の名前を「パラメーター」として示してから、特定の型の関数のインスタンスを決定する方が良いでしょうか。 そして、これらの関数はまだ比較的単純ですが、それらがより長く、それらのセット-より多くの場合を想像してください。



これは、C ++のこの場合にぴったりです。キーワードテンプレートがあります。 しかし、悲しいかな、純粋なCではありません。



これで、Cの古き良きプリプロセッサを使用して、このキーワードをエミュレートできることがわかります。



Cのテンプレート



いくつかの材料が必要になります。



1:ビレット


最初に、いくつかのマクロを定義しましょう。 これらは別のヘッダーファイルに配置されますが、このファイルは引き続き必要です。 明確にするために、この別個のヘッダーファイルを「templates.h」と呼びましょう。



templates.h

 #ifndef TEMPLATES_H_ #define TEMPLATES_H_ #define CAT(X,Y) X##_##Y #define TEMPLATE(X,Y) CAT(X,Y) #endif
      
      





将来的には、XとYのマクロ定義をX_Yの形式で結合するためにテンプレートマクロが必要になるので、TEMPLATE(関数、タイプ)を記述することにより、この場所でfunction_typeを取得できます。



プリプロセッサCでは、ディレクティブ##を使用すると、2つのトークンを1つに結合できます。 ここで1つの#define TEMPLATE(X、Y)X ## Yの代わりに2つのマクロを使用する理由は、Xがマクロ定義でもあるためです...しかし、それは重要ではありません。 この質問はこの記事の範囲外です。



2:料理


通常の関数は、拡張子が.cのファイルに含まれている必要があり、そのプロトタイプは.hファイルに記述されているはずですよね? さて、それらをすでに書きましょう。 関数が対象とするデータのタイプに対応するパラメーターを示すために、伝統的に「T」という文字を使用します。 後で#defineディレクティブでそれを満たします。



sum_as_template.h

 #ifdef T #include "templates.h" void TEMPLATE(sum,T)(int n, T *a, T *b); #endif
      
      





確かに、このヘッダーファイルには、#ifndef HEADER_H #define HEADER_H ... #endifが繰り返し含まれないように保護するための典型的な設計がないことにすでに気付いています。 そして、これは偶然ではありません。そして、この瞬間に戻ります。 一方、#Ifdef Tは必ずしも必要ではありませんが、ヘッダーファイルが含まれ、タイプが定義されていない場合に非常に役立ちます。 そうでない場合、エラーメッセージはあまり情報にならない場合があります。



今c



sum_as_template.c

 #ifdef T #include "templates.h" void TEMPLATE(sum,T) (int n, T *a, T *b) { /* computes a:=a+b where a and b are two arrays of length n */ int i; for(i=0;i<n;i++) a[i]+=b[i]; } #endif
      
      







3.提供します。


以前に何行書いたか覚えていませんが、要約のために、その数に3または4を安全に掛けることができますか?



all_possible_sums.c

 #include "templates.h" #include "all_possible_sums.h" #ifdef T #undef T #endif #define T float #include "sum_as_template.c" #ifdef T #undef T #endif #define T double #include "sum_as_template.c" #ifdef T #undef T #endif #define T int #include "sum_as_template.c"
      
      







人生のささいなこと:GCC 3行の場合#ifdef T #undef T #endifは1つの#undef Tに置き換えることができますが、Visual C ++があります(少なくともバージョン7までは、そのような自由を許容しません)



さて、そしてHiiiiiiiiid!



all_possible_sums.h

 #ifndef ALL_POSSIBLE_SUMS_H_ #define ALL_POSSIBLE_SUMS_H_ #include "templates.h" #ifdef T #undef T #endif #define T float #include "sum_as_template.h" #ifdef T #undef T #endif #define T double #include "sum_as_template.h" #ifdef T #undef T #endif #define T int #include "sum_as_template.h" #endif
      
      







これで、sum_as_template.hを複数のインクルードから保護しなかった理由が明確になります。関係するタイプごとに1回インクルードします。



4.サーブ


まあ、実際には、それだけです。 あなたは電話することができます:



main.c

 #include "all_possible_sums.h" int main(int argc, char **argv) { int ai[3] = {1,2,3}; int bi[3] = {4,5,6}; float af[3] = {1.0,2.0,3.0}; float bf[3] = {1.5,2.5,3.5}; TEMPLATE(sum,int)(3,ai,bi); TEMPLATE(sum,float)(3,af,bf); return 0; }
      
      









そしてもう一つ。



好奇心reader盛な読者は翻訳者に尋ねます。「unsigned long long」タイプが必要な場合はどうなりますか? 結局、関数「void sum_unsigned long long()」を取得しますか? 翻訳者にとって幸いなことに、著者もこれを提供しました。 typedefを使用します。



 typedef unsigned long long uint64; TEMPLATE(sum,uint64)
      
      







の代わりに

 TEMPLATE(sum,unsigned long long)
      
      









(これは非常に無料の翻訳です。私はすでに記事を書くのが面倒です。Googleはプレーンcの質問に対する関数テンプレートへの答えを知っているので、間違いなくその記事に追加するものは何もありません。消えない良い、死後の公開)



-8 <-[元の投稿はここで終了]-



UPD:ソースで少なくとも4つのエラーを特定し、そのうちの3つ-目で、記事を読んでいる間、GRAFIN99 habrayuzerに多大な感謝を申し上げます。



All Articles