将结构附加到向量<uint8_t>

时间:2017-12-21 03:37:47

标签: c++ c++11

我正在写一个消息comuniction lib,需要读取一些结构的数据,并将这个结构附加到一个向量,然后再次读取,再次追加。

如果在C语言中,memcpy工作正常,但我想要的是用C ++ 11代码风格制作所有代码。

我尝试使用std :: copy,但它需要一个开始和结束的交互器,那么我怎样才能使用像std::copy(&a, &a + sizeof(A), back_inserter(buffer));那样的std :: copy?

3 个答案:

答案 0 :(得分:1)

你可以这样做:

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);

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

答案 1 :(得分:1)

这是一种干净的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 << "failed\n";
    return -1;
}
std::cout << d2.buff << "\n";

Live example

我们可以优化推送字节,如果它们太慢而不能预先调整缓冲区大小,那么使用std copy或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) );
}

可能会更快一些。

答案 2 :(得分:0)

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

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

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

完整代码here

请注意,与Yakk答案相比,这是非常简单的代码,但我认为有些人可能更容易阅读没有模板的代码。我也使用C样式转换,有些人认为不应该在C ++中完成,但我发现重新解释为这个用例过于冗长。