将结构附加到向量

问题描述 投票:3回答:3

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

如果在C语言中,memcpy工作得很完美,但我想要的是用C ++ 11代码样式制作所有代码。

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

c++ c++11
3个回答
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::memcpyreinterpret_cast抛弃了语言的所有类型安全。亚历山大建议使用static_assert是一个很好的建议。

编辑:

马里奥是对的,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
投票

这是一个干净的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) );
}

这可能会更快一些。


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 ++完成,但我发现重新解释为这个用例过于冗长。

© www.soinside.com 2019 - 2024. All rights reserved.