4

同じ目標を達成する複数の機能があるとしましょう。この例は、データ型の「コンストラクター」を作成することです。

String new_String();
String new_String(const char *cstr);
String new_String(String s);

明らかに、これはCでは実現できませんが、このような関数(および関数のオーバーロードが役立つ状況)では、それらに名前を付けるための規則またはベストプラクティスがありますか?

このようなもの?

String new_String();
String new_String_c(const char *cstr);
String new_String_s(String s);

私にはそれがぎこちなくて読みにくいと感じます。またはこのようなもの?

String new_String();
String new_String_from_cstr(const char *str);
String new_String_copy(String s);

これはすぐにばかげてしまう可能性があるので、恐ろしく長いJava名を思い出させます。

int String_last_index_of_any_characters(String s, char *chars, int length);
4

4 に答える 4

3

void*初期化パラメーターと列挙型を受け取る単一の「コンストラクター」を定義できます。

enum StringCtor { SC_DEFAULT, SC_C_STR, SC_COPY };

String new_String(enum StringCtor type, const void *arg);

String s1 = new_String(SC_DEFAULT, 0);
String s2 = new_String(SC_C_STR, "hello");
String s3 = new_String(SC_COPY, &s2);

...の代わりにを使用することもできますvoid *。考え方は同じですva_*が、文字列またはコピーであると想定される場合は、マクロを使用してパラメーターを抽出します。

APIに単一のコンストラクターの外観を持たせたいが、型の安全性を維持したい場合は、上記の手法を使用して実際のコンストラクター実装を作成し、インライン関数とプリプロセッサーのトリックを使用して単一のコンストラクターの外観を与えることができます。タイプセーフティ付き。

String new_StringImpl(enum StringCtor type, const void *arg);

static inline String new_StringImplDefault () {
    return new_StringImpl(SC_DEFAULT, 0);
}
static inline String new_StringImplCstr (const char *s) {
    return new_StringImpl(SC_C_STR, s);
}
static inline String new_StringImplCopy (String *s) {
    return new_StringImpl(SC_COPY, s);
}

#define new_String_Paste(TYPE) new_String_ ## TYPE
#define new_String_SC_DEFAULT(ARG) new_StringImplDefault()
#define new_String_SC_C_STR(ARG) new_StringImplCstr(ARG)
#define new_String_SC_COPY(ARG) new_StringImplCopy(ARG)

#define new_String(TYPE, ...) new_String_Paste(TYPE)(__VA_ARGS__)

String s1 = new_String(SC_DEFAULT);
String s2 = new_String(SC_C_STR, "hello");
String s3 = new_String(SC_COPY, &s2);

可変個引数マクロで注意してSC_DEFAULTください。もう2番目のパラメーターは必要ありません。十分な最適化レベルでは、コードは単一の実装関数の呼び出しに変換され、コンパイル時の型安全性チェックの利点があります。したがって、コーディングを増やすことで、ライブラリのユーザーに、複数のコンストラクター関数のすべての型安全性を備えた単一のコンストラクターAPIの外観を与えることができます。

于 2012-07-14T22:07:03.907 に答える
0

標準的な規則はありませんが、多かれ少なかれ類似した関数の多くのオーバーロードの場合、人々は通常、OpenGL関数の命名規則を参照します。

@ user315052によって提供される方法は、型の安全性の問題(エラーが発生しやすい、変更が難しいなど)や、渡す引数が複数ある場合の不便さのため、通常は好ましくありません。ただし、さまざまな技術的理由で使用されることもあります(カーネルシステムコールが良い例です)。

于 2012-07-14T22:10:30.327 に答える
0

オーバーロードの処理方法については、定評のある規則はないと思います。2番目のアプローチは問題ありません。常識を使用して、名前を適度に短くしますが、それでも簡単に理解できるようにします。

最初のアプローチ(またはOpenGLの場合など)のような体系的な命名規則は、場合によっては良い考えかもしれませんが、コードが少しわかりにくくなります。コードを読む人は、最初に命名規則を理解する必要があります。

于 2012-07-14T22:47:16.340 に答える
0

現代のC、別名C11は、あなたが思っている以上のことをすることができます。_Generic新しい式を使用してC++での関数のオーバーロードに似たものを実行できるジェネリック型マクロがあります。

ライブラリの内部インターフェイスとして、関数宣言が必要です。

String new_String_from_cstr(const char *str);
String new_String_copy(String s);

これで、ジェネリック型マクロは次のようになります。

#define new_String(X)                       \
_Generic((X),                               \
         const char*: new_String_from_cstr, \
         String: new_String_copy)(X)

C11はまだ完全には実装されていませんが、最新のclangコンパイラーにはすでにここで必要なものが備わっていると思います。

他のコンパイラ(gccおよびいとこ)については、それに近い機能をエミュレートするP99を調べることができます。P99には、異なる数のパラメーターを受け取るマクロを処理するためのトリックもあります。

ユーザーインターフェイスなどのツールを使用する場合、関数自体の命名規則はそれほど重要ではなくなります。

于 2012-07-14T22:49:28.273 に答える