ぼやきごと/2012-05-31 のバックアップ(No.1)


C++:ビットフィールドのビット数を template 引数で指定

ふと「ビットフィールドのビット数を template 引数で指定できるのか?」と疑問に思った。
ビット数指定に求められる要件がコンパイル時定数であることなら template 引数でもいけるはず。

そんなわけで実際にやってみた。

すべて開くすべて閉じる
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 
 
-
!
 
-
|
|
|
!
 
-
!
 
-
|
|
|
|
|
|
!
 
-
!
-
|
|
|
|
|
|
|
|
|
!
#include <iostream>
 
// ビット数 N のビットフィールド3つを持つ構造体
template<std::size_t N>
struct Data
{
    unsigned long value1 : N;
    unsigned long value2 : N;
    unsigned long value3 : N;
};
 
// Data<N> の内容出力
template<std::size_t N>
void printData(const Data<N>& data)
{
    std::cout
        << "Data<" << N << ">[size:" << sizeof(data) << "] = { "
        << data.value1 << ", "
        << data.value2 << ", "
        << data.value3 << " };"
        << std::endl;
}
 
// メイン関数
int main(int, char**)
{
    Data<1> data1 = { 1, 0, 1 };
    Data<13> data13 = { 2, 30, 400 };
    Data<22> data22 = { 5000, 60000, 700000 };
 
    printData(data1);
    printData(data13);
    printData(data22);
 
    return 0;
}

VC++2010でもgcc4.3.0でもコンパイル&リンクが通り、実行すると次のように出力された。

Data<1>[size:4] = { 1, 0, 1 };
Data<13>[size:8] = { 2, 30, 400 };
Data<22>[size:12] = { 5000, 60000, 700000 };

問題なく指定できるようだ。
だから何だという話ではあるけども。

Category: [プログラミング][C++] - 2012-05-31 05:27:56

C++:ビットフィールドのアドレスは取得できない

一度でも試したことのある人やC/C++仕様を熟知している人なら既知だと思いますが、ビットフィールドのアドレスを取得することはできません。
つまりポインタや参照を取る関数の引数に渡すこともできません。

すべて開くすべて閉じる
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
-
!
 
 
-
|
|
!
 
 
-
|
|
|
!
// コンパイルエラーになるコードの例
#include <utility> // for swap
 
struct ByteData
{
    unsigned char low   : 4;
    unsigned char high  : 4;
};
 
int main(int, char**)
{
    ByteData data = { 1, 2 };
    std::swap(data.low, data.high); // エラー C2664
    return 0;
}

上記のようなコードを書くと、VC++2010などではコンパイルする以前にIntelliSenseに怒られます。*1
そしてもちろんコンパイルするとコンパイルエラーになります。

ちなみに、STLの std::bitset テンプレートクラスも値を1ビット単位で扱いますが、こちらはどうかというと…

すべて開くすべて閉じる
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 
 
 
 
-
|
|
|
|
!
#include <bitset>
#include <utility> // for swap
 
int main(int, char**)
{
    std::bitset<2> bits;
    bits[0] = 1;
    std::swap(bits[0], bits[1]);
    return 0;
}

何の問題も無くコンパイル&リンク成功して実行できます。

std::bitset<N> クラスの非const版 operator[] オーバロードはビットの参照を返すわけではなく、 std::bitset<N>::reference クラスの値を返します。
このクラスが std::bitset<N> インスタンスの特定ビットに対する操作を行うようになっています。
詳しくは bitset ヘッダを読みましょう。

例えば構造体定義のあるソースコードを解析して各メンバ値を別の構造体等へコンバートするコードを自動生成するような処理を作る場合にメンバの中にビットフィールドが含まれているとハマることがあるので気を付けましょう。
例がやけに具体的なのは気にしてはいけません。いけませんったら。

Category: [プログラミング][C++] - 2012-05-31 04:39:18

*1 VC++2010のIntelliSenseは問題ないコードでも怒ることがあったりしますが。