読者です 読者をやめる 読者になる 読者になる

最近書いたマクロ(構造体をtupleに変換したい)

C++

これはC++ Advent Calendar 2016の9日目の記事です

はじまり

こんな感じのコードを書いていた

struct try_focus {
    Player p; Card c;
};
string to_json(try_play const &a){
    return make_json("try_play", a.p, a.c);
}
struct try_exc_hand {
    Player p; Card c1;Card c2;
};
string to_json(try_exc_hand const &a){
    return make_json("try_exc_hand", a.p, a.c1, a.c2);
}
struct nodata{};
string to_json(try_exc_hand const &){
    return make_json("nodata");
}
/*以下略*/
struct Key{
    Player p; Card c;
    bool operator<(Key const &rhs){return std::tie(p,c) < std::tie(rhs.p,rhs.c);}
};
struct Key{
    string s;rect r;
    bool operator<(Key const &rhs){return std::tie(s,r) < std::tie(rhs.s,rhs.r);}
};
/*以下略*/
struct pos{
    int p1,p2,p3;
    bool operator==(Key const &rhs)const{return std::tie(p1,p2) == std::tie(rhs.p1,rhs.p2);}
};
/*後で追加したp3を比較要素に追加し忘れた*/

あ!つらい!!
我に二度同じ文言書かすものみな死に絶えればいいのに!!!

はじまり2

そんな分けでいい感じのマクロないかな、とboost内をあさっていたところ BOOST_FUSION_DEFINE_STRUCT_INLINE なる顧客が本当に必要だったものが!
さっそく使おう
(ファンが不吉な音を立て始める)
(IDE候補検索が遅れる、出ない)
(IDEが長考する)
(締め切りがヤバイ)

もうだめだ。自分で書こう

つまりこうなりたいという気持ちがあります

hashとかoperator<とか決まりきった文句はいい感じに定義されててほしいし
to_stringとかto_jsonはちょっと頑張ったら定義できるようにしたい
それらは構造体をtupleに変換できれば解決します

書いた

#define LUCKY_STRUCT_WHITE 

#define LUCKY_STRUCT8_IMPL(name,X1,X2,X3,X4,X5,X6,X7,X8,...) \
struct name##define{ \
    template<class LUCKY_STRUCT_WHITE##X1,class LUCKY_STRUCT_WHITE##X2,class LUCKY_STRUCT_WHITE##X3,class LUCKY_STRUCT_WHITE##X4,class LUCKY_STRUCT_WHITE##X5,class LUCKY_STRUCT_WHITE##X6,class LUCKY_STRUCT_WHITE##X7,class LUCKY_STRUCT_WHITE##X8, class...>struct impl{ \
        LUCKY_STRUCT_WHITE##X1 X1;LUCKY_STRUCT_WHITE##X2 X2;LUCKY_STRUCT_WHITE##X3 X3;LUCKY_STRUCT_WHITE##X4 X4;LUCKY_STRUCT_WHITE##X5 X5;LUCKY_STRUCT_WHITE##X6 X6;LUCKY_STRUCT_WHITE##X7 X7;LUCKY_STRUCT_WHITE##X8 X8; \
        auto operator|(::lucky_struct::to_tie_t){return std::tuple_cat(std::tie(X1),std::tie(X2),std::tie(X3),std::tie(X4),std::tie(X5),std::tie(X6),std::tie(X7),std::tie(X8));} \
        auto operator|(::lucky_struct::get_name_t)const{return #name;} \
    };\
    template<class...T>using type = impl<T... ,void,void,void,void,void,void,void,void>;\
};\
using name=typename name##define::type
#define LUCKY_STRUCT8(name,...) LUCKY_STRUCT8_IMPL(name,__VA_ARGS__, , , , , , , )

8要素までの構造体を定義するマクロのコア部分
tupleに結び付けられるし余計な要素が入ることもない
そしてそこまで重くない(当社比)
[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ


コードの動き

8要素までだとコードの動きが追いづらいので2要素かつ機能を一部削ったマクロで考える

#define LUCKY_STRUCT2_IMPL(name,X1,X2,...) \
struct name##define{ \
    template<class LUCKY_STRUCT_WHITE##X1,class LUCKY_STRUCT_WHITE##X2, class...>struct impl{ \
        LUCKY_STRUCT_WHITE##X1 X1;LUCKY_STRUCT_WHITE##X2 X2; \
        auto operator|(::lucky_struct::to_tie_t){return std::tuple_cat(std::tie(X1),std::tie(X2));} \
    };\
    template<class...T>using type = impl<T... ,void,void>;\
};\
using name=typename name##define::type
#define LUCKY_STRUCT2(name,...) LUCKY_STRUCT2_IMPL(name,__VA_ARGS__, )
  LUCKY_STRUCT2(Alpha,val)<int>;

このように定義するとマクロはまず以下のように展開される

  LUCKY_STRUCT2_IMPL(Alpha,val,)<int>

引数はname=Alpha,X1=val, X2= となりLUCKY_STRUCT2_IMPLが展開される

struct Alphadefine{
    template<class LUCKY_STRUCT_WHITEval,class LUCKY_STRUCT_WHITE, class...>struct impl{
        LUCKY_STRUCT_WHITEval val;LUCKY_STRUCT_WHITE ;  //1
        auto operator|(::lucky_struct::to_tie_t){return std::tuple_cat(std::tie(val),std::tie());} //2
    };
    template<class...T>using type = impl<T... ,void,void>;
};
using Alpha=typename Alphadefine::type<int>

そしてLUCKY_STRUCT_WHITE が に置換される

struct Alphadefine{
    template<class LUCKY_STRUCT_WHITEval,class , class...>struct impl{
        LUCKY_STRUCT_WHITEval val; ;   //1
        auto operator|(::lucky_struct::to_tie_t){return std::tuple_cat(std::tie(val),std::tie());}  //2
    };
    template<class...T>using type = impl<T... ,void,void>;
};
using Alpha=typename Alphadefine::type<int>

//1の部分
メンバ変数の定義。
X2は空だったので変数名は定義されず、LUCKY_STRUCT_WHITE##X2もLUCKY_STRUCT_WHITEになり空に置換され、第2変数を構成する要素は構造体から消えた
X1は名前を持っていたのでLUCKY_STRUCT_WHITEval val;という定義になる。LUCKY_STRUCT_WHITEval はテンプレート引数として使われる

//2の部分
X1は名前を持っていたのでstd::tie(val)に、X2は空だったのでstd::tie()になった。これらはtuple_catで結合され最終的に1要素のtieが出力される

こうして2要素まで定義できるマクロで1要素の変数を持つ構造体が定義できた
もちろん余計なストレージを所有することなどない
これを8要素に拡張すれば上で定義したものになる。もっと必要なら16や32など拡張してもいいだろう(そんな必要になることはないと思うが)

あとは適当に好きな関数を定義すれば比較も出力も思うが儘だろう
とりあえず比較を実装した
[Wandbox]三へ( へ՞ਊ ՞)へ ハッハッ









つづく

txt-txt.hateblo.jp