增强对字符缓冲区的序列化和数据的反序列化

问题描述 投票:0回答:1

我正在使用

boost::interprocess::message_queue
制作进程之间的通信模块 因为消息队列需要数组缓冲区,所以我会将数据包序列化到数组缓冲区中。

inline void message_queue_t<VoidPointer>::send
   (const void *buffer, size_type buffer_size, unsigned int priority)

例如,

  header header(5, 2);
  char buffer[64] = {};
  uint32_t size = header.save(buffer);

  queue.send(buffer, sizeof(size), 0);   // queue is boost::interprocess::message_queue

这是一个进展:


#ifndef IPC_PACKET_HEADER_HPP
#define IPC_PACKET_HEADER_HPP

#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/iostreams/stream.hpp>
#include <iostream>
#include <string>

class header {
public: 
  header(uint32_t id, uint32_t size) :
    id_(id),
    size_(size) {
  }

  ~header() = default;

  uint32_t save(char* buffer) {
    boost::iostreams::basic_array_sink<char> sink((char *)buffer, 64);  
    boost::iostreams::stream<boost::iostreams::basic_array_sink<char>> os(sink);
    boost::archive::binary_oarchive oa(os);
    oa & *(this);

    return 0; // size that are copied to the buffer?
  }

  void load(const void* data) {
    // boost::archive::binary_iarchive ia(iss);
    // ia & *(this);
  }

private:
  friend class boost::serialization::access;
  
  uint32_t id_;
  uint32_t size_;

template<class Archive>
  void serialize(Archive& ar, const unsigned int version) {
    ar & id_;
    ar & size_;
  }
};

#endif

两个问题。

  1. 使用 save() 函数序列化时,我需要知道序列化的大小,以便将其传递给
    boost::interprocess::message_queue
    的发送函数。我们如何得到这个尺寸?
  2. 我不知道如何实现加载功能。它假设获取一个字节数据并应该自行加载它们。你能帮忙吗?

如有任何意见,我将不胜感激。

c++ boost boost-serialization
1个回答
0
投票

动态缓冲区

我建议使用动态大小的容器:

void save(std::vector<char>& buffer) {
    bio::stream os(bio::back_inserter(buffer));
    boost::archive::binary_oarchive(os) << *this;
}

现在您将只拥有

buffer.size()
反映序列化的字节:

header example {42,9};

std::vector<char> dynamic;
example.save(dynamic);
fmt::print("dynamic({}), {::02x}\n", dynamic.size(), dynamic);

静态缓冲区

当然,如果您坚持可以使用静态大小的容器。

uint32_t save(std::span<char> buffer) {
    bio::stream os(bio::array_sink(buffer.data(), buffer.size()));
    boost::archive::binary_oarchive(os) << *this;
    return os.tellp();
}

这里,返回的值是流的放置缓冲区中的结果查找位置。

警告如果您的缓冲区太小,它会默默地创建一个不完整的存档

加载中

无论选择哪种方法,加载看起来都是一样的:

void load(std::span<char const> data) {
    bio::stream is(bio::array_source(data.data(), data.size()));
    boost::archive::binary_iarchive(is) >> *this;
}

现场演示

演示所有方法并使用一些标志优化存档大小:

住在Coliru

#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>

#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/stream.hpp>
#include <fmt/ranges.h>
#include <iostream>
#include <span>

namespace bio = boost::iostreams;

template <int ArchiveFlags = 0> class header {
  public:
    header(uint32_t id = -1, uint32_t size = -1) : id_(id), size_(size) {}

    ~header() = default;

    uint32_t save(std::span<char> buffer) {
        bio::stream os(bio::array_sink(buffer.data(), buffer.size()));
        boost::archive::binary_oarchive(os, ArchiveFlags) << *this;
        return os.tellp();
    }

    void save(std::vector<char>& buffer) {
        bio::stream os(bio::back_inserter(buffer));
        boost::archive::binary_oarchive(os, ArchiveFlags) << *this;
    }

    void load(std::span<char const> data) {
        bio::stream is(bio::array_source(data.data(), data.size()));
        boost::archive::binary_iarchive(is, ArchiveFlags) >> *this;
    }

    bool operator==(header const&) const = default;

  private:
    friend class boost::serialization::access;

    uint32_t id_;
    uint32_t size_;

    template <class Archive> void serialize(Archive& ar, unsigned) { ar& id_& size_; }
};

