MSGPACK_DEFINEが32個までしか対応していない問題

msgpackについては以下を参照

ludwig125.hatenablog.com

問題

MSGPACK_DEFINEで33個以上の引数を取ることができなかった

msgpackのバージョン

現在のmasterのバージョンは2.1.5

https://github.com/msgpack/msgpack-c/blob/master/CHANGELOG.md

MSGPACK_DEFINEとは

保存したいクラスのメンバ変数を、MSGPACK_DEFINEマクロに入れると、 自動でシリアライズ/デシリアライズできる

https://github.com/msgpack/msgpack-c/blob/master/QUICKSTART-CPP.md#user-defined-classes

You can use serialize/deserializes user-defined classes using MSGPACK_DEFINE macro.

ソースコード

MSGPACK_DEFINEの数が32のファイル

[~/git/work/src/cpp/msgpack/my_sample/msgpack_define_expand] $ cat msgpack_define_before-expand32.cpp
#include <msgpack.hpp>
#include <vector>
#include <string>
#include <iostream>

class myclass {
public:
    std::string var[32];

    MSGPACK_DEFINE(
    var[0], var[1], var[2], var[3], var[4], var[5], var[6], var[7], var[8], var[9], var[10], var[11], var[12], var[13], var[14], var[15], var[16], var[17], var[18], var[19], var[20], var[21], var[22], var[23], var[24], var[25], var[26], var[27], var[28], var[29], var[30], var[31]);
    myclass() {
        var[0] = "0";
        var[1] = "1";
        var[2] = "2";
        var[3] = "3";
        var[4] = "4";
        var[5] = "5";
        var[6] = "6";
        var[7] = "7";
        var[8] = "8";
        var[9] = "9";
        var[10] = "10";
        var[11] = "11";
        var[12] = "12";
        var[13] = "13";
        var[14] = "14";
        var[15] = "15";
        var[16] = "16";
        var[17] = "17";
        var[18] = "18";
        var[19] = "19";
        var[20] = "20";
        var[21] = "21";
        var[22] = "22";
        var[23] = "23";
        var[24] = "24";
        var[25] = "25";
        var[26] = "26";
        var[27] = "27";
        var[28] = "28";
        var[29] = "29";
        var[30] = "30";
        var[31] = "31";
}
};

int main(void) {
        std::vector<myclass> vec;
        // add some elements into vec...
        vec.push_back(myclass());

        // you can serialize myclass directly
        msgpack::sbuffer sbuf;
        msgpack::pack(sbuf, vec);

        msgpack::object_handle oh =
            msgpack::unpack(sbuf.data(), sbuf.size());

        msgpack::object obj = oh.get();

        // you can convert object to myclass directly
        std::vector<myclass> rvec;
        obj.convert(rvec);

        for(std::vector<myclass>::iterator it = rvec.begin(); it != rvec.end(); ++it) {

            for(int i = 0; i < 32; i++) {
                std::cout << (*it).var[i] << std::endl;
            }
        }
}
[~/git/work/src/cpp/msgpack/my_sample/msgpack_define_expand] $ 

コンパイルと実行結果

[~/git/work/src/cpp/msgpack/my_sample/msgpack_define_expand] $ g++ -Ipath_to_msgpack/include msgpack_define_before-expand32.cpp
[~/git/work/src/cpp/msgpack/my_sample/msgpack_define_expand] $ ./a.out 
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[~/git/work/src/cpp/msgpack/my_sample/msgpack_define_expand] $ 

これはできる

MSGPACK_DEFINEの数が33のファイル

上のファイルで33個目を追加するとコンパイルに失敗

コンパイルエラー抜粋

 /usr/local/include/msgpack/v1/adaptor/detail/cpp03_define_array.hpp:4468:171: note:   template argument deduction/substitution failed:
In file included from /usr/local/include/msgpack/adaptor/define.hpp:13:0,
                 from /usr/local/include/msgpack/type.hpp:25,
                 from /usr/local/include/msgpack.hpp:18,
                 from msgpack_define_before-expand33.cpp:1:
