I'm writing a message comuniction lib, and need to read some data to a struct, and append this struct to a vector, and read again, append again.
If in C language, memcpy works perfect, but I want is to make all code in C++11 code style.
I tried to use std::copy, but it needs a begin and end interator, so how exactly I can use std::copy like std::copy(&a, &a + sizeof(A), back_inserter(buffer));?
You can do this:
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));
Note that std::copy in this case just reverts to std::memcpy underneath, and reinterpret_cast throws away all type safety of the language. Alexander's suggestion of using a static_assert is a good one.
EDIT:
Mário is right, back_inserter would cause std::copy to not be equivalent to std::memcpy. An alternative could be to reallocate your buffer first, then copy:
size_t len = buffer.size();
buffer.resize(len+sizeof(data));
std::copy(ptr, ptr + sizeof(data), buffer.data() + len);
(or something to that extent).
Here is a clean C++ way to do it:
First a simple range type:
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}; }
it represents a range of some iterators.
Next, some functions to treat pod data as bytes:
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));
}
both read and write versions.
Next, functions that take a structure and stuff them into a vector, or pop them out:
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;
}
Finally, test code:
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.
We could optimize push bytes if they turn out to be too slow to pre-size the buffer, then shove the bytes in using std copy or memcpy. However, you should be careful to ensure exponential data reservation in that case.
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) );
}
that may turn out to be a bit faster.
You can use vector insert member function.
This is better than copy since vector insert knows how to allocate memory(you do not need to use ugly back_inserter).
void append(std::vector<unsigned char>& v, const MyStruct& s){
v.insert(v.end(), (unsigned char*) &s, ((unsigned char*)&s)+sizeof s);
}
full code here
note that this is very simple code compared to Yakk answer but I think it may be easier for some people to read code without templates. Also I use C style cast that some people think should not be done in C++ but I find reinterpret cast too verbose for this use case.
Related
Moin Moin,
I'd like to pass a variable Pointer Address to a Function to get the bytes of the variable in a buffer - see code snippet!
This version of the code works ... but I like to skip the additional address definition before the function call.
Thank you very much!
float f = 133.2342;
int x = 12345;
byte buffer[8] = {0,0,0,0,0,0,0,0};
void var_to_byte(const int anz, int* var_split, int* buffer)
{
for (int i=0;i<8;i++) buffer[i]=0;
for (int i=0;i<anz;i++)
{
buffer[i]=var_split[i];
}
}
void setup()
{
Serial.begin(115200);
// I like to skip these lines
int z = &f;
int y = &buffer;
// call the function with var_to_byte(sizeof(f),&f,&buffer); is not working
var_to_byte ( sizeof(f),z,y); // this works !!!!
Serial.print("Buffer: ");
for (int i=0;i<8;i++)
{
if (buffer[i]<0x10)
{
Serial.print("0");
}
Serial.print(buffer[i],HEX);
}
Serial.println();
}
void loop() {
// put your main code here, to run repeatedly:
}
I don't know how to solve this problem ... and yes I'm new to C++ and/or Arduino ....
It cannot work because the types do not match in the slightest.
You can serialise a value into an existing buffer like so:
// if out is too small, this will blow up (undefined behaviour)
inline void serialise(float const f, std::byte* out) {
auto const* raw = reinterpret_cast<std::byte const*>(&f);
std::copy(raw, raw+sizeof(float), out);
}
But you can also avoid having to pass in the buffer:
(see below if you want to avoid std::array)
#include <array>
inline auto serialise(float const f) {
std::array<std::byte, sizeof(f)> out;
auto const* raw = reinterpret_cast<std::byte const*>(&f);
std::copy(raw, raw+sizeof(f), out.begin());
return out; // copy elided by all sensible compilers
}
// usage
int main() {
float const f = 3.14f;
auto const ser = serialise(f);
// do stuff with ser
for (auto const x: ser) {
if (x<0x10)
Serial.print("0");
Serial.print(x,HEX);
}
Serial.println();
}
Of course, you can generalise serialise to any type:
template <typename T>
inline auto serialise(T const& t) {
// pretty much the same code only with T instead of float
}
Edit:
If you have to avoid using the standard library, you can also pass a sized raw-array reference:
auto serialise(float const f, std::byte (&out)[sizeof(float)]) {
auto const* raw = reinterpret_cast<std::byte const*>(&f);
std::copy(raw, raw+sizeof(f), out);
return out; // copy elided by all sensible compilers
}
The types in your code don't make sense. If you want to populate a buffer of bytes then why does your function say this int* buffer? That's a buffer of int not byte.
The simple approach is to use the standard library function memcpy for copying bytes. No need for your own function for this standard task.
memcpy(buffer, &f, sizeof f); // copy the bytes of f to buffer
memcpy is declared in the header file <string.h>
I'd like a way to take an arbitrary-size array of bytes and return a hex string. Specifically for logging packets sent over the net, but I use the equivalent function that takes a std::vector a lot. Something like this, probably a template?
std::string hex_str(const std::array<uint8_t,???> array);
I've searched but the solutions all say "treat it as a C-style array" and I am specifically asking whether there's a way not to do that. I assume the reason this isn't in every single C++ FAQ is that it's impossible and if so can someone outline why?
I already have these overloads and the second one can be used for std::array by decaying into a C-style array, so please don't tell me how to do that.
std::string hex_str(const std::vector<uint8_t> &data);
std::string hex_str(const uint8_t *data, const size_t size);
(edit: vector is a reference in my code)
You should consider writing the function to work with iterators, like standard algorithms do. Then you can use it with both std::vector and std::array inputs, eg:
template<typename Iter>
std::string hex_str(Iter begin, Iter end)
{
std::ostringstream output;
output << std::hex << std::setw(2) << std::setfill('0');
while(begin != end)
output << static_cast<unsigned>(*begin++);
return output.str();
}
Online Demo
If you really want to avoid having to call begin()/end() on whatever container you pass in, you can define a helper to handle that for you, eg:
template<typename C>
std::string hex_str(const C &data) {
return hex_str(data.begin(), data.end());
}
Online Demo
Or, if you really wanted to, you can just flatten this all down into a single function, eg:
template <typename C>
std::string hex_str(const C& data)
{
std::ostringstream output;
output << std::hex << std::setw(2) << std::setfill('0');
for(const auto &elem : data)
output << static_cast<unsigned>(elem);
return output.str();
}
Online Demo
If you know the size of the std::array at compile time you can use a non type template parameter.
template<std::size_t N>
std::string hex_str( const std::array<std::uint8_t, N>& buffer )
{ /* Implementation */ }
int main( )
{
// Usage.
std::array<std::uint8_t, 5> bytes = { 1, 2, 3, 4, 5 };
const auto value{ hex_str( bytes ) };
}
Or you can just template the entire container (cut down on your overloads).
template<typename Container>
std::string hex_str( const Container& buffer )
{ /* Implementaion */ }
int main( )
{
// Usage.
std::array<std::uint8_t, 5> bytes = { 1, 2, 3, 4, 5 };
const auto value{ hex_str( bytes ) };
}
So I have the following available:
struct data_t {
char field1[10];
char field2[20];
char field3[30];
};
const char *getData(const char *key);
const char *field_keys[] = { "key1", "key2", "key3" };
This code is given to my and I cannot modify it in any way. It comes from some old C project.
I need to fill in the struct using the getData function with the different keys, something like the following:
struct data_t my_data;
strncpy(my_data.field1, getData(field_keys[0]), sizeof(my_data.field1));
strncpy(my_data.field1, getData(field_keys[1]), sizeof(my_data.field2));
strncpy(my_data.field1, getData(field_keys[2]), sizeof(my_data.field3));
Of course, this is a simplification, and more things are going on in each assignment. The point is that I would like to represent the mapping between keys and struct member in a constant structure, and use that to transform the last code in a loop. I am looking for something like the following:
struct data_t {
char field1[10];
char field2[20];
char field3[30];
};
typedef char *(data_t:: *my_struct_member);
const std::vector<std::pair<const char *, my_struct_member>> mapping = {
{ "FIRST_KEY" , &my_struct_t::field1},
{ "SECOND_KEY", &my_struct_t::field2},
{ "THIRD_KEY", &my_struct_t::field3},
};
int main()
{
data_t data;
for (auto const& it : mapping) {
strcpy(data.*(it.second), getData(it.first));
// Ideally, I would like to do
// strlcpy(data.*(it.second), getData(it.first), <the right sizeof here>);
}
}
This, however, has two problems:
It does not compile :) But I believe that should be easy to solve.
I am not sure about how to get the sizeof() argument for using strncpy/strlcpy, instead of strcpy. I am using char * as the type of the members, so I am losing the type information about how long each array is. In the other hand, I am not sure how to use the specific char[T] types of each member, because if each struct member pointer has a different type I don't think I will be able to have them in a std::vector<T>.
As explained in my comment, if you can store enough information to process a field in a mapping, then you can write a function that does the same.
Therefore, write a function to do so, using array references to ensure what you do is safe, e.g.:
template <std::size_t N>
void process_field(char (&dest)[N], const char * src)
{
strlcpy(dest, getData(src), N);
// more work with the field...
};
And then simply, instead of your for loop:
process_field(data.field1, "foo");
process_field(data.field2, "bar");
// ...
Note that the amount of lines is the same as with a mapping (one per field), so this is not worse than a mapping solution in terms of repetition.
Now, the advantages:
Easier to understand.
Faster: no memory needed to keep the mapping, more easily optimizable, etc.
Allows you to write different functions for different fields, easily, if needed.
Further, if both of your strings are known at compile-time, you can even do:
template <std::size_t N, std::size_t M>
void process_field(char (&dest)[N], const char (&src)[M])
{
static_assert(N >= M);
std::memcpy(dest, src, M);
// more work with the field...
};
Which will be always safe, e.g.:
process_field(data.field1, "123456789"); // just fits!
process_field(data.field1, "1234567890"); // error
Which has even more pros:
Way faster than any strcpy variant (if the call is done in run-time).
Guaranteed to be safe at compile-time instead of run-time.
A variadic templates based solution:
struct my_struct_t {
char one_field[30];
char another_field[40];
};
template<typename T1, typename T2>
void do_mapping(T1& a, T2& b) {
std::cout << sizeof(b) << std::endl;
strncpy(b, a, sizeof(b));
}
template<typename T1, typename T2, typename... Args>
void do_mapping(T1& a, T2& b, Args&... args) {
do_mapping(a, b);
do_mapping(args...);
}
int main()
{
my_struct_t ms;
do_mapping(
"FIRST_MAPPING", ms.one_field,
"SECOND_MAPPING", ms.another_field
);
return 0;
}
Since data_t is a POD structure, you can use offsetof() for this.
const std::vector<std::pair<const char *, std::size_t>> mapping = {
{ "FIRST_FIELD" , offsetof(data_t, field1},
{ "SECOND_FIELD", offsetof(data_t, field2)}
};
Then the loop would be:
for (auto const& it : mapping) {
strcpy(static_cast<char*>(&data) + it.second, getData(it.first));
}
I don't think there's any way to get the size of the member similarly. You can subtract the offset of the current member from the next member, but this will include padding bytes. You'd also have to special-case the last member, subtracting the offset from the size of the structure itself, since there's no next member.
The mapping can be a function to write the data into the appropriate member
struct mapping_t
{
const char * name;
std::function<void(my_struct_t *, const char *)> write;
};
const std::vector<mapping_t> mapping = {
{ "FIRST_KEY", [](data_t & data, const char * str) { strlcpy(data.field1, str, sizeof(data.field1); } }
{ "SECOND_KEY", [](data_t & data, const char * str) { strlcpy(data.field2, str, sizeof(data.field2); } },
{ "THIRD_KEY", [](data_t & data, const char * str) { strlcpy(data.field3, str, sizeof(data.field3); } },
};
int main()
{
data_t data;
for (auto const& it : mapping) {
it.write(data, getData(it.name));
}
}
To iterate over struct member you need:
offset / pointer to the beginning of that member
size of that member
struct Map {
const char *key;
std::size_t offset;
std::size_t size;
};
std::vector<Map> map = {
{ field_keys[0], offsetof(data_t, field1), sizeof(data_t::field1), },
{ field_keys[1], offsetof(data_t, field2), sizeof(data_t::field2), },
{ field_keys[2], offsetof(data_t, field3), sizeof(data_t::field3), },
};
once we have that we need strlcpy:
std::size_t mystrlcpy(char *to, const char *from, std::size_t max)
{
char * const to0 = to;
if (max == 0)
return 0;
while (--max != 0 && *from) {
*to++ = *from++;
}
*to = '\0';
return to0 - to - 1;
}
After having that, we can just:
data_t data;
for (auto const& it : map) {
mystrlcpy(reinterpret_cast<char*>(&data) + it.offset, getData(it.key), it.size);
}
That reinterpret_cast looks a bit ugly, but it just shift &data pointer to the needed field.
We can also create a smarter container which takes variable pointer on construction, thus is bind with an existing variable and it needs a little bit of writing:
struct Map2 {
static constexpr std::size_t max = sizeof(field_keys)/sizeof(*field_keys);
Map2(data_t* pnt) : mpnt(pnt) {}
char* getDest(std::size_t num) {
std::array<char*, max> arr = {
mpnt->field1,
mpnt->field2,
mpnt->field3,
};
return arr[num];
}
const char* getKey(std::size_t num) {
return field_keys[num];
}
std::size_t getSize(std::size_t num) {
std::array<std::size_t, max> arr = {
sizeof(mpnt->field1),
sizeof(mpnt->field2),
sizeof(mpnt->field3),
};
return arr[num];
}
private:
data_t* mpnt;
};
But probably makes the iterating more readable:
Map2 m(&data);
for (std::size_t i = 0; i < m.max; ++i) {
mystrlcpy(m.getDest(i), getData(m.getKey(i)), m.getSize(i));
}
Live code available at onlinegdb.
So I'm currently refactoring a giant function:
int giant_function(size_t n, size_t m, /*... other parameters */) {
int x[n]{};
float y[n]{};
int z[m]{};
/* ... more array definitions */
And when I find a group of related definitions with discrete functionality, grouping them into a class definition:
class V0 {
std::unique_ptr<int[]> x;
std::unique_ptr<float[]> y;
std::unique_ptr<int[]> z;
public:
V0(size_t n, size_t m)
: x{new int[n]{}}
, y{new float[n]{}}
, z{new int[m]{}}
{}
// methods...
}
The refactored version is inevitably more readable, but one thing I find less-than-satisfying is the increase in the number of allocations.
Allocating all those (potentially very large) arrays on the stack is arguably a problem waiting to happen in the unrefactored version, but there's no reason that we couldn't get by with just one larger allocation:
class V1 {
int* x;
float* y;
int* z;
public:
V1(size_t n, size_t m) {
char *buf = new char[n*sizeof(int)+n*sizeof(float)+m*sizeof(int)];
x = (int*) buf;
buf += n*sizeof(int);
y = (float*) buf;
buf += n*sizeof(float);
z = (int*) buf;
}
// methods...
~V0() { delete[] ((char *) x); }
}
Not only does this approach involve a lot of manual (read:error-prone) bookkeeping, but its greater sin is that it's not composable.
If I want to have a V1 value and a W1 value on the stack, then that's
one allocation each for their behind-the-scenes resources. Even simpler, I'd want the ability to allocate a V1 and the resources it points to in a single allocation, and I can't do that with this approach.
What this initially led me to was a two-pass approach - one pass to calculate how much space was needed, then make one giant allocation, then another pass to parcel out the allocation and initialize the data structures.
class V2 {
int* x;
float* y;
int* z;
public:
static size_t size(size_t n, size_t m) {
return sizeof(V2) + n*sizeof(int) + n*sizeof(float) + m*sizeof(int);
}
V2(size_t n, size_t m, char** buf) {
x = (int*) *buf;
*buf += n*sizeof(int);
y = (float*) *buf;
*buf += n*sizeof(float);
z = (int*) *buf;
*buf += m*sizeof(int);
}
}
// ...
size_t total = ... + V2::size(n,m) + ...
char* buf = new char[total];
// ...
void* here = buf;
buf += sizeof(V2);
V2* v2 = new (here) V2{n, m, &buf};
However that approach had a lot of repetition at a distance, which is asking for trouble in the long run. Returning a factory got rid of that:
class V3 {
int* const x;
float* const y;
int* const z;
V3(int* x, float* y, int* z) : x{x}, y{y}, z{z} {}
public:
class V3Factory {
size_t const n;
size_t const m;
public:
Factory(size_t n, size_t m) : n{n}, m{m};
size_t size() {
return sizeof(V3) + sizeof(int)*n + sizeof(float)*n + sizeof(int)*m;
}
V3* build(char** buf) {
void * here = *buf;
*buf += sizeof(V3);
x = (int*) *buf;
*buf += n*sizeof(int);
y = (float*) *buf;
*buf += n*sizeof(float);
z = (int*) *buf;
*buf += m*sizeof(int);
return new (here) V3{x,y,z};
}
}
}
// ...
V3::Factory v3factory{n,m};
// ...
size_t total = ... + v3factory.size() + ...
char* buf = new char[total];
// ..
V3* v3 = v3factory.build(&buf);
Still some repetition, but the params only get input once. And still a lot of manual bookkeeping. It'd be nice if I could build this factory out of smaller factories...
And then my haskell brain hit me. I was implementing an Applicative Functor. This could totally be nicer!
All I needed to do was write some tooling to automatically sum sizes and run build functions side-by-side:
namespace plan {
template <typename A, typename B>
struct Apply {
A const a;
B const b;
Apply(A const a, B const b) : a{a}, b{b} {};
template<typename ... Args>
auto build(char* buf, Args ... args) const {
return a.build(buf, b.build(buf + a.size()), args...);
}
size_t size() const {
return a.size() + b.size();
}
Apply(Apply<A,B> const & plan) : a{plan.a}, b{plan.b} {}
Apply(Apply<A,B> const && plan) : a{plan.a}, b{plan.b} {}
template<typename U, typename ... Vs>
auto operator()(U const u, Vs const ... vs) const {
return Apply<decltype(*this),U>{*this,u}(vs...);
}
auto operator()() const {
return *this;
}
};
template<typename T>
struct Lift {
template<typename ... Args>
T* build(char* buf, Args ... args) const {
return new (buf) T{args...};
}
size_t size() const {
return sizeof(T);
}
Lift() {}
Lift(Lift<T> const &) {}
Lift(Lift<T> const &&) {}
template<typename U, typename ... Vs>
auto operator()(U const u, Vs const ... vs) const {
return Apply<decltype(*this),U>{*this,u}(vs...);
}
auto operator()() const {
return *this;
}
};
template<typename T>
struct Array {
size_t const length;
Array(size_t length) : length{length} {}
T* build(char* buf) const {
return new (buf) T[length]{};
}
size_t size() const {
return sizeof(T[length]);
}
};
template <typename P>
auto heap_allocate(P plan) {
return plan.build(new char[plan.size()]);
}
}
Now I could state my class quite simply:
class V4 {
int* const x;
float* const y;
int* const z;
public:
V4(int* x, float* y, int* z) : x{x}, y{y}, z{z} {}
static auto plan(size_t n, size_t m) {
return plan::Lift<V4>{}(
plan::Array<int>{n},
plan::Array<float>{n},
plan::Array<int>{m}
);
}
};
And use it in a single pass:
V4* v4;
W4* w4;
std::tie{ ..., v4, w4, .... } = *plan::heap_allocate(
plan::Lift<std::tie>{}(
// ...
V4::plan(n,m),
W4::plan(m,p,2*m+1),
// ...
)
);
It's not perfect (among other issues, I need to add code to track destructors, and have heap_allocate return a std::unique_ptr that calls all of them), but before I went further down the rabbit hole, I thought I should check for pre-existing art.
For all I know, modern compilers may be smart enough to recognize that the memory in V0 always gets allocated/deallocated together and batch the allocation for me.
If not, is there a preexisting implementation of this idea (or a variation thereof) for batching allocations with an applicative functor?
First, I'd like to provide feedback on problems with your solutions:
You ignore alignment. Relying on assumption that int and float share the same alignment on your system, your particular use case might be "fine". But try to add some double into the mix and there will be UB. You might find your program crashing on ARM chips due to unaligned access.
new (buf) T[length]{}; is unfortunately bad and non-portable. In short: Standard allows the compiler to reserve initial y bytes of the given storage for internal use. Your program fails to allocate this y bytes on systems where y > 0 (and yes, those systems apparently exist; VC++ does this allegedly).
Having to allocate for y is bad, but what makes array-placement-new unusable is the inability to find out how big y is until placement new is actually called. There's really no way to use it for this case.
You're already aware of this, but for completeness: You don't destroy the sub-buffers, so if you ever use a non-trivially-destructible type, then there will be UB.
Solutions:
Allocate extra alignof(T) - 1 bytes for each buffer. Align start of each buffer with std::align.
You need to loop and use non-array placement new. Technically, doing non-array placement new means that using pointer arithmetic on these objects has UB, but standard is just silly in this regard and I choose to ignore it. Here's language lawyerish discussion about that. As I understand, p0593r2 proposal includes a resolution to this technicality.
Add destructor calls that correspond to placement-new calls (or static_assert that only trivially destructible types shall be used). Note that support for non-trivial destruction raises the need for exception safety. If construction of one buffer throws an exception, then the sub buffers that were constructed earlier need to be destroyed. Same care need to be taken when constructor of single element throws after some have already been constructed.
I don't know of prior art, but how about some subsequent art? I decided to take a stab at this from a slightly different angle. Be warned though, this lacks testing and may contain bugs.
buffer_clump template for constructing / destructing objects into an external raw storage, and calculating aligned boundaries of each sub-buffer:
#include <cstddef>
#include <memory>
#include <vector>
#include <tuple>
#include <cassert>
#include <type_traits>
#include <utility>
// recursion base
template <class... Args>
class buffer_clump {
protected:
constexpr std::size_t buffer_size() const noexcept { return 0; }
constexpr std::tuple<> buffers(char*) const noexcept { return {}; }
constexpr void construct(char*) const noexcept { }
constexpr void destroy(const char*) const noexcept {}
};
template<class Head, class... Tail>
class buffer_clump<Head, Tail...> : buffer_clump<Tail...> {
using tail = buffer_clump<Tail...>;
const std::size_t length;
constexpr std::size_t size() const noexcept
{
return sizeof(Head) * length + alignof(Head) - 1;
}
constexpr Head* align(char* buf) const noexcept
{
void* aligned = buf;
std::size_t space = size();
assert(std::align(
alignof(Head),
sizeof(Head) * length,
aligned,
space
));
return (Head*)aligned;
}
constexpr char* next(char* buf) const noexcept
{
return buf + size();
}
static constexpr void
destroy_head(Head* head_ptr, std::size_t last)
noexcept(std::is_nothrow_destructible<Head>::value)
{
if constexpr (!std::is_trivially_destructible<Head>::value)
while (last--)
head_ptr[last].~Head();
}
public:
template<class... Size_t>
constexpr buffer_clump(std::size_t length, Size_t... tail_lengths) noexcept
: tail(tail_lengths...), length(length) {}
constexpr std::size_t
buffer_size() const noexcept
{
return size() + tail::buffer_size();
}
constexpr auto
buffers(char* buf) const noexcept
{
return std::tuple_cat(
std::make_tuple(align(buf)),
tail::buffers(next(buf))
);
}
void
construct(char* buf) const
noexcept(std::is_nothrow_default_constructible<Head, Tail...>::value)
{
Head* aligned = align(buf);
std::size_t i;
try {
for (i = 0; i < length; i++)
new (&aligned[i]) Head;
tail::construct(next(buf));
} catch (...) {
destroy_head(aligned, i);
throw;
}
}
constexpr void
destroy(char* buf) const
noexcept(std::is_nothrow_destructible<Head, Tail...>::value)
{
tail::destroy(next(buf));
destroy_head(align(buf), length);
}
};
A buffer_clump_storage template that leverages buffer_clump to construct sub-buffers into a RAII container.
template <class... Args>
class buffer_clump_storage {
const buffer_clump<Args...> clump;
std::vector<char> storage;
public:
constexpr auto buffers() noexcept {
return clump.buffers(storage.data());
}
template<class... Size_t>
buffer_clump_storage(Size_t... lengths)
: clump(lengths...), storage(clump.buffer_size())
{
clump.construct(storage.data());
}
~buffer_clump_storage()
noexcept(noexcept(clump.destroy(nullptr)))
{
if (storage.size())
clump.destroy(storage.data());
}
buffer_clump_storage(buffer_clump_storage&& other) noexcept
: clump(other.clump), storage(std::move(other.storage))
{
other.storage.clear();
}
};
Finally, a class that can be allocated as automatic variable and provides named pointers to sub-buffers of buffer_clump_storage:
class V5 {
// macro tricks or boost mpl magic could be used to avoid repetitive boilerplate
buffer_clump_storage<int, float, int> storage;
public:
int* x;
float* y;
int* z;
V5(std::size_t xs, std::size_t ys, std::size_t zs)
: storage(xs, ys, zs)
{
std::tie(x, y, z) = storage.buffers();
}
};
And usage:
int giant_function(size_t n, size_t m, /*... other parameters */) {
V5 v(n, n, m);
for(std::size_t i = 0; i < n; i++)
v.x[i] = i;
In case you need only the clumped allocation and not so much the ability to name the group, this direct usage avoids pretty much all boilerplate:
int giant_function(size_t n, size_t m, /*... other parameters */) {
buffer_clump_storage<int, float, int> v(n, n, m);
auto [x, y, z] = v.buffers();
Criticism of my own work:
I didn't bother making V5 members const which would have arguably been nice, but I found it involved more boilerplate than I would prefer.
Compilers will warn that there is a throw in a function that is declared noexcept when the constructor cannot throw. Neither g++ nor clang++ were smart enough to understand that the throw will never happen when the function is noexcept. I guess that can be worked around by using partial specialization, or I could just add (non-standard) directives to disable the warning.
buffer_clump_storage could be made copyable and assignable. This involves loads more code, and I wouldn't expect to have need for them. The move constructor might be superfluous as well, but at least it's efficient and concise to implement.
PROBLEM
I have this old piece of pre-stl C++ code that I want to translate into std C++11 without losing efficiency.
using T = unsigned; // but can be any POD
FILE* fp = fopen( outfile.c_str(), "r" );
T* x = new T[big_n];
fread( x, sizeof(T), big_n, fp );
delete[] x;
fclose( fp );
Note that big_n is really big - like millions of records big, so any inefficiencies are pronounced.
PREVIOUS SOLUTION
In this answer from my previous question, I accepted this solution:
std::vector<T> x(big_n);
fread(x.data(), sizeof(T), big_n, fp);
ISSUE AND ATTEMPTED SOLUTION
That previous solution works, but the constructor actually calls T's default constructor big_n times. This is very slow when big_n is really big (and totally unnecessary as I am about to fread() the entire chunk from disk). FWIW, in my test case for one file, it was taking 3 seconds instead of 200ms.
So I tried to use this instead:
std::vector<T> x;
x.reserve( big_n );
fread(x.data(), sizeof(T), big_n, fp);
This seems to work, but then I run into the issue that size() returns 0 and not big_n.
How do I correct this without losing too much efficiency?
ADDENDUM
I just noticed that std::vector<> can take a custom allocator. Could using that form of the constructor solve my problem? I'm looking into this approach now.
WHAT WORKS FOR ME
I've looked into Ali's custom allocator solution below in addition to jrok's simple array solution. I have decided to adapt jrock's solution for its ease-of-understanding/lower maintenance.
The working code I came up with is below:
#include <vector>
#include <set>
#include <memory>
#include <fstream>
#include <iostream>
#include <cassert>
struct Foo
{
int m_i;
Foo() { }
Foo( int i ) : m_i( i ) { }
bool operator==( Foo const& rhs ) const { return m_i==rhs.m_i; }
bool operator!=( Foo const& rhs ) const { return m_i!=rhs.m_i; }
friend std::ostream& operator<<( std::ostream& os, Foo const& rhs )
{ os << rhs.m_i; }
};
// DESIGN NOTES /*{{{*/
//
// LIMITATION T must be a POD so we can fread/fwrite quickly
//
// WHY DO WE NEED THIS CLASS?
//
// We want to write a large number of small PODs to disk and read them back without
// 1. spurious calls to default constructors by std::vector
// 2. writing to disk a gazillion times
//
// SOLUTION
// A hybrid class containing a std::vector<> for adding new items and a
// std::unique_ptr<T[]> for fast persistence. From the user's POV, it looks
// like a std::vector<>.
//
// Algorithm
// 1. add new items into:
// std::vector<T> m_v;
// 2. when writing to disk, write out m_v as a chunk
// 3. when reading from disk, read into m_chunk (m_v will start empty again)
// 4. m_chunk and m_v combined will represent all the data
/*}}}*/
template<typename T>
class vector_chunk
{
// STATE /*{{{*/
size_t m_n_in_chunk;
std::unique_ptr<T[]> m_chunk;
std::vector<T> m_v;
/*}}}*/
// CONSTRUCTOR, INITIALIZATION /*{{{*/
public:
vector_chunk() : m_n_in_chunk( 0 ) { }
/*}}}*/
// EQUALITY /*{{{*/
public:
bool operator==( vector_chunk const& rhs ) const
{
if ( rhs.size()!=size() )
return false;
for( size_t i=0; i<size(); ++i )
if ( operator[]( i )!=rhs[i] )
return false;
return true;
}
/*}}}*/
// OSTREAM /*{{{*/
public:
friend std::ostream& operator<<( std::ostream& os, vector_chunk const& rhs )
{
for( size_t i=0; i<rhs.m_n_in_chunk; ++i )
os << rhs.m_chunk[i] << "\n";
for( T const& t : rhs.m_v )
os << rhs.t << "\n";
}
/*}}}*/
// BINARY I/O /*{{{*/
public:
void write_as_binary( std::ostream& os ) const
{
// write everything out
size_t const n_total = size();
os.write( reinterpret_cast<const char*>( &n_total ), sizeof( n_total ));
os.write( reinterpret_cast<const char*>( &m_chunk[0] ), m_n_in_chunk * sizeof( T ));
os.write( reinterpret_cast<const char*>( m_v.data() ), m_v.size() * sizeof( T ));
}
void read_as_binary( std::istream& is )
{
// only read into m_chunk, clear m_v
is.read( reinterpret_cast<char*>( &m_n_in_chunk ), sizeof( m_n_in_chunk ));
m_chunk.reset( new T[ m_n_in_chunk ] );
is.read( reinterpret_cast<char*>( &m_chunk[0] ), m_n_in_chunk * sizeof( T ));
m_v.clear();
}
/*}}}*/
// DELEGATION to std::vector<T> /*{{{*/
public:
size_t size() const { return m_n_in_chunk + m_v.size(); }
void push_back( T const& value ) { m_v.push_back( value ); }
void push_back( T&& value ) { m_v.push_back( value ); }
template< class... Args >
void emplace_back( Args&&... args ) { m_v.emplace_back( args... ); }
typename std::vector<T>::const_reference
operator[]( size_t pos ) const
{ return ((pos < m_n_in_chunk) ? m_chunk[ pos ] : m_v[ pos - m_n_in_chunk]); }
typename std::vector<T>::reference
operator[]( size_t pos )
{ return ((pos < m_n_in_chunk) ? m_chunk[ pos ] : m_v[ pos - m_n_in_chunk]); }
/*}}}*/
};
int main()
{
size_t const n = 10;
vector_chunk<Foo> v, w;
for( int i=0; i<n; ++i )
v.emplace_back( Foo{ i } );
std::filebuf ofb, ifb;
std::unique_ptr<std::ostream> osp;
std::unique_ptr<std::istream> isp;
ofb.open( "/tmp/junk.bin", (std::ios::out | std::ios::binary));
osp.reset( new std::ostream( &ofb ));
v.write_as_binary( *osp );
ofb.close();
ifb.open( "/tmp/junk.bin", (std::ios::in | std::ios::binary));
isp.reset( new std::istream( &ifb ));
w.read_as_binary( *isp );
ifb.close();
assert( v==w );
}
Using vector::reserve() and then writing into vector::data() is a dirty hack and undefined behavior. Please don't do that.
The way to solve this problem is to use a custom allocator, such as the one in this answer. I have just tested it, works fine with clang 3.5 trunk but doesn't compile with gcc 4.7.2.
Although, as others have already pointed out, unique_ptr<T[]> will serve your needs just fine.
If you don't need the interface of the vector:
auto p = unique_ptr<T[]>{ new T[big_n] };
It won't initialize the array if T is POD, otherwise it calls default constructors (default-initialization).
In C++1y, you'll be able to use std::make_unique.
If using boost is an option for you, since version 1.55 boost::container::vector has had support for explicitly default-initializing elements when resizing using the syntax:
using namespace boost::container;
vector<T> vector(37283, default_init);
at creation or
using namespace boost::container;
vector.resize(37283, default_init);
after creation. This results in the nice syntax:
using T = unsigned; // but can be any trivially copyable type
FILE* fp = fopen( outfile.c_str(), "r" );
boost::container::vector<T> x(big_n, boost::container::default_init);
fread( x.data(), sizeof(T), big_n, fp );
fclose( fp );
In my tests performance is identical to using std::vector with a default-initializing allocator.
EDIT: Unrelated aside, I'd use an RAII wrapper for FILE*:
struct FILE_deleter {
void operator () (FILE* f) const {
if (f) fclose(f);
}
};
using FILE_ptr = std::unique_ptr<FILE, FILE_deleter>;
using T = unsigned; // but can be any trivially copyable type
FILE_ptr fp{fopen( outfile.c_str(), "r" )};
boost::container::vector<T> x(big_n, boost::container::default_init);
fread( x.data(), sizeof(T), big_n, fp.get() );
I'm a bit OCD about RAII.
EDIT 2: Another option, if you absolutely MUST produce a std::vector<T>, and not a boost::container::vector<T> or std::vector<T, default_allocator<T>>, is to fill your std::vector<T> from a custom iterator pair. Here's one way to make an fread iterator:
template <typename T>
class fread_iterator :
public boost::iterator_facade<fread_iterator<T>, T,
std::input_iterator_tag, T> {
friend boost::iterator_core_access;
bool equal(const fread_iterator& other) const {
return (file_ && feof(file_)) || n_ <= other.n_;
}
T dereference() const {
// is_trivially_copyable is sufficient, but libstdc++
// (for whatever reason) doesn't have that trait.
static_assert(std::is_pod<T>::value,
"Jabberwocky is killing user.");
T result;
fread(&result, sizeof(result), 1, file_);
return result;
}
void increment() { --n_; }
FILE* file_;
std::size_t n_;
public:
fread_iterator() : file_(nullptr), n_(0) {}
fread_iterator(FILE* file, std::size_t n) : file_(file), n_(n) {}
};
(I've used boost::iterator_facade to reduce the iterator boilerplate.) The idea here is that the compiler can elide the move construction of dereference's return value so that fread will read directly into the vector's memory buffer. It will likely be less efficient due to calling fread once per item vs. just once for the allocator modification methods, but nothing too terrible since (a) the file data is still only copied once from the stdio buffer into the vector, and (b) the whole point of buffering IO is so that granularity has less impact. You would fill the vector using its assign(iterator, iterator) member:
using T = unsigned; // but can be any trivially copyable type
FILE_ptr fp{fopen( outfile.c_str(), "r" )};
std::vector<T> x;
x.reserve(big_n);
x.assign(fread_iterator<T>{fp.get(), big_n}, fread_iterator<T>{});
Throwing it all together and testing side-by-side, this iterator method is about 10% slower than using the custom allocator method or boost::container::vector. The allocator and boost method have virtually identical performance.
Since you are upgrading to c++11, why not use file streams as well ? I just tried to read a 17 MB to a char* using ifstream & then write the contents to a file using ofstream.
I ran the same application in a loop 15 times and the maximum time it took is 320 ms and minimum is 120 ms.
std::unique_ptr<char []> ReadToEnd(const char* filename)
{
std::ifstream inpfile(filename, std::ios::in | std::ios::binary | std::ios::ate);
std::unique_ptr<char[]> ret;
if (inpfile.is_open())
{
auto sz = static_cast<size_t>(inpfile.tellg());
inpfile.seekg(std::ios::beg);
ret.reset(new char[sz + 1]);
ret[sz] = '\0';
inpfile.read(ret.get(), sz);
}
return ret;
}
int main(int argc, char* argv [])
{
auto data = ReadToEnd(argv[1]);
std::cout << "Num of characters in file:" << strlen(data.get()) << "\n";
std::ofstream outfile("output.txt");
outfile.write(data.get(), strlen(data.get()));
}
Output
D:\code\cpp\ConsoleApplication1\Release>ConsoleApplication1.exe d:\code\cpp\SampleApp\Release\output.txt
Num of characters in file:18805057
Time taken to read the file, d:\code\cpp\SampleApp\Release\output.txt:152.008 ms.