C99に対応した標準Cライブラリの実装レポートを行っていきます。

プロフィール 

Author:高木信尚

ホームページ
ブログ

最近の記事 

最近のコメント 

最近のトラックバック 

月別アーカイブ 

カテゴリー 

ブロとも申請フォーム 

この人とブロともになる

ホーム 全記事一覧 << 前の記事 次の記事 >>

 

2008/10/07 11:41|

 

今回は、標準Cライブラリにおける共通定義を行う<stddef.h>についてお話します。<stddef.h>は、標準Cライブラリのヘッダの中でも、最も処理系に依存するものです。さらに、C++にも対応するとなると、いろいろ面倒なことをやらなければなりません。

まずは、size_t型からです。size_t型はsizeof演算子の結果の型ですので、意味的には、gcc拡張である__typeof__を使って、

typedef __typeof__(sizeof(int)) size_t;

とするのが妥当かと思います。ただ、__SIZE_TYPE__というマクロが予め定義されていますので、今回は、

typedef __SIZE_TYPE__ size_t;

としたいと思います。なお、size_t型は、<stddef.h>以外のいくつかのヘッダでも定義されていますので、次のように二重定義のガードを行います。

#ifndef _SIZE_T
#define _SIZE_T
typedef __SIZE_TYPE__ size_t;
#endif

次に、ポインタどうしの減算の結果の型であるptrdiff_t型ですが、__SIZE_TYPE__と同様に、ptrdiff_t型のために__PTRDIFF_TYPE__というマクロも予め定義されているようですので、

typedef __PTRDIFF_TYPE__ ptrdiff_t;

とします。ptrdiff_tは<stddef.h>のみで定義されるので、二重定義のガードは必要ないでしょう。

同じく、ワイド文字を表す型であるwchar_t型も、__WCHAR_TYPE__マクロが予め定義されていますので、

#if defined(_WCHAR_T) && !defined(__cplusplus)
#define _WCHAR_T
typedef __WCHAR_TYPE__ wchar_t;
#endif

のように書くことができます。wchar_tは、C++では予約語ですので、__cplusplusマクロを用いて、C++のときは定義しないようにしています。

次は、空ポインタ定数を表すNULLです。NULLは、CとC++では定義を変える必要があります。C言語では、void*型から他の型へのポインタに暗黙の変換ができますは、C++では明示的なキャストが必要なため、

#define NULL ((void*)0)

とすることができません。通常、C++ではNULLを 0 または 0L に定義しますが、gccではNULLを定義するために__nullというマジック値が提供されています。したがって、

#ifndef __cplsuplus
#define NULL ((void*)0)
#else
#define NULL __null
#endif

とすることで、CおよびC++の両方に対応することができます。

さて、最後はoffsetofです。このマクロは構造体や共用体のメンバの先頭からのオフセットを調べるためのものです。これも、CとC++では、定義を変更する必要があります。
Cでは、

#define offsetof(type, member) ((size_t)&((type*)0)->member)

と書けば問題ありません。しかし、C++では、&演算子が多重定義されていると期待通りに動作しません(C互換型でも、非メンバ関数として多重定義できます)。したがって、ちょっとした工夫が必要になります。

#define offsetof(type, member) \
        ( __offsetof__ ( reinterpret_cast<std::size_t> \
            (&reinterpret_cast<const volatile char*&> \
                (static_cast<type*>(0)->member) ) ) )

のようにしなければなりません。ここで__offsetof__は、それに続く括弧内に、本来であれば整数定数式に含まれてはならない構文要素があった場合でも、整数定数式とすることができるgccの拡張機能です。いったん、const volatile char&型にキャストすることで、例え、&演算子が多重定義されていたとしても、無視することができます。また、size_t型はstd::size_t型としています。


ここまでの内容で、C言語に関しては問題ないのですが、C++に対応するためには、もう少し手間がかかります。なぜなら、<stddef.h>の中で定義される識別子は、いったんstd名前空間の中で定義され、その後、大域的名前空間に導入しなければならないからです。

最も簡単な方法は、これまでの内容を<stddef.h>に記述するのではなく、いったん<cstddef>に記述して、それを<stddef.h>からインクルードするというものです。

まず、<cstddef>は次のようになります。

/* <cstddef>
#ifndef _CSTDDEF
#define _CSTDDEF

#ifdef __cplusplus
namespace std {
#endif
... /* 型定義 */
#ifdef __cplusplus
}
#endif
... /* マクロ定義 */
#endif

次に、<stddef.h>から、<cstddef>をインクルードします。

/* <stddef.h> */
#ifndef _STDDEF_H
#define _STDDEF_H

#include <cstddef>

#ifdef __cplusplus
using std::size_t;
using std::ptrdiff_t;
#endif

#endif

これで、C++にも対応することができるようになります。

多くの標準ライブラリの実装では、<stddef.h>で定義される型名が、std名前空間ではなく、大域的名前空間で直接定義されていますが、それは正しくありません。
2006/01/24 19:45|処理系の特性TB:0CM:0

コメント
コメントの投稿

管理者にだけ表示を許可する


トラックバック
トラックバックURLはこちら
http://libc.blog47.fc2.com/tb.php/11-57cefbda

ホーム 全記事一覧 << 前の記事 次の記事 >>

ブログ内検索 

お勧め書籍 

RSSフィード 

リンク 

このブログをリンクに追加する

Powered By FC2ブログ 

Powered By FC2ブログ
ブログやるならFC2ブログ

Copyright(C) 2006 TAKAGI Nobuhisa All rights reserved.
Powered by FC2ブログ. 無料ホームページ アフィリエイト レンタルサーバー FC2ブログ 一戸建て template designed by 遥かなるわらしべ長者への挑戦.