組み込みでも使える、scanf に代わる C++ input.hpp
scanf に似た仕様で文字列から数値へ変換する C++ クラス
概要
C++ では、C 言語で一般的な可変引数を扱う関数は使わない文化があります。
一番の問題は、引数の引渡しはスタックを経由する点、又、スタックに引数が何個格納
されているのか判らない点です、これによりスタックをオーバーロードさせ、システム
に悪影響を与える事が出来てしまいます。
代表的な実装は「printf」関数です。
コンパイラはフォーマット文を解析して、引数の整合性をチェックしますが、完全にはチェックできません。
一方、「printf」は優れた柔軟性をもたらし、文字、数値を扱う事をたやすくします。
boost には、printf の柔軟性と、安全性を考慮した、format.hpp があります。
※「%」オペレーターのオーバーロード機構を利用して、複数の引数を受け取る事ができます。
※boost::format は優れた実装ですが、iostream に依存していて組み込みマイコンでは問題があります。
※iostream を取り込むと、容量が肥大化します。
その為、組み込みマイコン向けの軽量な「format.hpp」を実装しています。
同じように、printf とは逆の動作をする scanf も安全性においては printf と同じ問題を抱えています。
そこで、format.hpp の仕組みをまねて、scanf に代わる input クラスを実装しました。
仕様
- 基本的な仕様
2進数、8進数、10進数、16進数、浮動小数点、文字などを受け取る事が出来ます。
- 名前空間は「utils」です。
- 文字列の受け取りは、ファンクタを定義して、テンプレートパラメーターとします。
※標準的なファンクタ「def_chainp」クラスが定義されており、以下のように
typedef されています。
typedef basic_input<def_chainp> input;
※「def_chainp」クラスも、「input.hpp」に含まれています。
標準のファンクタは、標準入力から文字を受け取りますが、キャラクター型ポインター
を定義する事ができ、「sscanf」のように機能します。
組み込みマイコンで使う事を考えて、エラーに関する処理では、「例外」を送出しません。
入力変換時に起こったエラーは、エラー種別として取得する事ができます。
- 一般的に、例外を使うと多くのメモリを消費します。
- 例外を使った場合、エラーが発生して、正しい受取先が無い場合、致命的な問題を引き起こします。
- 複数の変換で、エラーが同時に発生すると、最後のエラーが残ります。
- 複数の分離キャラクターを扱える正規表現があります。
- %b ---> 2進の数値
- %o ---> 8進の数値
- %d ---> 10進の数値
- %u ---> 符号無し10進
- %x ---> 16進の数値
- %f ---> 浮動小数点数(float、double)
- %c ---> 1文字のキャラクター
- %a ---> 自動、2進(bnnn)、8進(onnn)、10進、16進(xnnn)、を判別
※ %c は、半角文字のみ対応
- %、[、]、などの特殊文字を、分離キャラクターとして使う場合は、「バックスラッシュ」を使う。
使い方
input.hpp をインクルードします。
#include "input.hpp"
※エクスポーネント算術に「std::pow」関数を利用する為、算術ライブラリのリンクが必要です。
※全ての機能を使うのに必要なヘッダーは「input.hpp」のみです。
名前空間は「utils」です。
サンプル
- 標準入力から、変数「a」に10進数を受け取ります。
※標準入力から受け取る場合、入力の終端は、'\n'(改行)とします。
int a;
utils::input("%d") % a;
- 以下のように変換ステートを受け取る事が出来るので、変換に失敗した場合を検出できます。
int a;
if((utils::input("%d") % a).state()) {
// OK
} else {
// NG
}
- 受け取り時、どんなエラーがあったのかを判別したい場合、以下のようにエラー種別を取得
する事ができます。
int a;
auto err = (utils::input("%d") % a).get_error();
if(err == utils::input::error::none) {
// OK
} else {
// NG: 「err」種別により、相当する処理
}
- エラー種別は enum class で定義されます。
none, ///< エラー無し
cha_sets, ///< 文字セットの不一致
partition, ///< 分離キャラクターの不一致
input_type, ///< 無効な入力タイプ
not_integer, ///< 整数型の不一致
different_sign, ///< 符号の不一致
sign_type, ///< 符号無し整数にマイナス符号
not_float, ///< 浮動小数点型の不一致
terminate, ///< 終端文字の不一致
overflow, ///< オーバーフロー
- 文字列から受け取る場合(sscanf 相当)は、以下のようにします。
※第二引数が省略された場合、上の例のように、標準入力となります。
int a;
static const char* inp = { "1234" };
utils::input("%d", inp) % a;
- 特殊文字「%, [, ]」を分離キャラクターとして使う場合、「\」(バックスラッシュ)で除外します。 ※「\」を文字列に1文字含める場合、「\」とします。
static const char* inp = { "123%456[789]5678" };
int a[4] = { -1 };
auto err = (input("%d\\%%d\\[%d\\]%d", inp) % a[0] % a[1] % a[2] % a[3]).get_error();
std::cout << "Test23, Special character separator as '" << inp << "': "
<< a[0] << ", " << a[1] << ", " << a[2] << ", " << a[3];
if(err == input::error::none) {
}
拡張機能、分離キャラクタ
数値ブロックを分離するキャラクターには、数値(10進の場合 0 から 9)以外を指定が出来ます。
さらに。「[」、「]」で囲まれた任意のキャラクターを指定出来ます。
以下のサンプルでは、分離キャラクターとして、「 」(スペース)、「,」のどちらかを指定出来ます。
int a = 0;
uint32_t b = 0;
int c = 0;
static const char* inp = { "-99 100,200" };
auto n = (input("%d[, ]%x[ ,]%d", inp) % a % b % c).num();
std::cout << "Test05, multi scan for integer: " << a << ", " << b << ", " << c;
if(n == 3 && a == -99 && b == 0x100 && c == 200) {
std::cout << " Pass." << std::endl;
++pass;
} else {
std::cout << " Scan NG!" << std::endl;
}
拡張機能、「%a」オート
接頭子として、「b,0b」二進数、「o,0o」八進数、「x,0x」16進数、「なし」10進数、を受け付ける機能。
※整数のみで、浮動小数点は受け取れません。
※先頭の「0」は省略可能
static const char* inp = { "100 0x9a 0b1101 0o775" };
int a[4] = { -1 };
auto n = (input("%a %a %a %a", inp) % a[0] % a[1] % a[2] % a[3]).num();
std::cout << "Test10, multi scan for 'auto': "
<< a[0] << ", " << a[1] << ", " << a[2] << ", " << a[3] << " (" << n << ")";
if(n == 4 && a[0] == 100 && a[1] == 0x9a && a[2] == 0b1101 && a[3] == 0775) {
std::cout << " Pass." << std::endl;
++pass;
} else {
std::cout << " Scan NG!" << std::endl;
}
変換エラーが発生した場合:
- 変換エラーが発生した場合、参照へは、結果を返さず、破棄されます。
int a = -1;
input("%d", "1O5") % a;
上記のように、変換に失敗した(文字の O が含まれる)場合は、初期化の値「-1」が保持されます。
オーバーフローエラーの挙動:
- 浮動小数点の小数点以下は、桁あふれ以降の数値は無視され、オーバーフローエラーは返しません。
- 実数部では、「utils::input::error::overflow」がセットされます。
※オーバーフローエラーの場合、オペレーターで指定された参照へは、オーバーフローする前の値を返します。
プロジェクト(全体テスト)
input クラスは、全体テストと共に提供されます。
make
で全体テストがコンパイルされます。
make run
全体テストが走り、全てのテストが通過すると、正常終了となります。
※テストに失敗すると、-1 を返します。
License
MIT
Author And Source
この問題について(組み込みでも使える、scanf に代わる C++ input.hpp), 我々は、より多くの情報をここで見つけました https://qiita.com/hira_kuni_45/items/415ccccd74713e6d12cf著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .