scanfに対応するC++のクラスを作ってみる~

可変引数は「危険」なので、C++では「printf」を使わない文化がある。
それは、以前に説明した、しかしながら、printf のコンビニエンス性は捨てがたい、
そこで、boost::format に習って、組み込み向け utils::format クラスを提供した。


それでは、入力はどうしたものか・・・
一般的には、scanf を使って文字列中の数字を変換する。
しかし、scanf は C の API で、可変引数も使う、なので、format クラスに対応する
入力クラスも実装する必要性が大きい、そこで、input クラスを実装してみた。
※良くある、数字の変換に付き物の機能も追加した。

以下のように使う。

    int a;
    uint32_t b;

    int n = (utils::input("%d[, ]%x", "-99 100") % a % b).num();
    std::cout << a << ", " << b << std::endl;
    std::cout << "N: " << n << std::endl;

結果:

-99, 256
N: 2

上のサンプルでは、「%d」、「%x」の仕切りとして、「,」又は「 」(スペース)を
使う事ができる。
これは、正規表現に似た書式を簡略化したものだが、機能的には最低限の物しか無い。
※豊富な機能を盛り込むとコードが肥大化するし、たぶん実際には、それ程高機能で
無くても困らないと思えるからだ。

また、取り込んだ数は、「num()」を使って取得する事ができる。

input クラスの書式は以下のようになっている。

//-----------------------------------------------------------------//
/*!
    @brief  コンストラクター
    @param[in]  form    入力形式
    @param[in]  inp     変換文字列(nullptrの場合、sci_getch で取得)
*/
//-----------------------------------------------------------------//
basic_input(const char* form, const char* inp = nullptr);

basic_input クラス・テンプレートは、以下のように typedef されている。

typedef basic_input<def_chainp> input

ここで、「def_chainp」クラスは以下のようになっている。
自前の特殊クラスを作って食わせる事ができる。

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
    @brief  標準入力ファンクタ
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
class def_chainp {
    const char* str_;
    char        last_;
    bool        unget_;
public:
    def_chainp(const char* str = nullptr) : str_(str), last_(0), unget_(false) { }

    void unget() {
        unget_ = true;
    }

    char operator() () {
        if(unget_) {
            unget_ = false;
        } else {
            if(str_ == nullptr) {
                last_ = sci_getch();
            } else {
                last_ = *str_;
                if(last_ != 0) { ++str_; }
            }
        }
        return last_;
    }
};

※「unget()」は、一文字を書き戻す。

input.hpp のプロジェクト