将结构追加到矢量<uint8_t>

Append structs to a vector<uint8_t>

本文关键字:uint8 gt 结构 lt 追加      更新时间:2023-10-16

我正在写一个消息com com comuntiction lib,需要将一些数据读取到struct,并将该结构附加到向量,然后再次阅读,再次附加。

如果用C语言,memcpy的工作原理是完美的,但是我想用C 11代码样式制作所有代码。

我尝试使用std ::复制,但是它需要一个开始和结束互机,那么我如何才能使用std :: copy(例如 std::copy(&a, &a + sizeof(A), back_inserter(buffer));

您可以做到这一点:

struct MyStruct {
   int a;
   double b;
   int c;
};
std::vector<uint8_t> buffer;
MyStruct data { 42, 3.14159, 123 };
uint8_t* ptr = reinterpret_cast<uint8_t*>(&data);
std::copy(ptr, ptr + sizeof(data), back_inserter(buffer));

请注意,在这种情况下,std::copy只需恢复到下方的std::memcpy,而reinterpret_cast抛弃了该语言的所有类型安全性。亚历山大使用static_assert的建议是一个很好的建议。

编辑:

mário是对的,back_inserter会导致std::copy不等于std::memcpy。一种替代方法可能是首先重新分配您的缓冲区,然后复制:

size_t len = buffer.size();
buffer.resize(len+sizeof(data));
std::copy(ptr, ptr + sizeof(data), buffer.data() + len);

(或某种程度上的东西)。

这是一种干净的C 做到的方法:

首先是简单的范围类型:

template<class It>
struct range_t {
  It b, e;
  It begin() const { return b; }
  It end() const { return e; }
  std::size_t size() const { return end()-begin(); }
};
template<class It>
range_t<It> range(It s, It f) { return {s,f}; }

它代表了一些迭代器。

接下来,一些将POD数据视为字节的功能:

template<class T>
range_t< unsigned char* > as_bytes( T* t ) {
  static_assert( std::is_trivially_copyable<T>::value, "bad idea if not trivially copyable" );
  auto* ptr = reinterpret_cast<unsigned char*>(t);
  return range(ptr, ptr+sizeof(T));
}
template<class T>
range_t< unsigned char const* > as_bytes( T const* t ) {
  static_assert( std::is_trivially_copyable<T>::value, "bad idea if not trivially copyable" );
  auto* ptr = reinterpret_cast<unsigned char const*>(t);
  return range(ptr, ptr+sizeof(T));
}

既读写版本。

接下来,将结构并塞入向量的功能或将其弹出:

template<class T>
void push_bytes_in( std::vector<std::uint8_t>& target, T const* data ) {
  auto bytes = as_bytes(data);
  target.insert( target.end(), bytes.begin(), bytes.end() );
}
template<class T>
bool pop_bytes_out( std::vector<std::uint8_t>& src, T* data ) {
  auto bytes = as_bytes(data);
  if (bytes.size() > src.size()) return false;
  std::copy( src.end()-bytes.size(), src.end(), bytes.begin() );
  src.resize( src.size()-bytes.size() );
  return true;
}

最后,测试代码:

struct some_data {
  int x, y;
  char buff[1024];
};

std::vector<std::uint8_t> bytes;
some_data data{1,2, "hello"};
push_bytes_in( bytes, &data );
some_data d2;
if (!pop_bytes_out( bytes, &d2)) {
    std::cout << "failedn";
    return -1;
}
std::cout << d2.buff << "n";

现场示例。

,如果它们的速度太慢,无法预先大小缓冲区,然后使用std副本或memcpy推出字节,我们可以优化推送字节。但是,您应该谨慎地确保在这种情况下保留指数数据。

template<class T>
void push_bytes_in( std::vector<std::uint8_t>& target, T const* data ) {
  if (target.capacity() < target.size()+sizeof(T)) {
    target.reserve( target.capacity()*3/2 +1 );
  }
  auto bytes = as_bytes(data);
  target.resize( target.size() + sizeof(T) );
  std::copy( bytes.begin(), bytes.end(), target.end()-sizeof(T) );
}

可能会更快一些。

您可以使用向量插入成员函数。

这比复制要好,因为向量插入知道如何分配内存(您不需要使用丑陋的back_inserter)。

void append(std::vector<unsigned char>& v, const MyStruct& s){
    v.insert(v.end(), (unsigned char*) &s, ((unsigned char*)&s)+sizeof s);   
}

此处的完整代码

请注意,与Yakk答案相比,这是非常简单的代码,但我认为某些人在没有模板的情况下读取代码可能会更容易。另外,我使用的是某些人认为不应该在C 中完成的C样式,但我发现重新解释了该用例的冗长。