NTPと型のアップキャスト

先日の記事(id:uruwasi:20080220:1203484601)で

“後はboost::is_base_ofを使ってNTP_REDECに入力する型を基底クラスにキャストする実装を書けばいいんだけれど”
型に対するキャストとはどう言う事だ

となじられるのも嫌なので、気力を++して書いてきました。主にtupleからsequenceへの移行、継承に関するものその他を無駄に思いつつ改良。
しかし、無責任にもboost::is_base_ofは使ってません。

簡単な例

#include <iostream>
using std::cout; using std::endl;

NTP(
  struct, test_1,
  ((typename, T1))
  ((typename, T2))
  ((int, V1))
  ((int, V2))
){
  void t1(){ cout << typeid(T1).name() << endl; }
  void t2(){ cout << typeid(T2).name() << endl; }
  void v1(){ cout << V1 << endl; }
  void v2(){ cout << V2 << endl; }
  void f(){ t1(); t2(); v1(); v2(); }
};

NTP(
  struct, test_2,
  ((int, V))
), test_1<char, char, 987, 789>{
  typedef test_1<char, char, 987, 789> base_1;
  void print_value(){ cout << V << endl; }
};

NTP(
  struct, test_3,
  ((typename, T))
), test_2<1024>{
  typedef test_2<1024> base_2;
  void print_typename(){ cout << typeid(T).name() << endl; }
};

int main(){
  typedef test_1<int, short, 123, 321> t1;
  { t1 a; a.f(); cout << endl; }

  typedef NTP_REDEC(test_1, t1)::T1<void>::V2<1>::end t2;
  { t2 a; a.f(); cout << endl; }

  typedef test_3<const void * const &> t3;
  {
    t3 a;
    a.print_typename();
    cout << endl;

    t3::base_1 *ptr1 = &a;
    ptr1->f();
    cout << endl;

    t3::base_2 *ptr2 = &a;
    ptr2->print_value();
    cout << endl;
  }

  // 型の'アップキャスト'
  // t3 は test_1 を継承した test_3 の typedef であって test_1 ではない
  typedef NTP_REDEC(test_1, t3)::T2<char********>::end t4;
  { t4 a; a.f(); cout << endl; }

  return 0;
}

結果

int
short
123
321

void
short
123
1

void const *

char
char
987
789

1024

char
char * * * * * * * *
987
789

実装

#include <boost/preprocessor.hpp>

#define NTP_TUPLE_ARGTYPE(num, i, tuple) \
  BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_TUPLE_ELEM(num, i, tuple))

#define NTP_TUPLE_ARGNAME(num, i, tuple) \
  BOOST_PP_TUPLE_ELEM(2, 1, BOOST_PP_TUPLE_ELEM(num, i, tuple))

// ユーザー定義の引数名
#define NTP_CLASS_WRITEARG_0_I(r, nil, i, tuple) \
  BOOST_PP_COMMA_IF(i) \
  BOOST_PP_TUPLE_ELEM(2, 0, tuple) \
  BOOST_PP_TUPLE_ELEM(2, 1, tuple)

#define NTP_CLASS_WRITEARG_0(seq) \
  BOOST_PP_SEQ_FOR_EACH_I(NTP_CLASS_WRITEARG_0_I, nil, seq)

#define NTP_ARG_WRITEARG_0_I(r, nil, i, tuple) \
  BOOST_PP_COMMA_IF(i) BOOST_PP_TUPLE_ELEM(2, 1, tuple)

#define NTP_ARG_WRITEARG_0(seq) \
  BOOST_PP_SEQ_FOR_EACH_I(NTP_ARG_WRITEARG_0_I, nil, seq)

// 引数名を連番で記述する 1
// targ_n
#define NTP_CLASS_WRITEARG_1_I(r, nil, i, tuple) \
  BOOST_PP_COMMA_IF(i) \
  BOOST_PP_TUPLE_ELEM(2, 0, tuple) \
  BOOST_PP_CAT(targ_, i)

#define NTP_CLASS_WRITEARG_1(seq) \
  BOOST_PP_SEQ_FOR_EACH_I(NTP_CLASS_WRITEARG_1_I, nil, seq)

