構造体のメンバ変数を定数の値で取得したい その2

前回の続きです。
コメントにも頂きましたが、std::mt の年と月のデータは特殊で、そのまま渡しても正しい値として認識がされません。
ですので、変数に渡す場合は、差分を計算して、代入しなければなりません。

std::tm st;

st.tm_year = 2010 - 1900;   // 1900年 が 0 なので
st.tm_mon  = 12 - 1;        // 同じく 1月 が 0

しかし、代入するたびに差分を計算するのも手間なのでちょっと改造。

#include <iostream>
#include <ctime>
#include <boost/assert.hpp>

#include <boost/multi_index/member.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/at.hpp>


namespace multi_index = boost::multi_index;
namespace mpl = boost::mpl;

typedef mpl::int_<0> year;
typedef mpl::int_<1> month;
typedef mpl::int_<2> day;
typedef mpl::int_<3> hour;
typedef mpl::int_<4> minute;
typedef mpl::int_<5> second;

namespace detail{

typedef mpl::vector<
    multi_index::member<std::tm, int, &std::tm::tm_year>,
    multi_index::member<std::tm, int, &std::tm::tm_mon >,
    multi_index::member<std::tm, int, &std::tm::tm_mday>,
    multi_index::member<std::tm, int, &std::tm::tm_hour>,
    multi_index::member<std::tm, int, &std::tm::tm_min >,
    multi_index::member<std::tm, int, &std::tm::tm_sec >
>::type tm_members_type;

template<typename index>
struct holder{
    typedef index index_type;

    explicit holder(std::tm& st_) : st(st_){}

    operator int() const{
        return get(index_type());
    }
    holder& operator =(int const& rhs){
        set(index_type(), rhs);
        return *this;
    }
private:
    std::tm&    st;

    template<typename index>
    void set(index&, int const& value){
        mpl::at<detail::tm_members_type, index>::type()(st) = value;
    }
    template<typename index>
    int const& get(index&) const{
        return mpl::at<detail::tm_members_type, index>::type()(st);
    }

    // 差分が必要な値のオーバーロード
    void set(year&, int const& value){
        mpl::at<detail::tm_members_type, index>::type()(st) = value - 1900;
    }
    int get(year&) const{
        return mpl::at<detail::tm_members_type, index>::type()(st) + 1900;
    }
    void set(month&, int const& value){
        mpl::at<detail::tm_members_type, index>::type()(st) = value - 1;
    }
    int get(month&) const{
        return mpl::at<detail::tm_members_type, index>::type()(st) + 1;
    }
};

} // namespace detail

template<typename index>
detail::holder<index>
tm_cast(std::tm& st){
    return detail::holder<index>(st);
}

int
main(){
    std::tm st;
    
    tm_cast<year>  (st) = 2010;
    tm_cast<month> (st) = 12;
    tm_cast<day>   (st) = 25;
    tm_cast<hour>  (st) = 9;
    tm_cast<minute>(st) = 12;
    tm_cast<second>(st) = 49;
    
    // tm_cast<year>(st) は、tm_year + 1900 の値が返ってくるので不正
//  BOOST_ASSERT(( st.tm_year == tm_cast<year>(st) ));
    BOOST_ASSERT( st.tm_year == (2010 - 1900) );
    BOOST_ASSERT( st.tm_mon  == (12 - 1) );
    BOOST_ASSERT( st.tm_sec == 49 );

    std::cout << tm_cast<year>  (st) << "-"
              << tm_cast<month> (st) << "-"
              << tm_cast<day>   (st) << "-"
              << tm_cast<hour>  (st) << "-"
              << tm_cast<minute>(st) << "-"
              << tm_cast<second>(st) << std::endl;
            
    return 0;
}

std::mt を detail::holder でラップして、各値の設定と取得が出来るようにしました。
メンバ変数のポインタが取得できないこと以外は、前回と使い方は同じです。
今回は、2つしかなかったので、オーバーロードで処理の分岐をさせましたが、将来的に他の値でも差分が必要になってくるようならば、まとめて差分の値を定義しておくのもいいかもしれない。

std::tm に限らず、人間が扱うデータと、実際に処理するデータで値が違うことはよくあるので、もうちょっと汎用的に使えるようにしたい。
0から始まるのと1から始まるのでも全然違いますからねぇー。