前回、「fixed_string」テンプレートを紹介したが、本題の「format」に移る。
「format」クラスは、「boost::format」の組み込みマイコン向けに仕様を変更した俺俺クラスだ。
それでも、sprintf などの他ライブラリに依存していない為、「format.hpp」のみで簡潔するようになっている。
※インクルード部
#include <type_traits> #include <unistd.h>
最終的な出力は、「iostream」では無く、ターミナルへの文字出力で、「syscalls」の「write」を使っている。(ディスクリプタは stdout)
また、出力クラスは、テンプレートの引数で与えており、出力ファンクタを実装する事で、色々な出力形態に対応できる。
以前は、固定文字列クラスを実装していなかった為、簡易固定文字列を作り、利用していたが、柔軟性が無く拡張しずらかった為文字列クラスを使える仕様に修正した。
※出来てみると、よりシンプルな構造になり、使いやすく、判りやすくなった。
クラスの型は、こんな感じだ。
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// /*! @brief 簡易 format クラス @param[in] CHAOUT 文字出力ファンクタ */ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// template <class CHAOUT> class basic_format {
※「CHAOUT」へは、基本1文字づつ追加する。
標準の文字出力クラスは以下のようになっている(stdout)
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// /*! @brief 標準出力ファンクタ */ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// class stdout_chaout { uint32_t size_; public: //-----------------------------------------------------------------// /*! @brief コンストラクター */ //-----------------------------------------------------------------// stdout_chaout() : size_(0) { } void operator() (char ch) { char tmp = ch; write(1, &tmp, 1); // FD by stdout ++size_; } void clear() { size_ = 0; }; uint32_t size() const { return size_; } };
「format」クラスの一番大きな変更は、「CHAOUT」をスタティックとした点で。
static CHAOUT chaout_; ・・・ }; // テンプレートクラス定義の外で、static の実態を宣言する。 template <class CHAOUT> CHAOUT basic_format<CHAOUT>::chaout_;
文字出力が、都度、リセットされないので、追加などの制御がやりやすくなった。
CHAOUT の暗黙的な仕様として、「operator(char)」、「size()」があれば良い。
※「clear()」は利便性の為用意してある。
標準的な文字列クラスを使った場合は以下のように、文字の終端を制御するクラスも与える事で、内部的制御を一般化できる。
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// /*! @brief 文字列クラス、出力ファンクタ @param[in] STR 文字列クラス @param[in] TERM ターミネーター・ファンクタ */ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// template <class STR, class TERM> class string_chaout { ・・・ void operator () (char ch) { str_ += ch; if(str_.size() >= str_.capacity()) { term_(str_.c_str(), str_.size()); str_.clear(); } } ・・・ };
「TERM」クラスは、「STR」クラスへの文字追加が出来なくなる前に、挙動を制御する。
void flush() { if(str_.size() > 0) { term_(str_.c_str(), str_.size()); str_.clear(); } } STR& at_str() { return str_; } TERM& at_term() { return term_; }
「flush()」関数と、「STR」クラスへの参照、「TERM」クラスへの参照を追加してある。
※「format」クラスは、これらの関数を必要としない。
CHAOUT クラスをスタティックにすると、一時的に確保した領域(スタック)などで使いたい場合に、使いにくくなってしまう。
そこで、バッファのポインターとサイズを与えて処理を行えるように、コンストラクターを追加した。(これは以前に作ったものを部分的に作り直した。)
リソースはスタティックなので、利便性を考えて関数をリセットする為の仕組みも追加した。
//-----------------------------------------------------------------// /*! @brief コンストラクター @param[in] form フォーマット式 @param[in] buff 文字バッファ @param[in] size 文字バッファサイズ @param[in] append 文字バッファに追加する場合「true」 */ //-----------------------------------------------------------------// basic_format(const char* form, char* buff, uint32_t size, bool append = false) noexcept :
また、この場合に合わせて、「memory_chaout」クラスを用意した。
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// /*! @brief メモリー文字列クラス */ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// class memory_chaout { char* dst_; uint32_t size_; uint32_t pos_; public: //-----------------------------------------------------------------// /*! @brief コンストラクター @param[in] dst 出力先 @param[in] size 最大サイズ */ //-----------------------------------------------------------------// memory_chaout(char* dst = nullptr, uint32_t size = 0) : dst_(dst), size_(size), pos_(0) { } void set(char* dst, uint32_t size) { if(dst_ != dst || size_ != size) { // ポインター、サイズ、どちらか異なる場合は常にリセット pos_ = 0; } dst_ = dst; size_ = size; } void operator () (char ch) { if(pos_ < (size_ - 1)) { dst_[pos_] = ch; ++pos_; dst_[pos_] = 0; } } void clear() { pos_ = 0; } uint32_t size() const { return size_; } };
これで、以下のように使える。
typedef utils::basic_format<utils::memory_chaout> sformat; char tmp[256]; sformat(xxxxxxx, tmp, sizeof(tmp)); sformat(xxxxxxx, tmp, sizeof(tmp), true); // 追加する場合