template <int Flags = 0> void demo() {
    using T = header<Flags>;

    T example{42, 9};

    std::vector<char> dynamic;
    example.save(dynamic);
    fmt::print("dynamic({}), {::02x}\n", dynamic.size(), dynamic);

    {
        T roundtrip;
        roundtrip.load(dynamic);
        fmt::print("roundtrip: {}\n", roundtrip == example);
    }

    std::array<char, 64> fixed;
    auto n = example.save(fixed);
    fmt::print("fixed({}), {::02x}\n", n, std::span(fixed).subspan(0, n));

    {
        T roundtrip;
        roundtrip.load(fixed); // remaining bytes ignored
        fmt::print("sloppy roundtrip: {}\n", roundtrip == example);
    }

    {
        T roundtrip;
        roundtrip.load(std::span(fixed).subspan(0, n)); // trimmed remaining bytes
        fmt::print("trimmed roundtrip: {}\n", roundtrip == example);
    }
}

int main() {
    fmt::print("\n------ Normal archive flags\n");
    demo(); // normal

    fmt::print("\n------ Size-optimized archive flags\n");
    demo<boost::archive::no_header     //
         | boost::archive::no_codecvt  //
         | boost::archive::no_tracking //
         >();                          // optimized
}

打印信息和预期:

------ Normal archive flags
dynamic(53), [16, 00, 00, 00, 00, 00, 00, 00, 73, 65, 72, 69, 61, 6c, 69, 7a, 61, 74, 69, 6f, 6e, 3a, 3a, 61, 72, 63, 68, 69, 76, 65, 14, 00, 04, 08, 04, 08, 01, 00, 00, 00, 00, 00, 00, 00, 00, 2a, 00, 00, 00, 09, 00, 00, 00]
roundtrip: true
fixed(53), [16, 00, 00, 00, 00, 00, 00, 00, 73, 65, 72, 69, 61, 6c, 69, 7a, 61, 74, 69, 6f, 6e, 3a, 3a, 61, 72, 63, 68, 69, 76, 65, 14, 00, 04, 08, 04, 08, 01, 00, 00, 00, 00, 00, 00, 00, 00, 2a, 00, 00, 00, 09, 00, 00, 00]
sloppy roundtrip: true
trimmed roundtrip: true

------ Size-optimized archive flags
dynamic(13), [00, 00, 00, 00, 00, 2a, 00, 00, 00, 09, 00, 00, 00]
roundtrip: true
fixed(13), [00, 00, 00, 00, 00, 2a, 00, 00, 00, 09, 00, 00, 00]
sloppy roundtrip: true
trimmed roundtrip: true

奖金

由于 Boost Serialization 在这里没有给您带来任何功能(没有对象跟踪、对象图递归,甚至没有可移植性),请考虑在这里仅使用按位序列化:

住在Coliru

#include <cassert>
#include <fmt/ranges.h>
#include <iostream>
#include <span>

namespace MyMessages {
    struct header {
        uint32_t id_;
        uint32_t size_;

        auto operator<=>(header const&) const = default;
    };

    struct some_other_message {
        header   header_;
        uint32_t len_;
        uint8_t  text_[32];

        auto operator<=>(some_other_message const&) const = default;
    };

    using std::span; // or e.g. boost::span

    template <typename T> static inline auto save(T const& msg, span<char> out) {
        static_assert(std::is_trivial_v<T> && std::is_standard_layout_v<T>);

        assert(out.size() >= sizeof(T));
        memcpy(out.data(), &msg, sizeof(T));
        return out.subspan(sizeof(T));
    }

    template <typename T> static inline auto load(T& msg, span<char const> in) {
        static_assert(std::is_trivial_v<T> && std::is_standard_layout_v<T>);

        assert(in.size() >= sizeof(T));
        memcpy(&msg, in.data(), sizeof(T));
        return in.subspan(sizeof(T));
    }
} // namespace MyMessages


int main() {
    using MyMessages::span;
    MyMessages::some_other_message example{{42, 9}, 12, "Hello world!"};

    std::array<char, 64> buf;

    {
        auto remain = save(example, buf);
        auto n      = remain.data() - buf.data();
        fmt::print("fixed({}), {::02x}\n", n, span(buf).subspan(0, n));
    }

    {
        MyMessages::some_other_message roundtrip;

        auto remain   = load(roundtrip, buf);
        auto consumed = remain.data() - buf.data();
        fmt::print("roundtrip({}): {}\n", consumed, roundtrip == example);
    }

    {
        MyMessages::header just_header;

        auto remain   = load(just_header, buf);
        auto consumed = remain.data() - buf.data();
        fmt::print("partial deserialization({}): {}\n", consumed, just_header == example.header_);
    }
}

请注意它如何不使用 Boost Serialization、Boost Iostreams 或任何 boost,并且标头使用序列化存档序列化为 8 个字节而不是 53 个字节:

fixed(44), [2a, 00, 00, 00, 09, 00, 00, 00, 0c, 00, 00, 00, 48, 65, 6c, 6c, 6f, 20, 77, 6f, 72, 6c, 64, 21, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]
roundtrip(44): true
partial deserialization(8): true
© www.soinside.com 2019 - 2024. All rights reserved.