msgpack / msgpack-c

MessagePack implementation for C and C++ / msgpack.org[C/C++]
Other
3.03k stars 883 forks source link

Mixing intrusive and non-intrusive approach in custom adaptor #1010

Closed patrickjane closed 2 years ago

patrickjane commented 2 years ago

Hello,

I have just started working with MsgPack (C++). I have a library which gives me some kind of data structures, where I am unable to use the intrusive approach, so I need to specialize pack (I only need the packing-part).

However, what I would like to achieve, is that the binary format which is created using the specialized pack implementation can be unpacked again using other structs which are using the intrusive approach. But I can't seem to get this to work. Specifically, I don't understand the concept of MSGPACK_DEFINE and MSGPACK_DEFINE_ARRAY / MSGPACK_DEFINE_MAP. I assume that when I use MSGPACK_DEFINE, my custom struckt members are packed as array, so in my non-intrusive counterpart I would need to use pack_array, but it stops working as soon as there are complex members.

Let me clarify with an example:

struct MyType {
 int a;
 int b;
};

And for the non-custom types (supplied by the 3rd party library) I use:

namespace msgpack {
   MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS) {
      namespace adaptor {
         template<>
         struct pack<ThirdPartyType> {
               template <typename Stream>
               packer<Stream>& operator()(msgpack::packer<Stream>& o, ThirdPartyType const& v) const {
                  o.pack_array(2);
                  o.pack_uint16(v.getA());
                  o.pack_uint16(v.getB());

Now when I pack an instance of ThirdPartyType and try to unpack into a MyType, the deserialization works, and members a and b are set correctly.

If I try to use a complex member in MyType, it won't work correctly anymore:

struct MySubType {
 int x;
 int y;

 MSGPACK_DEFINE(x, y)
};

struct MyType {
 int a;
 int b;
 MySubType other;

 MSGPACK_DEFINE(a, b, other)
};

And for the non-custom types (supplied by the 3rd party library) I use:

namespace msgpack {
   MSGPACK_API_VERSION_NAMESPACE(MSGPACK_DEFAULT_API_NS) {
      namespace adaptor {
         template<>
         struct pack<ThirdPartyType> {
               template <typename Stream>
               packer<Stream>& operator()(msgpack::packer<Stream>& o, ThirdPartyType const& v) const {
                  o.pack_array(4);
                  o.pack_uint16(v.getA());
                  o.pack_uint16(v.getB());
                  o.pack_uint16(v.getX());
                  o.pack_uint16(v.getY());

Now I don't get any exception, but x and y are not set in other.

How do I need to pack in ThirdPartyType in order to be able to successfully unpack using the intrusive approach for MyType (and MySubType) ?

redboltz commented 2 years ago

I'm not sure what is the problem point, but I guess that the following information could help you. MySubType is packed as [x,y] and MyType is packed as [a,b,[x,y]], not [a,b,x,y].

patrickjane commented 2 years ago

Well, I tried to give a pretty detailed example and question.

The question is, how do I manually pack ThirdPartyType into [a,b,[x,y]] so it can be unpacked successfully?

I have tried

o.pack_array(2);
o.pack_uint16(v.getA());
o.pack_uint16(v.getB());
o.pack_array(2);
o.pack_uint16(v.getX());
o.pack_uint16(v.getY());

But it did not seem to work.

redboltz commented 2 years ago
o.pack_array(3); // not 2 
o.pack_uint16(v.getA());
o.pack_uint16(v.getB());
o.pack_array(2);
o.pack_uint16(v.getX());
o.pack_uint16(v.getY());

The first number of array should be 3 not 2 becaulse it has the third member [x, y].

patrickjane commented 2 years ago

Alright thanks, this worked.