msgpack_define_before-expand33.cpp:10:5: note:   candidate expects 32 arguments, 33 provided
     MSGPACK_DEFINE(
     ^
In file included from /usr/local/include/msgpack/v1/adaptor/define.hpp:14:0,
                 from /usr/local/include/msgpack/adaptor/define.hpp:15,
                 from /usr/local/include/msgpack/type.hpp:25,
                 from /usr/local/include/msgpack.hpp:18,
                 from msgpack_define_before-expand33.cpp:1:

MSGPACK_DEFINEの定義

https://github.com/msgpack/msgpack-c/blob/315bbd4b40ba5b21bfaeced0e5b7688d833a5b91/include/msgpack/v1/adaptor/detail/cpp03_define_array.hpp#L4470

return define_array<A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, A30, A31>(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31);
  • MSGPACK_DEFINEで呼んでいる関数には32個までしか対応していない

対応策

考えられる対応策は以下の3つ

  • 案1.c++11にする
  • 案2.MSGPACK_DEFINEを入れ子にする
  • 案3.拡張版のMSGPACK_DEFINEを作成する

以下詳細を説明

案1.c++11にする

c++11なら出来るらしい

msgpackの1.0以上、かつc++11だったら33個以上もできそう https://github.com/msgpack/msgpack-c/issues/11

for c++98, we can't extend it easily, all we can do is just write extra specialization for more args. but for c++11, we can use variadic template to break it, although, there is limit, but it's compiler's business.

これができるならそれで問題ない

c++11でコンパイルが通ることを確認

上でコンパイルエラーになったmsgpack_define_before-expand33.cppをc++11でコンパイル

[~/git/work/src/cpp/msgpack/my_sample/msgpack_define_expand] $ g++ -Ipath_to_msgpack/include -std=c++11 msgpack_define_before-expand33.cpp
[~/git/work/src/cpp/msgpack/my_sample/msgpack_define_expand] $ ./a.out 
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
[~/git/work/src/cpp/msgpack/my_sample/msgpack_define_expand] $ 

案2.MSGPACK_DEFINEを入れ子にする

  • 例えば、stuct Aに33個以上の項目を入れてまとめてMSGPACK_DEFINEをしようとすると失敗する
  • そこで、別にstruct Bを用意して、その中に項目を入れて、MSGPACK_DEFINEし、さらにそのstructBをstructAの一要素とすれば(入れ子にすれば)、それぞれのstructの項目数は32個を超えずに済む
  • 多分これでもできそうだが、入れ子になるのが気持ち悪かったので試してない

案3.拡張版のMSGPACK_DEFINEを作成する

  • msgpack 0.5ではかんたんだったけど、最新版は難しい

手順1.拡張版ファイルの作成

https://github.com/msgpack/msgpack-c/tree/315bbd4b40ba5b21bfaeced0e5b7688d833a5b91/erb/v1

ここのerbのGENERATION_LIMIT = 31を40とかに書き換える

https://github.com/msgpack/msgpack-c/blob/315bbd4b40ba5b21bfaeced0e5b7688d833a5b91/erb/v1/cpp03_define_array.hpp.erb#L24

今回上のソースコードで使っているのはarrayだけなので、arrayとそこでつかっているtupleだけ増やしたファイルを用意すればいい(mapは不要) なので、以下のファイルのLIMITを40とかに書き換える

  • cpp03_msgpack_tuple_decl.hpp.erb
  • cpp03_msgpack_tuple.hpp.erb
  • cpp03_define_array_decl.hpp.erb
  • cpp03_define_array.hpp.erb

あとは、以下のファイルを実行すれば、修正したerbをもとに拡張版のヘッダファイルが自動で作成される

https://github.com/msgpack/msgpack-c/blob/315bbd4b40ba5b21bfaeced0e5b7688d833a5b91/preprocess

rubyが無いと言われたので以下でインストール

http://kwski.net/linux/1223/

# apt-add-repository ppa:brightbox/ruby-ng  // レポジトリの追加
# apt-get update                            // 
# apt-get install ruby2.2                   // ruby(バージョン)のインストール

手順2.作ったファイルでライブラリのファイルを置き換える

これで作ったファイルを実際のmsgpackファイルと置き換える

  • 以下のファイルをつくったファイルと置き換える
    • /usr/local/include/msgpack/v1/adaptor/detail

これで実行すると、拡張版を使って33以上の項目もMSGPACK_DEFINEできる

[~/git/work/src/cpp/msgpack/my_sample/msgpack_define_expand] $ cat msgpack_define_expand33.cpp
#include <msgpack.hpp>
#include <vector>
#include <string>
#include <iostream>

class myclass {
public:
    std::string var[33];

    MSGPACK_DEFINE(
    var[0], var[1], var[2], var[3], var[4], var[5], var[6], var[7], var[8], var[9], var[10], var[11], var[12], var[13], var[14], var[15], var[16], var[17], var[18], var[19], var[20], var[21], var[22], var[23], var[24], var[25], var[26], var[27], var[28], var[29], var[30], var[31], var[32]);
    myclass() {
        var[0] = "0";
        var[1] = "1";
        var[2] = "2";
        var[3] = "3";
        var[4] = "4";
        var[5] = "5";
        var[6] = "6";
        var[7] = "7";
        var[8] = "8";
        var[9] = "9";
        var[10] = "10";
        var[11] = "11";
        var[12] = "12";
        var[13] = "13";
        var[14] = "14";
        var[15] = "15";
        var[16] = "16";
        var[17] = "17";
        var[18] = "18";
        var[19] = "19";
        var[20] = "20";
        var[21] = "21";
        var[22] = "22";
        var[23] = "23";
        var[24] = "24";
        var[25] = "25";
        var[26] = "26";
        var[27] = "27";
        var[28] = "28";
        var[29] = "29";
        var[30] = "30";
        var[31] = "31";
        var[32] = "32";
}
};

int main(void) {
        std::vector<myclass> vec;
        // add some elements into vec...
        vec.push_back(myclass());

        // you can serialize myclass directly
        msgpack::sbuffer sbuf;
        msgpack::pack(sbuf, vec);

        msgpack::object_handle oh =
            msgpack::unpack(sbuf.data(), sbuf.size());

        msgpack::object obj = oh.get();

        // you can convert object to myclass directly
        std::vector<myclass> rvec;
        obj.convert(rvec);

        for(std::vector<myclass>::iterator it = rvec.begin(); it != rvec.end(); ++it) {

            for(int i = 0; i < 33; i++) {
                std::cout << (*it).var[i] << std::endl;
            }
        }
}
[~/git/work/src/cpp/msgpack/my_sample/msgpack_define_expand] $ 

コンパイルと実行

[~/git/work/src/cpp/msgpack/my_sample/msgpack_define_expand] $ g++ -Ipath_to_msgpack/include msgpack_define_expand33.cpp
[~/git/work/src/cpp/msgpack/my_sample/msgpack_define_expand] $ ./a.out 
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32  ← 33個目が使えている!
[~/git/work/src/cpp/msgpack/my_sample/msgpack_define_expand] $