#define NTP_ARG_WRITEARG_1_I(r, nil, i, tuple) \
  BOOST_PP_COMMA_IF(i) BOOST_PP_CAT(targ_, i)

#define NTP_ARG_WRITEARG_1(seq) \
  BOOST_PP_SEQ_FOR_EACH_I(NTP_ARG_WRITEARG_1_I, nil, seq)

// 引数名を連番で記述する 2
// targ_n_
#define NTP_CLASS_WRITEARG_2_I(r, nil, i, tuple) \
  BOOST_PP_COMMA_IF(i) \
  BOOST_PP_TUPLE_ELEM(2, 0, tuple) \
  BOOST_PP_CAT(BOOST_PP_CAT(targ_, i), _)

#define NTP_CLASS_WRITEARG_2(seq) \
  BOOST_PP_SEQ_FOR_EACH_I(NTP_CLASS_WRITEARG_2_I, nil, seq)

#define NTP_ARG_WRITEARG_2_I(r, nil, i, tuple) \
  BOOST_PP_COMMA_IF(i) BOOST_PP_CAT(BOOST_PP_CAT(targ_, i), _)

#define NTP_ARG_WRITEARG_2(seq) \
  BOOST_PP_SEQ_FOR_EACH_I(NTP_ARG_WRITEARG_2_I, nil, seq)

//
#define NTP_ARGSTRUCT_III() _

#define NTP_ARGSTRUCT_II(i, spi) \
  BOOST_PP_IIF(BOOST_PP_EQUAL(i, spi), NTP_ARGSTRUCT_III, BOOST_PP_EMPTY)()

#define NTP_ARGSTRUCT_I(z, i, spi) \
  BOOST_PP_COMMA_IF(i) \
  BOOST_PP_CAT(BOOST_PP_CAT(targ_, i), NTP_ARGSTRUCT_II(i, spi))

#define NTP_ARGSTRUCT(r, seq, i, tuple) \
  template< \
    BOOST_PP_TUPLE_ELEM(2, 0, tuple) \
    BOOST_PP_CAT(BOOST_PP_CAT(targ_, i), _) \
  > struct BOOST_PP_TUPLE_ELEM(2, 1, tuple) : \
    dg<BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(seq), NTP_ARGSTRUCT_I, i)>{};

#define NTP_THIS_TYPE_HOLDER(name) \
  BOOST_PP_CAT(this_t_holder_, BOOST_PP_CAT(name, __))

#define NTP_THIS_TYPE(name) \
  BOOST_PP_CAT(BOOST_PP_CAT(_, name), _this_t__)

#define NTP_REDECDG(name) \
  BOOST_PP_CAT(BOOST_PP_CAT(_, name), _recdg__)

#define NTP_REDEC__(name) \
  BOOST_PP_CAT(BOOST_PP_CAT(_, name), _redec__)

#define NTP_HEAD(dec, name, seq) \
  template<NTP_CLASS_WRITEARG_0(seq)> dec name; \
  template<NTP_CLASS_WRITEARG_1(seq)> struct NTP_THIS_TYPE_HOLDER(name) \
  { typedef name<NTP_ARG_WRITEARG_1(seq)> NTP_THIS_TYPE(name); }; \
  namespace{ \
    template<NTP_CLASS_WRITEARG_1(seq)> struct NTP_REDECDG(name){ \
      template<NTP_CLASS_WRITEARG_2(seq)> struct holder \
      { typedef name<NTP_ARG_WRITEARG_2(seq)> end; }; \
      template<NTP_CLASS_WRITEARG_2(seq)> struct dg : \
        holder<NTP_ARG_WRITEARG_2(seq)>, \
        NTP_REDECDG(name)<NTP_ARG_WRITEARG_2(seq)>{}; \
      BOOST_PP_SEQ_FOR_EACH_I(NTP_ARGSTRUCT, seq, seq) \
    };\
    template<typename T> struct BOOST_PP_CAT(BOOST_PP_CAT(_, name), _redec__); \
    template<NTP_CLASS_WRITEARG_1(seq)> \
    struct NTP_REDEC__(name)< name<NTP_ARG_WRITEARG_1(seq)> > : \
      BOOST_PP_CAT(BOOST_PP_CAT(_, name), _recdg__)<NTP_ARG_WRITEARG_1(seq)>{}; \
  }

