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


関連:Shand.DateTime v1.2

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

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

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

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;

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;


template<typename index>
int&
tm_cast(std::tm& st){
    return mpl::at<tm_members_type, index>::type()(st);
}

template<typename index>
const int&
tm_cast(const std::tm& st){
    return mpl::at<tm_members_type, index>::type()(st);
}

struct print{
    print(std::tm & st_) : st(st_){};
    template<typename T>
    void operator ()(const T& rhs) const{
        std::cout << rhs(st) << "-";
    }
private:
    std::tm& st;
};


int
main(){
    std::tm st;
    
    BOOST_ASSERT(( &st.tm_year == &tm_cast<mpl::int_<0> >(st) ));
    BOOST_ASSERT(( &st.tm_hour == &tm_cast<hour>(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;
    
    mpl::for_each<tm_members_type>(print(st));

    return 0;
}


[出力]

2010-12-25-9-12-49-


std::tm の構造体の様に、多くのメンバ変数を持っている場合、for_each 等のループで回したい場合があります。
multi_index::member を使用すれば、メンバ変数へのアドレスを保持することが出来るので、それを利用して、std::tm の変数にアクセスを行っています。

typedef multi_index::member<std::tm, int, &std::tm::tm_year> year_member_type;

std::tm st;
BOOST_ASSERT(( &st.tm_year == &(year_member_type()(st)) ));


あとは mpl::vector で、使用するメンバ変数を列挙してやれば、コンパイル時に定数で変数にアクセすることが出来ます。
やったね!


C 言語的なイメージとしては、関数ポインタの配列を用意しているとかそんな感じです。
実行時の一時変数なんかでアクセスを行いたい場合は、もうちょっと工夫がいるかな?


以上、黒魔術でした。


[boost]
ver 1.45.0