typedef のちょっとした tips

先日、とあるプログラマーにあるテクニックを紹介してあげた。

ある ID の型を定義するときに、こういった書き方をすることはよくある。

/* ClientId と ServerId はまったくの別物だけど int として定義 */
typedef int ClientId;
typedef int ServerId;

しかし、上記の定義の仕方だと、間違えて ClientId に ServerId を代入した場合も特にエラーは発生しない。

/* ここは function.h で定義 */
typedef int ClientId;
typedef int ServerId;

ClientId getClientId();
ServerId getServerId();
/* ここまで function.h */

/* ここは function.c で定義 */
ClientId getClientId() { ... }
ServerId getServerId() { ... }
/* ここまで function.h */

/* これは main.c */
int main()
{
    ClientId cid = getServerId(); // 代入できちゃう
    ...
}

これを防ぐにはこんなテクニックがある

/* ここは function.h で定義 */
struct ClientOpaque;
typedef struct ClientOpaque* ClientId;

struct ServerOpaque;
typedef struct ServerOpaque* ServerId;

ClientId getClientId();
ServerId getServerId();
/* ここまで function.h */

/* ここは function.c で定義 */
ClientId getClientId() { ... }
ServerId getServerId() { ... }
/* ここまで function.c */

/* これは main.c */
int main()
{
    ClientId cid = getServerId(); // cannot convert `ServerOpaque*' to `ClientOpaque*'
    ...
}

function.h で struct ClientOpaque; しか定義してないけど、これはオッケー。
使ってるのは ClientOpaque のポインタだけなので、struct ClientOpaque のサイズを知らなくてもコンパイルすることができる。


でもって実際 struct ClientOpaque の中身を操作するのは function.c なので、その中でstruct ClientOpaque の定義を書けばオッケー。


どっかでこのテクニックを知ったんだが、どこで知ったか忘れてしまった・・・
あとこの方法ってなんて名前なんだろう。。。