« 2009年1月 | トップページ | 2009年3月 »

2009年2月の5件の記事

【C/C++】 bitset (STL)

STLのコンテナではないけど、C++標準に含まれてるテンプレートクラスでbitsetというのがあります。これは中々面白い。要はビット単位でデータを保持するクラスなんやけど、何につかうか?と言うと、例えば

  • コマンドオプションのフラグ情報保持
  • メモリマネージャのセグメント管理
  • データキャッシュのフラグ管理

ちょっと変わった所では

  • 可変長ビット演算

なんて物も考えられるかな?
まずは簡単な使い方。
128ビットの領域を確保して任意のビットの値を取得したりセットしたり...

bitset<128> bits;
bits.reset();                       // 全ビットクリア
bits[10] = 1;                      // 10bit目を1に設定
cout << bits[10] << endl;    // 10bit目の値を表示
bits.set( 10, 0 );                 // 10bit目を0に設定

[]演算子がオーバロードされているから、特定ビットの値を取得したり書き換えたりが簡単にできる。
自分で書いてみると分かるけど、これと同じ挙動するクラスを書くのはちょっと面倒。
コード量とかって言うより適切なバイト位置を経産してビットシフトして...って実装をすると結構バグるもんです。

ただ、このままやと単なるビットしか扱えないのでちょっと不便。
という事で、以下のようなメンバ関数があったら良いんではないかな?

void set_value( int p, int n, int len )
{
    for ( int i = 0; i < len; i++ )
        bits[p+i] = (n >> i) & 1;
}

このコードでは、ビット位置の大きい方がMSBとして処理しています。
bitsetのoperator[]演算子では0以外の値は全て1とみなすようなので、最下位ビットだけを論理積で取得しておく必要があるようやね。同じような感じでget_valueも作っておくと良いでしょう。

bitsetのメンバ一覧を書いておきます。(気づいたものだけやから全部ではないかも)

bitset<N>( 初期値 )            // コンストラクタで初期値(32ビットまで可)が指定可能
bitset<N>::reset()               // 全ビットクリア
bitset<N>::set(p)                // pビット目を1にする
bitset<N>::set(p,n)              // pビット目をnにする ( n == 0 ) ? 0 : 1
bitset<N>::operator[](p, n)    // pビット目をnにする
bitset<N>::operator<<(s)      // 全体をsビット左シフト(ビット番号の大きい方にシフト)
bitset<N>::operator>>(s)      // 全体をsビット右シフト(ビット番号の小さい方にシフト)

【C/C++】ifstreamのeofメンバ関数(違和感がある挙動)

ifstreamでバイナリファイルを読み込んだ時の挙動で気になる事が...
通常、バイナリファイルを全て読み込む時には

ifstream ifs
ifs.open( ファイル名, ios::in | ios::binary );
if ( ifs ) {
    while( !ifs.eof() ) {
        ifs.read( (char*)変数, sizeof(変数) );
    }
}

ってな感じで変数に逐次読み込んで行くのだけど、これだと何故かバイナリファイル中のサイズ+1のreadがされてしまうみたい。なぜかeof()がtrueになるのは、ファイルの全データを読み込んだ後のread関数を呼んだ後のような...。
取り合えず以下のようにすると、その問題を回避できるみたい。

ifstream ifs
ifs.open( ファイル名, ios::in | ios::binary );
if ( ifs ) {
    while( true ) {
        ifs.read( (char*)変数, sizeof(変数) );
        if ( ifs.eof() ) break;
    }
}

readを呼んだ後にeofをチェックしてあげると。
でも、この場合空ファイル(サイズ0バイトのファイル)を開くと必ず1ワードの無効値が取得できてしまう。それを避けるうまい方法がイマイチ思いつかない...

なんでこうなるか?という原因やけど、おそらくifstreamクラスの中でのeofチェックは実際にデータ取得をする時に判定されているのではないかと推測される。つまり、ifs.eof()はファイルの終端まで読み終わったかどうかを知る関数であって、ストリームポインタがファイル終端にある事を知る関数ではないのではないかと。その証拠に、最初のコードで0バイトのファイルを開くとwhileの条件文が真となってループの中が1度だけ実行される。

こうなると、やっぱりストリームイテレータを使ってファイルから読み込むのが良いのやろうけど、そっちはそっちで文字列読み込み -> 数値変換って挙動をするようで...。>>演算子をオーバーロードするという手もあるのだけど、他にも影響でそうやしなぁ。(ビットシフトする箇所あるし...あ、でもfstreamに対してビットシフトはしないから問題ないのか?)

今日は、この挙動に違和感を感じた一日でしたなぁ。

(2009/12/12追記)

一応の解決をみましたので、解決法にリンク貼っておきます。

【C/C++】 streamクラスのeofメンバ

【C/C++】 iostreamのマニピュレータ

