web-dev-qa-db-ja.com

Cで強力な型チェックを実施します(typedefの型の厳密さ)

同じタイプのtypedefに明示的なキャストを強制する方法はありますか? utf8を処理する必要があり、文字数とバイト数のインデックスと混同されることがあります。したがって、いくつかのtypedefがあるのは素晴らしいことです。

typedef unsigned int char_idx_t;
typedef unsigned int byte_idx_t;

さらに、それらの間に明示的なキャストが必要です。

char_idx_t a = 0;
byte_idx_t b;

b = a; // compile warning
b = (byte_idx_t) a; // ok

そのような機能がCに存在しないことは知っていますが、それを行うトリックまたはコンパイラ拡張機能(推奨されるgcc)を知っているかもしれません。


[〜#〜] edit [〜#〜]私はまだ一般的にハンガリアン記法があまり好きではありません。プロジェクトのコーディング規則のため、この問題には使用できませんでしたが、タイプが同じで意味が非常に似ている別の同様のケースで使用しました。そして私は認めなければなりません:それは助けになります。 「i」で始まるすべての整数を宣言することは決してありませんが、重複する型のJoelの例のように、命を救うことができます。

36
quinmars

「ハンドル」タイプ(不透明なポインター)の場合、Microsoftは構造体を宣言してから、構造体へのポインターをtypedefするというトリックを使用します。

#define DECLARE_HANDLE(name) struct name##__ { int unused; }; \
                             typedef struct name##__ *name

その後、代わりに

typedef void* FOOHANDLE;
typedef void* BARHANDLE;

彼らはします:

DECLARE_HANDLE(FOOHANDLE);
DECLARE_HANDLE(BARHANDLE);

だから今、これはうまくいきます:

FOOHANDLE make_foo();
BARHANDLE make_bar();
void do_bar(BARHANDLE);

FOOHANDLE foo = make_foo();  /* ok */
BARHANDLE bar = foo;         /* won't work! */
do_bar(foo);                 /* won't work! */   
20
Tim Lesher

あなたは次のようなことをすることができます:

typedef struct {
    unsigned int c_idx;
} char_idx;

typedef struct {
    unsigned int b_idx;
} byte_idx;

次に、それぞれをいつ使用しているかがわかります。

char_idx a;
byte_idx b;

b.b_idx = a.c_idx;  

これで、それらが異なるタイプであるが、それでもコンパイルされることがより明確になりました。

19
lillq

必要なものは「strongtypedef」または「stricttypedef」と呼ばれます。

一部のプログラミング言語[Rust、D、Haskell、Ada、...]は、言語レベルでこれをサポートしていますが、C [++]はサポートしていません。 「opaquetypedef」という名前の言語に含めるという提案がありましたが、受け入れられませんでした。

しかし、言語サポートの欠如は実際には問題ではありません。エイリアスを作成するタイプを、タイプTのデータメンバーが1つだけある新しいクラスにラップするだけです。繰り返しの多くは、テンプレートとマクロによって除外できます。この単純な手法は、直接サポートされているプログラミング言語と同じくらい便利です。

15
libeako

糸くずを使用します。 Splint:Types および strong type check を参照してください。

強力な型チェックにより、プログラミングエラーが明らかになることがよくあります。 Splintは、プリミティブC型を一般的なコンパイラ(4.1)よりも厳密かつ柔軟にチェックでき、ブール型(4.2)をサポートします。さらに、ユーザーは情報隠蔽(0)を提供する抽象型を定義できます。

7
Eugene Yokota

Cでは、ユーザー定義タイプ間の区別、つまりコンパイラーによって強制されるのみstructs間の区別です。個別の構造体を含むtypedefはすべて機能します。設計上の主な質問は異なる構造体タイプで同じメンバー名を使用する必要がありますか?その場合、マクロやその他の壊血病のトリックを使用して、ポリモルフィックコードをシミュレートできます。そうでない場合は、2つの異なる表現に真剣に取り組んでいます。例:できるようにしたいですか

#define INCREMENT(s, k) ((s).n += (k))

byte_idxchar_idxの両方でINCREMENTを使用しますか?次に、フィールドに同じ名前を付けます。

5
Norman Ramsey

あなたは拡張機能について尋ねました。ジェフ・フォスターの CQual はとてもいいです、そして私はそれがあなたが望む仕事をすることができると思います。

3
Norman Ramsey

BOOST_STRONG_TYPEDEF で定義されている強力なtypedefを使用します

C++を作成している場合は、unsignedintのラッパーである異なる名前の2つの同じように定義されたクラスを作成できます。私はあなたがCでやりたいことをするためのトリックを知りません。

2
John D. Cook

C++ 11では、列挙型クラスを使用できます。

enum class char_idx_t : unsigned int {};
enum class byte_idx_t : unsigned int {};

コンパイラーは、2つのタイプ間で明示的なキャストを強制します。薄いラッパークラスのようなものです。残念ながら、演算子のオーバーロードはありません。 2つのchar_idx_tを一緒に追加する場合は、それらをunsignedintにキャストする必要があります。

2
Charles Cooper