C++ では、printf を使わない文化がある。
これは、スタックベースの可変引数では、安全性が確保出来ない事に由来する。
その為、C++ では、printf が便利な事は判っていても、それから離れて、std::cout
関係のクラスを実装する事となったと思う。
ただ、典型的な C のプログラマーは、C++ 上でのプログラムでも、「std::cout」
が使いにくいと言い、printf を使い続ける輩が後を絶たない。
「std::cout」も馴れてしまえば、そうそう使いにくいものでも無いと思うが、それ
でも、printf にある、柔軟性やコンビニエンス的な使いかってには、敵わない。
boost では、printf の使いやすさと安全性を網羅した format クラスが作られた。
これにより、C++ でも、printf と遜色無い、利便性を手に入れたと思える。
小規模な組み込みマイコンでも、やはり、安全性が無い printf は使いたくは無い。
※printf のフォーマットと引数の不整合は、最悪の場合、プログラムがクラッシュ
する、ある程度は、コンパイラが不整合を検証しているが、完全には出来ないので、
多くの組み込みメーカーではデバッグ以外では使わない事が普通で、リリースビルド
では、「#define」を使って「printf」を消している。
boost::format クラスは、iostream に依存していて、非常に多くのリソースを消費
してしまう為、限られたメモリーしか無いようなマイコンでは、使う事が出来ない。
また、「float、double」を使わない場合に余分なコードを節約したい等、カスタム
したい場合もある。
組み込みでも、C 言語ベースのシステムでは、printf も多くのメモリーを消費する
為、多くの人が、廉価版の printf を実装して使う事が多いが、それに習って、C++
なので、boost::format の廉価版を実装してみた。
C++ では、演算子をオーバーロードして、全く別の機能として使う事が出来る。
これを利用して、「%」記号(通常は、整数の剰余)をオーバーロードして、引数を
受け取るようにしている。
不整合が起こった場合は、「例外」をスローするのが普通であるが、それは、別の問
題を含んでしまう為、エラーステータスによる処理を行なっている。
独自 format なので、拡張して、固定小数点の表示、二進数表示などを取り入れている。
又、内部から、sprintf を呼ぶような事も行なわない、純粋な物を実装したい。
以前は、整数表示や文字列表示などの機能しか実装していなかったが、float の表示
が必要な事は結構ある。
そこで、IEEE754 の表示が出来るように拡張してみたのだが、仕様が複雑で、難解
な部分が多く、現状では、全ての仕様を満足するまでには完成度は高く無いものの、
ある程度は、正しく表示できるのようになった。
以下のような方針で実装してみた。
・内部で float の計算を行なわない。(整数のみの計算で行なう)
・なるべくメモリーの消費を抑える。
・なるべく printf の仕様に準拠する。
printf では、float、double の区別は無く、基本的に、float は double にキャス
トされて内部処理される。
newlib の該当する部分を読んでみたが、あまりに複雑で、部分を抜き出して簡単に
使えそうも無い事から、自分で考えながら実装してみた。
その為、無駄な部分や間違いがあるかもしれない。
固定小数点表示:
組み込みでは、A/D 変換した値や温度など表示する場合など、シフトしたり掛けたり
割ったりして、実数部と小数部を分けて整数で表示する事が良くある。
そこで、もっと簡単に扱えるように固定小数点表示をサポートしている。
int32_t val = 4096;
utils::format("FixedPoint: %5.3:12y\n") % val;
val = 6372;
utils::format("FixedPoint: %5.3:12y\n") % val;
utils::format("FixedPoint: %5.2:12y\n") % val;
FixedPoint: 1.000
FixedPoint: 1.556
FixedPoint: 1.56
・上の例では、全体5桁(小数点含む)、小数部3桁、小数点以下12ビットとして
表示する。
・内部では、小数部+1桁目を四捨五入して丸めて表示する。(この仕様は、printf
の IEEE754 表示に準拠している)
IEEE754 サポートが必要無い場合は、ソースの頭で、以下のようにコメントアウトする
事で大幅にメモリーを節約できる。(その場合でも、固定小数点表示はサポートされる)
// #define WITH_FLOAT_FORMAT
※現状では、「double」の80ビットフォーマットをサポートしていない。
※R8C、RL78、RX マイコンで利用でき、もちろん、他のプラットホームでもコンパイル
できれば、利用可能と思う。
RL78/common/format.hpp
追記修正:
printf の桁数指定を勘違いしていた為、改めて、仕様を読み直し、printf に準拠する
ように修正を加えた。