#define NTP_DEC(dec, name, seq) \
  template<NTP_CLASS_WRITEARG_0(seq)> dec name : \
  public NTP_THIS_TYPE_HOLDER(name)<NTP_ARG_WRITEARG_0(seq)>

#define NTP(dec, name, seq) \
  NTP_HEAD(dec, name, seq) NTP_DEC(dec, name, seq)

#define NTP_REDEC(target, type) NTP_REDEC__(target)<type::NTP_THIS_TYPE(target)>

コメントへのレス

boost/parameter/aux_/preprocessor/for_each.hppは後で読みます。保留。

NTPのマクロの中途半端な実装

後はboost::is_base_ofを使ってNTP_REDECに入力する型を基底クラスにキャストする実装を書けばいいんだけれど、下にある通り衝撃的な情報を入手したので書く気力が消滅。

#include <boost/preprocessor.hpp>
#include <boost/type_traits.hpp>
#include <iostream>

#define NTP_TUPLE_ARGTYPE(num, i, tuple) \
  BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_TUPLE_ELEM(num, i, tuple))

#define NTP_TUPLE_ARGNAME(num, i, tuple) \
  BOOST_PP_TUPLE_ELEM(2, 1, BOOST_PP_TUPLE_ELEM(num, i, tuple))

#define NTP_TUPLE2_ARGTYPE(i, tuple2) \
  BOOST_PP_TUPLE_ELEM( \
    2, \
    0, \
    BOOST_PP_TUPLE_ELEM( \
      BOOST_PP_TUPLE_ELEM(2, 0, tuple2), \
      i, \
      BOOST_PP_TUPLE_ELEM(2, 1, tuple2) \
    ) \
  )

#define NTP_TUPLE2_ARGNAME(i, tuple2) \
  BOOST_PP_TUPLE_ELEM( \
    2, \
    1, \
    BOOST_PP_TUPLE_ELEM( \
      BOOST_PP_TUPLE_ELEM(2, 0, tuple2), \
      i, \
      BOOST_PP_TUPLE_ELEM(2, 1, tuple2) \
    ) \
  )

// 引数名をtupleから取ってくる
#define NTP_CLASS_WRITEARG_0_I(z, i, tuple2) \
  NTP_TUPLE2_ARGTYPE(i, tuple2) NTP_TUPLE2_ARGNAME(i, tuple2),

#define NTP_CLASS_WRITEARG_0(num, tuple) \
  BOOST_PP_REPEAT(BOOST_PP_SUB(num, 1), NTP_CLASS_WRITEARG_0_I, (num, tuple)) \
  NTP_TUPLE_ARGTYPE(num, BOOST_PP_SUB(num, 1), tuple) \
  NTP_TUPLE_ARGNAME(num, BOOST_PP_SUB(num, 1), tuple)

#define NTP_ARG_WRITEARG_0_I(z, i, tuple2) \
  NTP_TUPLE2_ARGTYPE(i, tuple2),

#define NTP_ARG_WRITEARG_0(num, tuple) \
  BOOST_PP_REPEAT(BOOST_PP_SUB(num, 1), NTP_ARG_WRITEARG_0_I, (num, tuple)) \
  NTP_TUPLE_ARGNAME(num, BOOST_PP_SUB(num, 1), tuple)

// 引数名を連番で記述する 1
// targ_n
#define NTP_CLASS_WRITEARG_1_I(z, i, tuple2) \
  NTP_TUPLE2_ARGTYPE(i, tuple2) BOOST_PP_CAT(targ_, i),

#define NTP_CLASS_WRITEARG_1(num, tuple) \
  BOOST_PP_REPEAT(BOOST_PP_SUB(num, 1), NTP_CLASS_WRITEARG_1_I, (num, tuple)) \
  NTP_TUPLE_ARGTYPE(num, BOOST_PP_SUB(num, 1), tuple) \
  BOOST_PP_CAT(targ_, BOOST_PP_SUB(num, 1))

#define NTP_ARG_WRITEARG_1_I(z, i, tuple2) \
  BOOST_PP_CAT(targ_, i),

