Hatena::ブログ(Diary)

Life like a clown このページをアンテナに追加 RSSフィード Twitter

2010-05-16

既存のソースコードの読み方

「既存のソースコードのうまい読み方(特に,ある程度歴史のあるソースコード)とは,どのような方法かな?」とたまに考えます.例えば,以下のコードは Boost 1.20.0 の lexical_cast のコード*1です.

namespace boost {
    
    ...
    
    template<typename Target, typename Source>
    Target lexical_cast(Source arg)
    {
# ifndef BOOST_NO_STRINGSTREAM
        std::stringstream interpreter;
# else
        std::strstream interpreter; // for out-of-the-box g++ 2.95.2
# endif
        Target result;

        if(!(interpreter << arg) || !(interpreter >> result) ||
           !(interpreter >> std::ws).eof())
            throw bad_lexical_cast();

        return result;
    }
}

この頃は std::strstream から std::stringstream への移行期であったのでその部分のコードが若干見難くなっていますが,lexical_cast の原理を理解するには分かりやすいコードです.

これに対して,以下のコードは最新のバージョンである Boost 1.43.0 の lexical_cast のコードです.・・・と書こうとしたのですが,長すぎたので lexical_cast の関数部分だけを載せます.

namespace boost {
    
    ...
    
    #ifndef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION

    // call-by-const reference version
    
    ...
    
    template<typename Target, typename Source>
    inline Target lexical_cast(const Source &arg)
    {
        typedef typename detail::array_to_pointer_decay<Source>::type src;

        typedef typename detail::widest_char<
            typename detail::stream_char<Target>::type
          , typename detail::stream_char<src>::type
          >::type char_type;

        typedef detail::lcast_src_length<char_type, src> lcast_src_length;
        std::size_t const src_len = lcast_src_length::value;
        char_type buf[src_len + 1];
        lcast_src_length::check_coverage();
        return detail::lexical_cast<Target, src, !src_len>(arg, buf, src_len);
    }

    #else

    // call-by-value fallback version (deprecated)

    template<typename Target, typename Source>
    Target lexical_cast(Source arg)
    {
        typedef typename detail::widest_char< 
            BOOST_DEDUCED_TYPENAME detail::stream_char<Target>::type 
          , BOOST_DEDUCED_TYPENAME detail::stream_char<Source>::type 
        >::type char_type; 

        typedef std::char_traits<char_type> traits;
        detail::lexical_stream<Target, Source, traits> interpreter;
        Target result;

        if(!(interpreter << arg && interpreter >> result))
#ifndef BOOST_NO_TYPEID
            throw_exception(bad_lexical_cast(typeid(Source), typeid(Target)));
#else
            throw_exception(bad_lexical_cast());
#endif
        return result;
    }

    #endif
}

このようにかなり複雑になっています.ちなみに,Boost 1.20.0 の lexical_cast.hpp のファイルサイズは 2.02KB で Boost 1.43.0 のそれは 38.6KB でした.様々な要件に対処した結果,ファイルサイズは実に 20 倍近く大きくなっています.lexical_cast の場合は,関数の定義自体はそれなりに原型が残っているので detail::lexical_stream が std::stringstream みたいな役割を果たすストリームと言う事が分かれば基本的な原理を理解するのはまだ楽なのですが,それでもいきなりこのソースコードを目の当たりにすると立ちすくんでしまいそうです.

ソースコードは,何らかのエラーへの対処やパフォーマンスの改善などの目的でだんだんと複雑になっていきます.そのため,動作原理を理解すると言う意味では,まず始めに初期の頃にかかれたコードを読み,その後に修正の履歴を眺めていくと言う形が良いのかなぁと思ったりもするのですが,どこまで遡れば良いのかと言うのも一概には言えないところもあり,なかなか難しいところです.

書籍やドキュメントと言う形で動作原理が分かりやすく書かれてある場合は良いのですが,そうではない場合にどうするかと言うのは難しいなぁと感じました.

*1:bad_lexical_cast の部分は省略.