iomanipでのsetw()などはどうやって実現するのでしょうか?

[戻る]
Baby Chicken [HomePage] 2009/05/29(金) 18:05:57
bit入力クラスを作成しました。そのクラスのテスト用に、二進数や、16進数で自由に表
記できるクラスを作成しました。
VC++ 2008, Console Application on Vista Businessです。

enum e_manip {DEC, BIN, HEX, ENDL};
    
class obitstream {
public:
    obitstream(): mFlag(DEC) {}
    obitstream operator << (const BYTE byte) {
        ByteToBits(byte);
        return *this;
    }
    obitstream operator << (const e_manip flag) {
        if ((flag != DEC) && (flag != HEX) && (flag != BIN) && 
(flag != ENDL)) return *this;
        if (flag == ENDL) {
            std::cout << std::endl;
        } else {
            mFlag = flag;
        }
        return *this;
    }
    obitstream operator << (const WORD word) {
        ByteToBits(word);
        return *this;
    }
    void LSBitsPrint(const int bin, const int size) { // intのLSB側sizeビ
ットを出力
        if ((size < 1) || (size > 16)) return;
        for (int i = size; i > 0; i--) {
            std::cout << (((bin >> (i - 1)) & 
0x0001) ? '1':'0');
        }
        return;
    }
};

こんな風にして、iostreamのように<<を用いて出力できるようになったのですが、
iomanipにあるsetw()やsetfill()を実現できません。仕方なく、void LSBitsPrint
(const int bin, const int size)のような関数にしました。でもこれでは<<に流せませ
ん。どのように実現すれば良いのでしょうか?

yoh2 [E-Mail] 2009/05/30(土) 14:31:01
ひとつ確認。
obitstreamに、「o」「stream」という名前が付いていますので、
obitstreamに対して出力を行いたいのかな? つまり

  obitstream bs;
  bs << 0x1234; // bsに0x1234を格納したい

としたいのかな? と思えました。obitstream::operator<< の
宣言もそのようなことをしているように見えます。
ところが、obitstream::LSBitsPrintの実装を見ると、
obitstreamから別のストリームに出力、といったことを行っています。
つまり、

  obitstream bs;
  ... // bsに対して値を設定
  std::cout << bs; // coutにbsの内容を出力したい

ということを行いたいように見えます。
実現したいのは、

1. obitstreamに対して整数等を出力 (前者)
2. 他ストリームに対してobitstreamを出力 (後者)

のどちらなのでしょうか?

Baby Chicken [HomePage] 2009/05/30(土) 21:02:08
yoh2様

僕の言葉足らずで申し訳ありません。
obitstream bs;
bs << 0xCA << ENDL;
とかは実現できたのでが、
iostreamでの out << setw(3) << 10;とかのように setw()を同様に流しこめないので
す。ですから已む無く、LSBitsPrintのような普通の関数にしました。
目的はBYTEやWORDのLSB側n bitsを二進数形式で出力させたいのです。そのためsetw(n)
のようなものができれば便利と考えたのです。
でも、僕の能力では実現できません。宜しくご指導お願いします。

επιστημη [E-Mail] [HomePage] 2009/05/31(日) 13:26:07
↓テキトー。

#include <iostream>

class obitstream {
    typedef void (obitstream::*manip_fun)(int);
private:
    int size_;
public:
    obitstream() : size_(32) {}
    obitstream& operator << (const int bin) {
      int size = size_;
      if ((size < 1) || (size > 16)) return *this;
      for (int i = size; i > 0; i--) {
        std::cout << (((bin >> (i - 1)) & 0x0001) ? '1':'0');
      }
      return *this;
    }
    void set_size(int s) { size_ = s; }

    template<typename Function> friend
    obitstream& operator<<(obitstream& stream, Function f) {
      f(stream); return stream;
    }
};

struct setsize {
  int arg;
  setsize(int n) : arg(n) {}
  void operator()(obitstream& stream)
    { stream.set_size(arg); }
};

int main() {
  obitstream stream;
  int n = 73;
  for ( int i = 1; i < 10; ++i ) {
    stream << setsize(i) << n;
    std::cout << std::endl;
  }
}

