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

たまに「既存のソースコード(特に、ある程度歴史のあるソースコード)を上手に効率よく読んでいくにはどうしたら良いか?」と言う事を考えます。例えば、以下のコードは Boost 1.20.0 の lexical_cast のコードです。

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 のような役割を果たすストリームと言う事が分かれば基本的な原理を理解するのはまだ楽なのですが、それでもいきなりこのソースコードを目の当たりにすると立ち竦んでしまいそうです。

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