#define NTP_ARG_WRITEARG_1(num, tuple) \
  BOOST_PP_REPEAT(BOOST_PP_SUB(num, 1), NTP_ARG_WRITEARG_1_I, (num, tuple)) \
  BOOST_PP_CAT(targ_, BOOST_PP_SUB(num, 1))

// 引数名を連番で記述する 2
// targ_n_
#define NTP_CLASS_WRITEARG_2_I(z, i, tuple2) \
  NTP_TUPLE2_ARGTYPE(i, tuple2) BOOST_PP_CAT(BOOST_PP_CAT(targ_, i), _),

#define NTP_CLASS_WRITEARG_2(num, tuple) \
  BOOST_PP_REPEAT(BOOST_PP_SUB(num, 1), NTP_CLASS_WRITEARG_2_I, (num, tuple)) \
  BOOST_PP_TUPLE_ELEM(2, 0, BOOST_PP_TUPLE_ELEM(num, BOOST_PP_SUB(num, 1), tuple)) \
  BOOST_PP_CAT(BOOST_PP_CAT(targ_, BOOST_PP_SUB(num, 1)), _)

#define NTP_ARG_WRITEARG_2_I(z, i, tuple2) \
  BOOST_PP_CAT(BOOST_PP_CAT(targ_, i), _),

#define NTP_ARG_WRITEARG_2(num, tuple) \
  BOOST_PP_REPEAT(BOOST_PP_SUB(num, 1), NTP_ARG_WRITEARG_2_I, (num, tuple)) \
  BOOST_PP_CAT(BOOST_PP_CAT(targ_, BOOST_PP_SUB(num, 1)), _)

//
#define NTP_ARGSTRUCT_II() _

#define NTP_ARGSTRUCT_I_II(i, spi) \
  BOOST_PP_IIF(BOOST_PP_EQUAL(i, spi), NTP_ARGSTRUCT_II, BOOST_PP_EMPTY)()

#define NTP_ARGSTRUCT_I_I(i, spi) \
  BOOST_PP_CAT(BOOST_PP_CAT(targ_, i), NTP_ARGSTRUCT_I_II(i, spi))

#define NTP_ARGSTRUCT_I(z, i, spi) \
  NTP_ARGSTRUCT_I_I(i, spi),

#define NTP_ARGSTRUCT(z, i, tuple2) \
  template< \
    NTP_TUPLE2_ARGTYPE(i, tuple2) BOOST_PP_CAT(BOOST_PP_CAT(targ_, i), _) \
  > \
  struct NTP_TUPLE2_ARGNAME(i, tuple2) : \
    dg< \
      BOOST_PP_REPEAT(BOOST_PP_SUB(BOOST_PP_TUPLE_ELEM(2, 0, tuple2), 1), NTP_ARGSTRUCT_I, i) \
      NTP_ARGSTRUCT_I_I(BOOST_PP_SUB(BOOST_PP_TUPLE_ELEM(2, 0, tuple2), 1), i) \
    >{};

// ヘッダの生成
// このマクロでヘッダさえ作れば
// クラス宣言は別の部分で化膿
#define NTP_HEAD(dec, name, num, tuple) \
  template<NTP_CLASS_WRITEARG_0(num, tuple)> dec name; \
  namespace{ \
    template<NTP_CLASS_WRITEARG_1(num, tuple)> struct BOOST_PP_CAT(BOOST_PP_CAT(_, name), _recdg__){ \
      template<NTP_CLASS_WRITEARG_2(num, tuple)> struct holder \
      { typedef name<NTP_ARG_WRITEARG_2(num, tuple)> end; }; \
      template<NTP_CLASS_WRITEARG_2(num, tuple)> struct dg : \
        holder<NTP_ARG_WRITEARG_2(num, tuple)>, \
        BOOST_PP_CAT(BOOST_PP_CAT(_, name), _recdg__)<NTP_ARG_WRITEARG_2(num, tuple)>{}; \
      BOOST_PP_REPEAT(num, NTP_ARGSTRUCT, (num, tuple)) \
    };\
    template<typename T> struct BOOST_PP_CAT(BOOST_PP_CAT(_, name), _redec__); \
    template<NTP_CLASS_WRITEARG_1(num, tuple)> struct BOOST_PP_CAT(BOOST_PP_CAT(_, name), _redec__)< name<NTP_ARG_WRITEARG_1(num, tuple)> > : \
      BOOST_PP_CAT(BOOST_PP_CAT(_, name), _recdg__)<NTP_ARG_WRITEARG_1(num, tuple)>{}; \
  }