yoh2 [E-Mail] 2009/05/31(日) 13:40:12
何か勘違いしていたようです。申し訳ない。

obitstreamは何か内部に値を溜め込むだけのクラスだと思っていたのですが、
そうではなくて、出力させたい値を受け取ったら、ビット列に変換した
結果を別のストリーム (std::cout限定?) に出力するための橋渡しとなる
クラスである、ということでしょうか。

もしそうなら、前回の私の回答は明後日の方向を向いているので無視して下さい。
以下、この前提で。
# 今度は明々後日の方向、とかだったら重ねてごめんなさい。

std::setw相当のものは以下のように実装できると思います。ただし未テスト。
DECやENDL等に合わせて、名前を大文字にしましたが、別に定数というわけでは
ないので、小文字にした方がいいかも。

    // SETWの戻り値型。operator<<適切にオーバーロードできるように、専用の型を定める
    struct setw_t { int n; };

    setw_t SETW(int n) {
        setw_t retval = {n};
        return retval;
    }

    class obitstream {
        int width;
        ...
    public:
        obitstream(): width(初期値) ...

        // 幅を定めるためのoperator<<
        obitstream &operator<<(setw_t w) {
            width = w.n;
            return *this;
        }

        obitstrem &operator<<(出力すべき整数) {
            // this->widthから出力すべき幅を見て出力
            // STLのstd::setw(n)の効果と似せるには、出力後、this->widthを
            // 初期値にリセットする必要があるかも
        }
    };


<余談>
    obitstreamを作るのではなく、値→std::string変換関数(orクラス)を
    作った方が楽なのでは? というのは前提を覆しすぎ?

    変換関数名を bin_format とするなら、
        std::cout << bin_format(値);
    とか
        std::cout << bin_format(値, 幅);
    とかできますし。
</余談>

yoh2 [E-Mail] 2009/05/31(日) 13:56:44
あ、被ってたorz。
まあ、実装詳細は違うので、
  bitstream stream;
  stream << 何か(幅);
として、幅を設定する方法はいくつかあるよ、ということを示せた、ということでここは
ひとつ(汗

Baby Chicken [HomePage] 2009/05/31(日) 19:23:55
[[解決]]
επιστημη様
ご指導ありがとうございます。僕のレベルではtemplateやfriendsがなかなかピンと来な
いので未だ理解できません。すみません。

yoh2様
多分επιστημη様の示されたtemplateをtemplateでなく示して頂いたのだと思い
ました。しかし、残念ながらまだ僕の知識レベルではついていけません。

もう少し精進して、理解できるように頑張ります。本当にありがとうございました。
(とりあえずは、通常の関数呼び出しで対応します)

επιστημη [E-Mail] [HomePage] 2009/05/31(日) 21:11:34
↓ template使わない版

#include <iostream>

class obitstream {
    typedef void (obitstream::*manip_fun)(int);
private:
    int size_;
public:
    obitstream() : size_(32) {}
    obitstream& operator << (const int bin) {
      int size = size_;
      if ((size < 1) || (size > 16)) return *this;
      for (int i = size; i > 0; i--) {
        std::cout << (((bin >> (i - 1)) & 0x0001) ? '1':'0');
      }
      return *this;
    }
    void set_size(int s) { size_ = s; }
};

struct manip {
  virtual void exec(obitstream& stream) const =0;
};

obitstream& operator<<(obitstream& stream, const manip& f) {
  f.exec(stream); 
  return stream;
}

struct setsize : manip {
  int arg;
  setsize(int n) : arg(n) {}
  virtual void exec(obitstream& stream) const
    { stream.set_size(arg); }
};

int main() {
  obitstream stream;
  int n = 73;
  for ( int i = 1; i < 10; ++i ) {
    stream << setsize(i) << n;
    std::cout << std::endl;
  }
}

Baby Chicken [HomePage] 2009/06/01(月) 14:35:17
επιστημη様

度々のご指導ありがとうございます。感謝しております。

古いスレッドには追加発言できません。