Attaching char buffer to vector<char> in STL - c++

What is the correct (and efficient) way of attaching the contents of C buffer (char *) to the end of std::vector<char>?

When you have a vector<char> available, you're probably best calling the vector<char>::insert method:
std::vector<char> vec;
const char* values="values";
const char* end = values + strlen( values );
vec.insert( vec.end(), values, end );
Delegating it to the vector is to be preferred to using a back_inserter because the vector can then decide upon its final size. The back_inserter will only push_back, possibly causing more reallocations.

I think the proper way would be to
vec.insert(vec.end(),buf,buf+length);
or
std::copy(buf,buf+length,std::back_inserter(vec));
Edit: I reordered two examples, so it's not that commenters are wrong, it's just me ;-)

I haven't compiled it, but it should be something like:
const char string1[] = "a string";
std::vector<char> vData;
vData.insert(vData.end(), string1, string1+strlen(string1));

Related

Put bytes from unsigned char array to std::string using memcpy() function

I have std::string variable. And I need to put some bytes from array of unsigned chars to it. I know the first byte and the the legth.
I can use the std::string::assign function. I've done it.
But I want to solve that issue in a right way using the memcpy function.
std::string newString;
memcpy(&newString, &bytes[startIndex], length);
I know it is wrong. I have researched and found some ideas using std::vector.
Please help me to find the most elegant solution for this issue.
Since we're just constructing the string, there is a std::string constructor that takes two iterators:
template< class InputIt >
basic_string( InputIt first, InputIt last,
const Allocator& alloc = Allocator() );
which we can provide:
std::string newString(&bytes[startIndex], &bytes[startIndex] + length);
If we're not constructing the string and are instead assigning to an existing one, you should still prefer to use assign(). That is precisely what that function is for:
oldString.assign(&bytes[startIndex], &bytes[startIndex] + length);
But if you really insist on memcpy() for some reason, then you need to make sure that the string actually has sufficient data to be copied into. And then just copy into it using &str[0] as the destination address†:
oldString.resize(length); // make sure we have enough space!
memcpy(&oldString[0], &bytes[startIndex], length);
†Pre-C++11 there is technically no guarantee that strings are stored in memory contiguously, though in practice this was done anyway.
You need to set the size of the string so that there will be a properly sized buffer to receive the data, and cast the constness out of the pointer you get from data()
std::string newString;
newString.resize(length);
memcpy((char*)newString.data(), &bytes[startIndex], length);
of course all of this is in the realm of undefined behavior, but pretty standard non the less.
It's a hack and as you said wrong way but it is possible since STL guarantees that std::string has contiguous storage:
std::string str(32, '\0');
std::strcpy(const_cast<char*>(str.data()), "REALLY DUDE, IT'S ILLEGAL WAY");
Of course, you can use std::memcpy in the same way (I used strcpy just to copy null-terminated string)...
In your case:
str.resize(length);
memcpy(const_cast<char*>(str.data()), bytes + startIndex, length);

C++, conversion from string to char array

I would like to perform the above mentioned operation, however I would like to make sure that the char array is exactly the same size with the string at hand.
So the real question is, how can I make an array with a size that is going to be determined in the run time?
allocating memory on the free store, and copying the string in one go:
std::string s("abcdef");
...
char* chars=strdup(s.c_str());
You need to free the memory manually, of course. Documentation e.g. on the man page. As #Loki mentions: freeing this memory is done through free(chars), not through delete. Also, you need to include the <cstring> header.
If you want to stay in the c++ world, use a vector; it can be created with two iterators to copy it's data from, and will allocate on the heap, and will cleanup by itself. Isn't that a treat?
std::vector<char> vec( s.begin(), s.end() );
You can create an array of size known at runtime with the "new" operator:
char* res = new char[str.size()+1];
strncpy(res, str.c_str(), str.size()+1);
std::string s = "hello";
char* c = new char[s.length() + 1]; // '+ 1' is for trailing NULL character.
strcpy(c, s.c_str());
#include <string>
int main(int argc, char *argv[])
{
std::string random_data("This is a string");
char *array=new char[random_data.size()+1];
// do stuff
delete[] array;
return 0;
}
Try:
char* res = new char[str.size()+1](); // Note the () makes sure it is '0' filled.
std::copy(str.begin(), str.end(), res); // Don't need to copy the '\0' as underlying
// array already has '\0' at the end position.
...
delete [] res; // Must not forget to delete.
Or: (preferably)
std::vector<char> res(str.begin(), str.end());
Or: If all you want to do is call a C-unction:
str.c_str()
Use strlen() to find the length of the string, then malloc() a char array of that size.

How to efficiently copy a std::string into a vector

