我正在编写一个消息comuniction lib,需要将一些数据读取到结构中,并将此结构附加到向量,然后再次读取,再次追加。
如果在C语言中,memcpy工作得很完美,但我想要的是用C ++ 11代码样式制作所有代码。
我尝试使用std :: copy,但它需要一个开始和结束的交互器,那么我怎样才能使用像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));
请注意,在这种情况下std::copy
只是恢复到下面的std::memcpy
和reinterpret_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);
(或者那种程度的东西)。
这是一个干净的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";
我们可以优化推送字节,如果它们变得太慢而不能预先调整缓冲区大小,那么使用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) );
}
这可能会更快一些。
您可以使用矢量插入成员函数。
这比复制更好,因为向量插入知道如何分配内存(你不需要使用丑陋的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 ++完成,但我发现重新解释为这个用例过于冗长。