読者です 読者をやめる 読者になる 読者になる

エラー時の動作を切り替えられるlexical_cast

C++

先日、boost::lexical_castの議論Twitterでありまして
その中で「lexical_castは変換失敗時に例外を投げるが、boost::optional版も用意すべき」という意見がありました。


下位互換性を保ったまま、boost::lexical_castにboost::optional版を追加するには
lexical_cast_optionalのような別名の関数を用意する、という選択肢がありますが
C++0xで導入される関数テンプレートのデフォルトテンプレート引数を使用すれば
これをオーバーロードで解決することができます。


以下は、関数テンプレートのデフォルトテンプレート引数に対応しているGCC 4.4を使用しています。

#include <iostream>
#include <sstream>
#include <string>
#include <typeinfo>
#include <boost/optional.hpp>

namespace tag {
    struct exception;
    struct optional;
} // namespace tag

struct bad_lexical_cast {};

template <class To, class From>
inline bool lexical_convert(To& result, From x)
{
    std::stringstream ss;
    return ss << x && ss >> result;
}

template <class To, class From, class ErrorTag>
struct lexical_cast_t {
    typedef To result_type;

    static To call(From x)
    {
        To result;

        if (!lexical_convert(result, x))
            throw bad_lexical_cast();

        return result;
    }
};

template <class To, class From>
struct lexical_cast_t<To, From, tag::optional> {
    typedef boost::optional<To> result_type;

    static boost::optional<To> call(From x)
    {
        boost::optional<To> result = To();

        if (!lexical_convert(*result, x))
            return boost::none;

        return result;
    }
};

template <class To, class ErrorTag = tag::exception, class From>
inline typename lexical_cast_t<To, From, ErrorTag>::result_type
    lexical_cast(From x)
{
    return lexical_cast_t<To, From, ErrorTag>::call(x);
}


int main()
{
    try {
        int n1 = lexical_cast<int, tag::exception>("123");
        std::cout << n1 << std::endl;

        int n2 = lexical_cast<int>("xyz");
        static_cast<void>(n2); // no use
    }
    catch (bad_lexical_cast&) {
        std::cout << "bad_lexical_cast" << std::endl;
    }

    boost::optional<int> n1 = lexical_cast<int, tag::optional>("456");
    std::cout << n1.get() << std::endl;

    if (boost::optional<int> n2 = lexical_cast<int, tag::optional>("xyz")) {
        std::cout << n2.get() << std::endl;
    }
    else {
        std::cout << "lexical_cast error" << std::endl;
    }
}
123
bad_lexical_cast
456
lexical_cast error