C++ 将结构附加到向量<;uint8_t>;

C++ 将结构附加到向量<;uint8_t>;,c++,c++11,C++,C++11,我正在写一个消息通信库,需要读取一些数据到一个结构,然后将这个结构附加到一个向量,然后再次读取,再次附加 如果在C语言中,memcpy工作得很完美,但我想让所有代码都采用C++11代码风格 我尝试使用std::copy,但它需要一个开始和结束interator,那么我如何才能像std::copy(&a,&a+sizeof(a),back_inserter(buffer))那样准确地使用std::copy呢?您可以执行以下操作: struct MyStruct { int a; dou

我正在写一个消息通信库,需要读取一些数据到一个结构,然后将这个结构附加到一个向量,然后再次读取,再次附加

如果在C语言中,memcpy工作得很完美,但我想让所有代码都采用C++11代码风格

我尝试使用std::copy,但它需要一个开始和结束interator,那么我如何才能像
std::copy(&a,&a+sizeof(a),back_inserter(buffer))那样准确地使用std::copy呢

您可以执行以下操作:

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

(或某种程度)。

< P>这里是一个干净的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}; }
模板
结构范围{
它是b,e;
它的begin()常量{return b;}
It end()常量{return e;}
std::size_t size()常量{return end()-begin();}
};
模板
range_t 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));
}
模板
范围\u t为\u字节(t*t){
静态断言(std::is_琐碎地_可复制::value,“如果不是琐碎地可复制,那就糟糕了”);
自动*ptr=重新解释铸件(t);
返回范围(ptr,ptr+sizeof(T));
}
模板
范围\u t为\u字节(t const*t){
静态断言(std::is_琐碎地_可复制::value,“如果不是琐碎地可复制,那就糟糕了”);
自动*ptr=重新解释铸件(t);
返回范围(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;
}
模板
无效推送字节(标准::向量和目标,T常量*数据){
自动字节=作为字节(数据);
insert(target.end(),bytes.begin(),bytes.end());
}
模板
bool弹出字节(std::vector和src,T*数据){
自动字节=作为字节(数据);
if(bytes.size()>src.size())返回false;
std::copy(src.end()-bytes.size(),src.end(),bytes.begin());
resize(src.size()-bytes.size());
返回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";
struct一些\u数据{
int x,y;
字符buff[1024];
};
std::向量字节;
一些_数据{1,2,“hello”};
推入字节(字节和数据);
一些数据;
如果(!弹出字节(字节,&d2)){

std::cout您可以使用向量插入成员函数

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

void追加(std::vector&v,const MyStruct&s){
v、 插入(v.end(),(unsigned char*)&s,((unsigned char*)&s)+sizeof s);
}
完整代码


注意到这是一个非常简单的代码,与YAKK的答案相比,但是我认为对于一些人来说,不使用模板来读取代码可能更容易。我也使用C型的转换,有些人认为不应该在C++中完成,但是我发现对于这个用例来说,解释太过于冗长了。在这里,我仍然使用memcpy,因为您所做的实际上是旧的hat C。出于安全原因,我会添加一个静态断言,以确保

静态断言(std::is_pod_v,“必须是普通的旧数据”)你必须把指针投向<代码> uTI8*T*<代码>,这样我就不确定它是什么。最好的选择是不要使用Struts作为中介。@ Alxand HeSuZAGH结构完全是POD。@ ReaviSeDeSK我建议在这里使用C风格的界面,C++安全。多字节值被转换为一致的字节顺序,并使用
重新解释\u cast
,因为您所做的实际上只是为C代码添加了更强的保证。
静态断言
确保如果您更改代码,您有编译时保证不会引入危险行为。这
返回\u inserter
可以进行一些优化,更不用说它的速度有多慢了。此外,执行
缓冲区插入(buffer.end(),ptr,ptr+sizeof(data))
在优化方面要好得多,因为
insert
可以
预先保留
最后一个挑剔:因为
(u)int8\u t
是整数类型,假设从指针读取不是UB是不安全的,尽管我不知道有任何实现不
typedef char int8\u t
。总之,使用
(无符号)char
std::byte
取而代之。@MárioFeroldi如果我先使用vector.reserve获得固定大小的内存会怎么样?back_inserter还会变慢吗?@reavenisadesk测量它!我的直觉说它有用,但不依赖直觉。我编写了一个测试代码,如
auto ptr=reinterpret_cast(a_ptr->data);vec.insert(vec.end()它的工作原理很好,但是当我在代码中使用它时,编译器会抛出错误:没有匹配的成员函数来调用“插入”。我查找标准的INSERT接口,没有找到这样的定义,这是一个标准的C++向量接口吗?指针是迭代器。因此它是过载(4)。在这个链接上:我的答案也有完整的代码,你们可以在这里找到可编译的例子
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";
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) );
}
void append(std::vector<unsigned char>& v, const MyStruct& s){
    v.insert(v.end(), (unsigned char*) &s, ((unsigned char*)&s)+sizeof s);   
}