第3回 境界調整(アラインメント)を調べる

組込み開発では、しばしば型の境界調整への配慮が必要になってきます。ところが、境界調整の要求サイズは完全に処理系に依存しますし、境界調整を調べるための専用かつ標準的な機能もないのが現状です。

確かに境界調整を調べるための専用の機能はありませんが、既存かつ標準の機能の組み合わせで調べるための方法はあります。残念ながら、この方法は C と C++ では異なるものになってしまいます。この辺りは C と C++ の微妙な非互換性に起因しているのです。

C の場合

まずは C の場合についてです。これは <stddef.h> で定義されているマクロ offsetof を使うことで実現できます。

#define alignof(type) offsetof(struct { char a; type b; }, b)

char 型と調査対象の型のメンバで構成される構造体を使用し、調査対象の型のオフセットを調べれば境界調整を調べることができます。境界調整が 1 バイトであれば、フィールド .a の直後に配置されるのでオフセットは 1 になりますし、境界調整が 2 バイトであれば、1 バイトの詰め物が入るのでオフセットは 2 になるといった具合です。

C++ の場合

さて、C++ の場合ですが、C と同じ方法は使えません。これは、C++ では式の中でクラス*1を定義することができないからです。そこで、基本的な考え方は同じですが、次のようにすることで解決できます。

template <typename T>
class alignof
{
    struct helper
    {
        char a_;
        T b_;
    };
public:
    static const std::size_t value = offsetof(helper, b_);
};

このクラスは、alignof<type>::value のように使うことができます。必要であれば、次のようにすることで、C と互換性を持たせることもできます。

#ifndef __cplusplus
#define alignof(type) offsetof(struct { char a; type b; }, b)
#else
#define alignof(type) alignof<type>::value
#endif

ところで、上記の方法では型を指定して境界調整を調べることはできますが、オブジェクトを指定して境界調整を調べることはできません。オブジェクトを指定する場合は、そのオブジェクトを参照渡しするような関数テンプレートを使えば実現できますが、sizeoftypeid のように型でもオブジェクトでもオペランドにとる方法は無理です。

大きな問題点

もう一つ、この方法には大きな欠点があります。というのは、調査対象の型は C 互換型( POD 型)でなければならないということです。なぜなら、offetof マクロに指定できるのは、C 互換構造体または C 互換共用体だけ( JIS X3014:2003 18.1 型 段落 5 )であるため、C 互換型以外のメンバを持つことができないからです。実際には、C 互換型以外を指定しても期待通りに動いてしまうことが多いのですが、動作が保証されないことは間違いありません。

さらに、これは処理系側の問題なのですが、& 演算子を多重定義していると、offsetof マクロが正しく動作しない場合があります。& 演算子はメンバでなくても構わないため、C 互換型であっても(たとえ後からでも)多重定義できてしまいます。このような場合でも、規格上は offsetof マクロが正しく機能しなければならないのですが、そうなっていない場合が少なくないのです。

このように、offsetof マクロにバグがある場合には、自分で offsetof マクロを定義しなおす必要があります。例えば、次のようにです。

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

次期規格への期待

このように、C++ で型の境界調整を調べるには、いろいろな問題が付きまとうわけですが、それも次期規格では解消しそうです。Boost C++ Libraries由来のクラステンプレートがTR1で導入されたからです。具体的には、<type_traits> ヘッダで定義された std::tr1::alignment_of クラステンプレートがそれにあたります。

興味のある方は、(組込みとは直接関係ありませんが)一度 Boost C++ Libraries をインストールして試してみてください。Boost C++ Libraries では、基本的に <boost/type_traits.hpp> ヘッダをインクルードし、boost::alignment_of クラステンプレートとして使用することになりますが、バージョン 1.34 からは TR1 ライブラリが追加になったため、<type_traits> ヘッダをインクルードして std::tr1::alignment_of クラステンプレートとして使用することも可能になっています。


*1 C++ では構造体もクラスの一種です。

トラックバック


URL から "-nospam" を削除してトラックバックを送信してください。
このエントリーを含むはてなブックマーク