iostreamのマニピュレータ一覧を載せておこうっと。
使い方は cout << マニピュレータ とか cin >> マニピュレータでOK。
マニピュレータを使う場合は #include <iomanip> しておく。

8進数                oct
10進数              dec
16進数              hex
改行                 endl  (ostreamのみ)
NULL文字          ends  (ostreamのみ)
フラッシュ           flush  (ostreamのみ)
空白削除           ws    (istreamのみ)
基数設定           setbase(n)   n=0,8,10,16
幅指定              setw(n)
フラグのリセット  resetiosflags(i)
フラグのセット    setiosflags(i)
文字埋め          setfill(c)
精度設定          setprecision(n)    n:桁

【C/C++】 標準出力への整形出力

C++の標準出力ではprintfも使えるけど、coutも使えるね。
いっつも悩むのが、どっちを使うか?って事で...
これには多分答えが無いような気がする。
printfだと行単位で任意のフォーマットで表示するのが楽。
coutだと複数行の記述でも(ある程度)見やすく書ける。
って所かなぁ。でもprintfでの複数行の表示はprintf自体を分ければ良いわけやし一長一短かな?

printfは良く使うけどcoutは複雑なんで、整形表示の書式を覚書きしときますか。
coutの書式はfstreamのオブジェクトにも使えるはず。つまりcoutなら標準出力やけど、出力ファイルオブジェクト(ofstrem)であればファイルに出力できると思われ。

何はともあれincludeするヘッダファイルを。

#include <iostream>
#include <iomanip>

変数の内容を出力

cout << 変数

指定の幅で出力

cout << setw(文字幅) << 変数;

指定した幅で頭に0詰めして出力

cout << setw(文字幅) << setfill('0') << 変数;

16進数で表示

cout << hex << 変数;

10進数で表示

cout << dec << 変数;

左詰めで出力

cout << left << 変数;

右詰めで出力

cout << right << 変数;

進数設定と詰め方の設定は内部変数に記録されるようです。
よって、一度でも

cout << hex;

とすると、その後の数値型は16進数で表示されてしまう。10進数に戻すには明示的に

cout << dec;

とする必要がありますね。
右詰め、左詰めも同じ。
ちょっと注意が必要やね。

まぁ、この辺を含めて考えると以外とprintfの方が書きやすかったりする。
coutのメリットは出力する変数の数が多くなっても記述しやすいって事かなぁ。
printfの場合フォーマットに対する引数が可変数引数で渡されてるからフォーマットから要求される変数の数と引数で指定した変数の数が合わないと不正メモリへのアクセスも発生しやすいし、変数の数を変更するのも手間やったりするし。

とは言え、結論は好みって事かなぁ。

【C/C++】 basic_stringクラス

ちょっと仕事でとある組み込み用プロセッサのアセンブラを作ってみようと思っとります。アセンブラに必要な機能として

プリプロセス(トリミング、マクロ展開)
パーシング
ラベル処理
ニモーニック→マシン語への変換

ってな位は必要になるわけです。ここで難しいのがパーシング(構文解析)で、C言語のような高級言語のコンパイラだったらyaccとかbisonとかパーサ出力してくれるコンパイラコンパイラで処理するのだけど、アセンブラ位でそこまでするのも何だかなぁ。しかも今回対象としているアセンブラはマイクロコード用のアセンブラで文法が特殊な構造してるから、ちょいっと独自に作ってみるかと思うわけです。

で、そうなるとどうやって作るかという事ですが、アセンブラは文字列処理を行うわけやし、ラベルの処理とかもあってデータ数が可変になる。あとPCで動作させるという事であればメモリや処理速度には余裕がありそうやね。文字列処理には向いていないけど、C++を使う事にするとSTLを使わない理由はない。Boost C++も使いたいけど標準でない事と環境のセットアップが面倒なんで、C++/STLで実装する事にしました。

そこで使う事になるテンプレートクラスは

string (厳密にはSTLではないけど)
vector (テキストファイルのバッファリング)
map (ラベル処理)

ってな感じかな。マシン語への変換はデザインパターンから Chain of Responsibility パターンを使うと楽そうですな。

話が大分脱線したけど、今回は復習ついでにstringクラスのメンバと使い方を調べてみようかと。
前にSTL使ったのって、もうかれこれ数年前です〜っかり忘れてるんだなぁ。この所組み込みばっかりやったし...

basic_stringクラスの主要メンバ関数

append   文字列の追加
assign     文字列の代入
insert      文字列の挿入
replace   文字列の置換
substr     文字列の一部抽出
at           特定文字への参照ポインタ
begin      文字列の先頭iterator取得
end        文字列の末尾+1 iterator取得
compare 文字列の比較
copy      文字列のコピー
empty    文字列の空チェック
erase     文字列の削除
find        指定文字列の検索
length     文字列の長さ

とりあえずstringクラスだけ。

« 2009年1月 | トップページ | 2009年3月 »