例えば、int
型のデータをstring
型で表現させるときや、逆にstring
型のインスタンスに格納された数値表現をint
型に変換するなど、値を文字列の表現に変換しなければならないことがしばしばある。これらは、画面上の表示と設定ファイルの関係のように、プログラムの内部構造を外部に出力する際に良く用いられる手法である。
C/C++標準ライブラリは、先に述べた変換機能の数々を提供するが、それらは使いやすさや拡張性、安全性の面においてまちまちである。
例として、atoi
に代表されるC標準関数における多くの制限について述べてみよう :
int
, long
, double
)のみである。
strtol
に代表される標準C関数では、いくつかの基本的な制限が存在するものの、変換に関する素晴らしい制御方法を提供してくれる。しかし、普通、そのような制御方法は必要ではないし、使われることもない。scanf
とその関連する関数では、さらに優れた制御方法を提供してくれるものの、安全性と使いやすさにおいて優秀とは言い難い。
C++標準ライブラリは、ここで問題になっている種類のin-core 整形のためにstringstream を提供している。これは、文字列による、任意の型と入出力との間の変換と整形に関して幅広く管理する。しかし単純な変換に関して言えば、stringstream を直接利用するのは不格好(余計なローカル変数を取る必要があり、中置記法の利便性を失う)であるか、或いは不明瞭になりうる(stringstream オブジェクトが式の中で一時的オブジェクトとして作成される場合)。これは多くの面で、包括的な概念及びテキスト表現の管理能力を提供するが、これらは比較的高水準なので、単純な変換のためにも極端に多くのことを巻き込まなければならない。
lexical_cast
テンプレート関数はテキストで表現可能な任意の型同士の変換を便利で一貫性のある形、簡単に言えば式レベルでの便利な変換を提供する。
精度や書式においてlexical_cast
が標準で行うより柔軟な操作を必要とするとき、stringstream
の使用を推奨する。また、数値型間の変換を行う場合、numeric_cast
の方が適している。
文字列ベースの表現に関する問題点等に関する議論を扱ったものとして、Herb Sutterの記事 The String Formatters of Manor Farmを紹介しておこう。これには、stringstream
やlexical_cast
等の比較も含まれている。
以下のサンプルでは文字列の中に数値を埋め込んでいる。int main(int argc, char * argv[]) { using boost::lexical_cast; using boost::bad_lexical_cast; std::vector<short> args; while(*++argv) { try { args.push_back(lexical_cast<short>(*argv)); } catch(bad_lexical_cast &) { args.push_back(0); } } ... }
void log_message(const std::string &); void log_errno(int yoko) { log_message("Error " + boost::lexical_cast<std::string>(yoko) + ": " + strerror(yoko)); }
"boost/lexical_cast.hpp"
テストコード:namespace boost { class bad_lexical_cast; template<typename Target, typename Source> Target lexical_cast(Source arg); }
"lexical_cast_test.cpp"
lexical_cast
引数として受け取ったtemplate<typename Target, typename Source> Target lexical_cast(Source arg);
arg
をstd::stringstream
に流し込み、Target
のデータに変換して返す。もし変換が失敗した場合、例外bad_lexical_cast
が発生する。Source
はOutputStreamable(streamに出力可能)でなければならない。std::ostream
のオブジェクトを左辺に取り、引数の型(Source
)のインスタンスを右辺に取るoperator<<
が定義されていなければならない。
Source
とTarget
はCopyConstructible(コピーコンストラクト可能)でなければならない。[20.1.3]
Target
はInputStreamable(streamから入力可能)でなければならない。std::istream
のオブジェクトを左辺に取り、戻り値の型(Target
)のインスタンスを右辺に取るoperator>>
が定義されていなければならない。
Target
はDefaultConstructible(デフォルトコンストラクト可能)でなければならない。Target
はAssignableでなければならない。[23.1]
streamにおけるベースの文字型は、特にワイド文字を利用した変換を必要としない場合、char
型を利用し、そうで無ければ、wchar_t
型を利用する。ベースにワイド文字を必要とするのは、wchar_t
、wchar_t *
、std::wstring
である。
より高度な変換を必要とする場合、std::stringstream
およびstd::wstringstream
を利用することをお勧めする。streamの機能の必要のない変換が要求されているのならば、lexical_cast
を利用することは適さない。そのような特殊なケースための準備をlexical_cast
は用意してないからである。
bad_lexical_cast
この例外はclass bad_lexical_cast : public std::bad_cast { public: ... // std::exceptionと同様のメンバ関数を持つ };
lexical_cast
が失敗したことを示すために使用される。
lexical_cast
は、浮動小数型の変換のためにstreamのデフォルトの精度を利用していたが、現行版においては、std::numeric_limits
を特殊化している型に関しては、それを元に変換を行うようになった。
lexical_cast
は、任意のワイド文字型の変換をサポートしていなかった。ワイド文字型の完全な言語、ライブラリのサポートがなされているコンパイラにおいては、lexical_cast
はwchar_t
、wchar_t *
、およびstd::wstring
の変換をサポートする。
lexical_cast
は、従来型のストリーム抽出演算子が値を読むために充分であると仮定していた。しかしながら、文字列入出力は、その空白文字が、文字列の内容ではなく、入出力セパレータの役目を演じてしまうという結果、非対称なのである。現在のバージョンではstd::string
とstd::wstring
(サポートされているところでは) でのこの誤りが修正されている。例えば、lexical_cast<std::string>("Hello, World")
は、bad_lexical_cast
例外で失敗することなく、成功する。
lexical_cast
は、ポインタへの危険なもしくは無意味な変換を許していたが、現行版においては、ポインタへの変換は例外bad_lexical_cast
を投げるようになっている:コードlexical_cast<char *>("Goodbye, World")
は未定義の振る舞いをする代わりに、例外を投げる。