我正在使用
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
两个问题。
boost::interprocess::message_queue
的发送函数。我们如何得到这个尺寸?如有任何意见,我将不胜感激。
我建议使用动态大小的容器:
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;
}
演示所有方法并使用一些标志优化存档大小:
#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 在这里没有给您带来任何功能(没有对象跟踪、对象图递归,甚至没有可移植性),请考虑在这里仅使用按位序列化:
#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