#define NTP(dec, name, num, tuple) \
  NTP_HEAD(dec, name, num, tuple) \
  template<NTP_CLASS_WRITEARG_0(num, tuple)> dec name

#define NTP_REDEC(target, type) BOOST_PP_CAT(BOOST_PP_CAT(_, target), _redec__)<type>

NTP(
  struct, test_1, 4,
  (
    (typename, T1),
    (typename, T2),
    (int, V1),
    (int, V2)
  )
){
  void t1(){ std::cout << typeid(T1).name() << std::endl; }
  void t2(){ std::cout << typeid(T2).name() << std::endl; }
  void v1(){ std::cout << V1 << std::endl; }
  void v2(){ std::cout << V2 << std::endl; }
};

NTP(
  struct, test_2, 2,
  (
    (typename, T1),
    (typename, T2)
  )
){
  T1 f1(T1 a){ return a * a; }  
  T2 f2(T2 a){ return a * a + a; }  
};

typedef test_1<void*, void**, 10, 20> tes1;
typedef test_2<int, double> tes2;

NTP(
  struct, test_3, 2,
  (
    (int, V1),
    (int, V2)
  )
) :
  // 他のNTPクラスも継承可能.
  tes1, tes2
{
  void f(){
    std::cout
      << "V1 " << V1 << ", V2 " << V2 << std::endl;

    tes1 *p1 = static_cast<tes1*>(this);
    p1->t1(); p1->t2(); p1->v1(); p1->v2();

    tes2 *p2 = static_cast<tes2*>(this);
    std::cout
      << p2->f1(16) << ", " << p2->f2(32.0) << std::endl;
  }
};

int main(){
  typedef test_1<int, short, 0, 1> type1;
  {
    type1 a;
    std::cout << "----- type1" << std::endl;
    a.t1(); a.t2(); a.v1(); a.v2();
  }

  std::cout << std::endl;

  // NTP_REDECで引数を再設定する.
  typedef NTP_REDEC(test_1, type1)::T1<int*>::T2<short*>::V1<2>::V2<3>::end type2;
  {
    type2 a;
    std::cout << "----- type2" << std::endl;
    a.t1(); a.t2(); a.v1(); a.v2();
  }

  std::cout << std::endl;

  // 継承を行ったクラスも使用可能.
  typedef test_3<64, 128> type3;
  {
    type3 a;
    std::cout << "----- type3" << std::endl;
    a.f();
  }

  std::cout << std::endl;

  typedef NTP_REDEC(test_3, type3)::V1<16>::V2<32>::end type4;
  {
    type4 a;
    std::cout << "----- type4" << std::endl;
    a.f();
  }

  return 0;
}

コメントに対するレス

uskz 2008/02/20 13:49 sequenceを使って

NTP(
 struct, test,
 ((typename, T1))
 ((typename, T2))
 ((int, V1))
)

の方がシンプルかもしれません
(そういえばBoost.PreprocessorにNTPサポートが)

BOOST_PP_TUPLE_ELEMでガリガリ書き終えたあとにそんな情報が・・・。それ以前に既にNTPのサポートがあったなんて・・・。
車輪の再発明はコーディングの練習という言い訳にはなるものの、boost.preprocessor.seqのような便利なものがあったならもっと遅く書き始めていれば良かった・・・。あの時の自分の衝動を呪う。

難しく考え過ぎでした

redecで再設定するクラスターゲットを指定すれば良いだけの話。多分これで継承問題を解決できると思います。思うだけです。知りません。
勢いで書いたので、冗長な部分とかあったら添削とかツッコミとか可。
楽しみに待っててね!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

マクロのインターフェイスとか(予定)

NTP(
  struct, test, 3,
  (
    (typename, T1),
    (typename, T2),
    (int, V1)
  )
)

