最終更新日時:
が更新

履歴 編集

Header boost/lexical_cast.hpp


動機

例えば、int型のデータをstring型で表現させるときや、逆にstring型のインスタンスに格納された数値表現をint型に変換するなど、値を文字列の表現に変換しなければならないことがしばしばある。これらは、画面上の表示と設定ファイルの関係のように、プログラムの内部構造を外部に出力する際に良く用いられる手法である。

C/C++標準ライブラリは、先に述べた変換機能の数々を提供するが、それらは使いやすさや拡張性、安全性の面においてまちまちである。

例として、atoiに代表されるC標準関数における多くの制限について述べてみよう :

  • 変換は文字列から内部データ型への一方向に限られる。Cライブラリを用いた別の変換方法としては、sprintf関数が存在するが不便で安全性も低い。
  • また、itoa関数のような非標準の機能を用いる方法もあるが、それでは移植性の低下を招いてしまう。
  • サポートする型がプリミティブな数値型の一部(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を紹介しておこう。これには、stringstreamlexical_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);

引数として受け取ったargstd::stringstreamに流し込み、Targetのデータに変換して返す。もし変換が失敗した場合、例外bad_lexical_castが発生する。

引数と戻り値の型の条件:

  • SourceOutputStreamablestreamに出力可能)でなければならない。
    • つまり、std::ostreamのオブジェクトを左辺に取り、引数の型(Source)のインスタンスを右辺に取るoperator<<が定義されていなければならない。
  • SourceTargetCopyConstructible(コピーコンストラクト可能)でなければならない。[20.1.3]
  • TargetInputStreamablestreamから入力可能)でなければならない。
    • つまり、std::istreamのオブジェクトを左辺に取り、戻り値の型(Target)のインスタンスを右辺に取るoperator>>が定義されていなければならない。
  • TargetDefaultConstructible(デフォルトコンストラクト可能)でなければならない。
    • つまり、Targetのオブジェクトのデフォルト初期化が可能でなければならない。[8.5, 20.1.4]
  • TargetAssignableでなければならない。[23.1]

streamにおけるベースの文字型は、特にワイド文字を利用した変換を必要としない場合、char型を利用し、そうで無ければ、wchar_t型を利用する。ベースにワイド文字を必要とするのは、wchar_twchar_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_castwchar_twchar_t *、およびstd::wstringの変換をサポートする。
  • 前バージョンのlexical_castは、従来型のストリーム抽出演算子が値を読むために充分であると仮定していた。しかしながら、文字列入出力は、その空白文字が、文字列の内容ではなく、入出力セパレータの役目を演じてしまうという結果、非対称なのである。現在のバージョンではstd::stringstd::wstring(サポートされているところでは) のでこの誤りが修正されている。例えば、lexical_cast<std::string>("Hello, World") は、bad_lexical_cast例外で失敗することなく、成功する。
  • 前バージョンのlexical_castは、ポインタへの危険なもしくは無意味な変換を許していたが、現行版においては、ポインタへの変換は例外bad_lexical_castを投げるようになっている:コードlexical_cast<char *>("Goodbye, World")は未定義の振る舞いをする代わりに、例外を投げる。

© Copyright Kevlin Henney, 2000–2003