I have a string
std::string s = "Stack Overflow";
That I need to copy into a vector.
This is how I am doing it
std::vector<char> v;
v.reserve(s.length()+1);
for(std::string::const_iterator it = s.begin(); it != s.end(); ++it)
{
v.push_back( *it );
}
v.push_back( '\0' );
But I hear range operation are more efficient. So I am thinking something like this
std::vector<char> v( s.begin(), s.end());
v.push_back('\0');
But is this better in this case? What about the potential re-allocation when inserting '\0'?
Another approach I am thinking is this
std::vector<char> v(s.length()+1);
std::strcpy(&v[0],s.c_str());
Perhaps fast but potentially unsafe?
EDIT
Has to be a null terminated string that can be used ( read/write ) inside a C function
If you really need a vector (e.g. because your C function modifies the string content), then the following should give you what you want, in one line:
std::vector<char> v(s.c_str(), s.c_str() + s.length() + 1);
Since c_str() returns a null-terminated string, you can just copy it whole into the vector.
However, I’m not actually sure how optimised this constructor is. I do know that std::copy is as optimised as it gets, so perhaps (measure!) the following is faster:
std::vector<char> v(s.length() + 1);
std::copy(s.c_str(), s.c_str() + s.length() + 1, v.begin());
If the C function doesn’t modify the string, just pass c_str() directly, and cast away const-ness. This is safe, as long as the C function only reads from the string.
In most cases, you don't need vector of char, as std::string pretty much is a container of char. std::string also have begin and end functions. And it also have c_str() function which returns the c-string which you can pass to any function which expects const char*, such as this:
void f(const char* str); //c-function
std::string s="some string";
f(s.c_str());
So why would you ever need std::vector<char>?
In my opinion, vector<char> is a very very rare need but if I ever need it, I would probably write this:
std::vector<char> v(s.begin(), s.end());
And to me, v.push_back('\0') doesn't make much sense. There is no such requirement on vector to have the last element as '\0' if the value_type is char.
Alright, as you said, std::string::c_str() returns const char*, and the c-function needs a non-const char* , then you can use std::vector because you want to take advantage of RAII which vector implements:
void g(char* s); //c-function
std::vector<char> v(s.begin(), s.end());
s.push_back('\0');
g(&v[0]);
which seems fine to me. But RAII is all that you need, then you've other option as well:
{
std::vector<char> memory(s.size()+1);
char *str = &memory[0]; //gets the memory!
std::strcpy(str, s.c_str());
g(str);
//....
} //<--- memory is destroyed here.
Use std::strcpy, std::memcpy or std::copy whichever is fast, as I cannot say which one is necessarily fast, without profiling.
I don't think std::strcpy(&v[0],s.c_str()); is a good choice. I think c_str() is allowed to re-allocate.
If you somehow "need" the \0 for dealing with C-APIs, then rely on string::c_str() to provide it to you, on request. In dont think that you need to put it into a vector-of-char, most things you can do with the string itself like with a vector.
Update:
If you make sure your vector gets initialized with 0s, you can circumvent the call to c_str by using strncopy:
std::vector<char> v(s.length()+1, 0); // added '0'
std::strncpy(&v[0],&s[0],s.length()); // no c_str()

Reading raw byte array into std::string

I've been wondering about the following issue: assume I have a C style function that reads raw data into a buffer
int recv_n(int handle, void* buf, size_t len);
Can I read the data directly into an std:string or stringstream without allocating any temporal buffers? For example,
std::string s(100, '\0');
recv_n(handle, s.data(), 100);
I guess this solution has an undefined outcome, because, afaik, string::c_str and string::data might return a temporal location and not necessarily return the pointer to the real place in the memory, used by the object to store the data.
Any ideas?
Why not use a vector<char> instead of a string? That way you can do:
vector<char> v(100, '\0');
recv_n(handle, &v[0], 100);
This seems more idiomatic to me, especially since you aren't using it as a string (you say it's raw data).
Yes, after C++11.
But you cant use s.data() as it returns a char const*
Try:
std::string s(100, '\0');
recv_n(handle, &s[0], 100);
Depending on situation, I may have chosen a std::vector<char> especially for raw data (though it would all depend on usage of the data in your application).

Converting a C-string to a std::vector<byte> in an efficient way

I want to convert a C-style string into a byte-vector.
A working solution would be converting each character manually and pushing it on the vector. However, I'm not satisfied with this solution and want to find a more elegant way.
One of my attempts was the following:
std::vector<byte> myVector;
&myVector[0] = (byte)"MyString";
which bugs and gets me an
error C2106: '=': left operand must be l-value
What is the correct way to do this?
The most basic thing would be something like:
const char *cstr = "bla"
std::vector<char> vec(cstr, cstr + strlen(cstr));
Of course, don't calculate the length if you know it.
The more common solution is to use the std::string class:
const char *cstr;
std::string str = cstr;
STL containers such as vector always take ownership, i.e. they manage their own memory. You cannot modify the memory managed internally by an STL container. For that reason, your attempt (and any similar attempt) is doomed to failure.
The only valid solution is to copy the memory or to write a new STL-style container that doesn’t take ownership of the memory it accesses.
Something along these lines should work
std::vector<byte> myVector = std::vector((byte*)cstring, (byte*)ctring + strlen(cstring))
Also, this is still going to just iterate through the c string and insert the values into the vector. Like Konrad said, that's just how you have to do it since vectors manage their own memory.
In case you would not like to copy the original string and would just like to iterate over it as an array of bytes, then C++20 has std::span to offer.
auto const const_bytes = std::as_bytes(std::span{str.data(), str.size()});
std::span<const std::byte> provides std::vector like iterating capabilities which I think is what you might be looking for.
Note: The original string would have to remain valid for the entire scope of std::span variable
std::vector<byte> myVector;
const char* str = "MyString";
myVector.assign( str, str+strlen(str) );
The most obvious question would be why you don't just use std::string:
std::string myString("MyString");
but if you really think you need a vector:
char myString[] = "MyString";
std::vector<byte> myVector(myString, myString+sizeof(myString));
You might also want to consider using std::tr1::array:
std::tr1::array<byte, sizeof("MyString")> myArray = {"MyString"};
C++ 0x will also have std::array.
const char *cstr = "bla"
std::vector<char> vec;
vec.resize(strlen(cstr)+1);
strcpy(&vec[0],cstr);