元となる考え(これをマクロで自動生成する方法を考える)

template<typename T1, typename T2, int V1> struct test;

template<typename targ_0, typename targ_1, int targ_2> struct _test_recdg__
{
  template<typename targ_0_, typename targ_1_, int targ_2_> struct holder{
    typedef test<targ_0_, targ_1_, targ_2_> end;
  };

  template<typename targ_0_, typename targ_1_, int targ_2_> struct dg :
    holder<targ_0_, targ_1_, targ_2_>,
    _test_recdg__<targ_0_, targ_1_, targ_2_>
  {};

  template<typename targ_0_> struct T1 :
    dg<targ_0_, targ_1, targ_2>
  {};

  template<typename targ_1_> struct T2 :
    dg<targ_0, targ_1_, targ_2>
  {};

  template<int targ_2_> struct V1 :
    dg<targ_0, targ_1, targ_2_>
  {};
};

template<typename T> struct _test_redec__;
template<typename targ_0, typename targ_1, int targ_2> struct _test_redec__< test<targ_0, targ_1, targ_2> > :
  _test_recdg__<targ_0, targ_1, targ_2>
{};

template<typename T1, typename T2, int V1> struct test
{
  void t1(){ std::cout << typeid(T1).name() << std::endl; }
  void t2(){ std::cout << typeid(T2).name() << std::endl; }
  void v1(){ std::cout << V1 << std::endl; }
};

#define redec(target, type) _##target##_redec__<type>

int main(){
  typedef test<void, void, 128> type1;
  type1 a;

  std::cout << "----- type1" << std::endl;
  a.t1();
  a.t2();
  a.v1();

  typedef redec(test, type1)::T1<int>::T2<short>::V1<256>::end type2;
  type2 b;

  std::cout << std::endl << "----- type2" << std::endl;
  b.t1();
  b.t2();
  b.v1();

  return 0;
}

思ったよりも苦戦しております

継承問題を解決するNTP生成マクロが難しい。
まず、引数再設定を行うクラスの中に「再設定を明示する何か」(redec)を入れることはボツ。昨日の様に、同じNTP生成マクロで生成したクラスを継承すると基底のものか派生のものかで曖昧になるからですううう。
それでredecを外の空間に追いやってしまう。将来的には

redec<type>::arg1<hogehoge>::arg2<fugafuga>::end;

でどうにかする予定。
そして次に、外にあるredecがどうやって引数名を取得して再設定用の構造体を作るか。これは、マクロの段階でint2typeとか継承とかを使って、裏でredecに渡す方法を考える予定。いや、本当は分からん。だって継承したらそれこそ曖昧になるじゃないか。
『派生クラスにはprivateだけどいつもはpublicなアクセス指定子』みたいなのがほしいいいいいい。なんかこういう事ずっと前にも考えてた様な気がする。多分思いついたのはC++学びたての頃だったな。protectedを見た後、『これとは逆に(上にある様な)指定子もいつかは必要になるかもしれないなフヒヒ』なんて思いを巡らせた青春(にしても悲しい青春だ)。それが現実になるとは。

先日のNTPなクラスを簡単に定義するマクロ(id:uruwasi:20080217:1203225365)を力作業でないもの、例えばBOOST_PP_REPEATなどを使って書こうと思う。近々。
name_rec__::redec内の、再設定するテンプレート引数を受け取り再設定するもの以外の引数を既存のもので維持する構造体が結構ややこしい、というか綺麗に書けなくて泣ける。boost.ppに関する記事。boostをもう1年くらいも前から知っておきながら最近知った。勉強します。
後は、NTP_Nマクロを使って定義したものが更にNTP_Nマクロを使って定義された別のものか既に内部でクラスredecが存在するものを継承すると、トップレベルのクラスの引数だけをredecで変更しようと思ってもredecがどのクラスのものなのかが曖昧になってコンパイルエラーを招いていしまう。そこも何とか直しておこうと。新しいマクロを使って。はい。
しどろもどろなのは何もここに今書いている文だけじゃない、動きが、態度が、全てがおかしい。死にたい。これが噂のテクノストレスというものか。早く寝ないと。自殺自殺自殺