スポンサーサイトビットフィールド

Home > ----- / スポンサー広告 > This Entry 2005-08 / 実装技術 > This Entry [com : 0][Tb : 2]

--------

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

2005-08-02

 制御レジスタの操作など、ビットフィールドが使えればコードがすっきるすることがよくあります。しかし、ビットフィールドは処理系に強く依存するので、移植性を考えるとあまり使用すべきではない機能でもあります。

ビットフィールドが実際にどんなビットアサインに対応するかは、完全に処理系に依存します。バイトオーダーがリトルエンディアンかビッグエンディアンかでビットオーダーも決まると考えるのは、よくある勘違いですが、実際にはビットオーダーとバイトオーダーとは直接関係ありません。

さらに、Cではビットフィールドに使える型はint, signed int, unsigned intの3種類だけ*1であり、intを指定した場合に符号付きになるか符合なしになるかも処理系定義という厄介な問題まであります。

多くの処理系では、int以外の型、すなわちcharやshortやlongなども使えるようになっていますが、独自拡張だけに、それらの配置方法には全く互換性がなく、型の種類ごとにビットフィールドを集める(すなわち宣言どおりの順序にならない)場合も多く見られます。

さてC++ですが、ビットフィールドにはint以外に、char、short、long、boolおよび列挙型を使うことができます。signedもunsignedも付かないchar、short、int、longの場合は符号付きか符号なしかはやはり処理系定義になります。

 このように、C++ではCよりはビットフィールドの型の自由度が増しましたが、配置の互換性といった観点からは何ら状況が変わりません。そこで、ここではC++ならではの方法を用いて、移植性のあるビットフィールドの記述方法をご紹介します。

まずは下のコードを見てください。

#include <limits>
#include <cstddef>

template <typename T, std::size_t Base, std::size_t Length>
class bitfield
{
    typedef std::numeric_limits<T> limits;
    static int force_unsigned_[limits::is_specialized && limits::is_integer && !limits::is_signed ? +1 : -1];
public:
    typedef T value_type;
    static const std::size_t base = Base;
    static const std::size_t length = Length;
    static const T mask = Length < std::numeric_limits<T>::digits ? (T(1) << length) - 1 : ~T(0);

    explicit bitfield(T* ptr = 0) : ptr_(ptr) {}
    operator T() const
    {
        return (*ptr_ >> base) & mask;
    }
    bitfield& operator=(T value)
    {
        *ptr_ = (*ptr_ & ~(mask << base)) | ((value & mask) << base);
        return *this;
    }
private:
    T* ptr_;
};



上のコードはbitfieldクラステンプレートの定義です。ここでは説明のための最小限の実装しか行っていませんが、だいたいのイメージを掴んでいただければよいかと思います。

それではコードの詳細を解説したいと思います。

テンプレート引数のTはメモリアクセスの単位となる型で、必ず符号なし整数型でなければなりません。Baseはビットフィールドの開始位置で、最下位ビットを0とした連番で表します。Lengthはビットフィールドのビット数です。

クラス定義の最初の方にあるforce_unsigned_静的メンバ変数ですが、これは変数というより、型Tが符号なし整数であることを強制するためのものです。もし他の型をTに指定すると、配列要素が-1になり、コンパイルエラーが発生します。

bitfieldのコンストラクタにはポインタを指定します。これにより、指定したアドレスの特定のビットフィールドへのアクセスが可能になります。

変換関数(operator T)と代入演算子はビットフィールドの値の読み書きを行うためのものです。このようにしておけば、通常の変数と同じように、ビットフィールドへの読み書きができるようになります。

このコードは、まだまだ洗練する余地がありますので、そのお話はまた別の機会にしたいと思います。


*1 ISO/IEC 9899:1999(通称C99)では_Boolも指定可能

Comment

Post a Comment









管理者にだけ表示を許可

Trackback

http://cppemb.blog17.fc2.com/tb.php/16-0ec9d51d

何も約束されないビットフィールド

I/Oポートなどの操作にビットフィールドを使っている方は多いと思います。ビット単位で操作ができるわけですから、I/Oポートの操作にビットフィールドが使えると非常に便利なのは確かです。しかし、I/Oポートの操作に最も適さないのがビットフィールドなのです。まず、ビッ ...

移植性のあるCプログラミング : 2006-04-21 01:32

ビットについて

ビットビット(bit)は、ほとんどのデジタルコンピュータが扱うデータの最小単位。英語の binary digit (2進数字)の略であり、2進数の1けたのこと。bit は、:en:John Tukey|John W. Tukey (英語) が1947年1月9日のベル研究所のメモに“binary di ...

単位がないと不便 : 2007-03-26 09:32

C++と組み込み環境 | Page Top▲

New >>
名前空間
<< old
コールバック
ブログ内検索
RSSフィード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。