I am using a library which accepts data as a vector of chars. I need to pass a string to the library.
I think about using std::vector constructor which accepts iterators to carry out the conversion - but wondered if there is a better way of doing it?
/*Note: json_str is of type std::string*/
const std::vector<char> charvect(json_str.begin(), json_str.end());
Nope, that's the way to do it, directly initializing the vector with the data from the string.
As #ildjarn points out in his comment, if for whatever reason your data buffer needs to be null-terminated, you need to explicitly add it with charvect.push_back('\0').
Also note, if you want to reuse the buffer, use the assign member function which takes iterators.
Your method of populating the vector is fine -- in fact, it's probably best in most cases.
Just so that you know however, it's not the only way. You could also simply copy the contents of the string in to the vector<char>. This is going to be most useful when you either have a vector already instantiated, or if you want to append more data to the end -- or at any point, really.
Example, where s is a std::string and v is a std::vector<char>:
std::copy( s.begin(), s.end(), std::back_inserter(v));
As with the constructor case, if you need a null-terminator then you'll need to push that back yourself:
v.push_back('\0');
You can do it in this way.
std::string s = "Hello World!";
std::vector<char> v(s.begin(), s.end());
for (const char &c: v)
std::cout << c;
An alternative that might be worth considering if you need the terminating null is:
std::vector<char> charvect(json_str.c_str(), json_str.c_str() + json_str.size() + 1);
and if charvect already exists you can do:
charvect.assign(json_str.c_str(), json_str.c_str() + json_str.size() + 1);
This might be quicker than doing a push_back('\0') particularly if the push_back triggers a memory reallocation.
Related
More specifically I have a vector of some struct
std::vector<SomeStruct> extensions = getThoseExtensions();
where someStructVariable.extensionName returns a string.
And I want to create a set of extensionName, something like this std::set<const char*>.
Process is fairly straightforward when done using some for loops but I want to use std::transform from <algorithm> instead.
std::transform has four parameters.
1,2. First range (to transform first range from and to)
3. Second range/inserter (to transform second range)
4. A function
This is what I have so far
auto lambdaFn =
[](SomeStruct x) -> const char* { return x.extensionName; };
std::transform(availableExtensions.begin(),
availableExtensions.end(),
std::inserter(xs, xs.begin()),
lambdaFn);
because there's no "proper context" for std::back_inserter in std::set I'm using std::inserter(xs, xs.begin()).
The problem is I'm trying to return stack mem in my lambda function. So how do I get around this problem?
Oddly enough if I remove return from the function it works just like I expect it to! But I don't understand why and that strikes fear of future repercussion.
EDIT:
I'm using several structs in place of SomeStruct like VkExtensionProperties defined in vulkan_core
typedef struct VkExtensionProperties {
char extensionName[VK_MAX_EXTENSION_NAME_SIZE];
uint32_t specVersion;
} VkExtensionProperties;
From Khronos specs
You probably can't create a set of char * unless all instances of extensionName with the same value point to the same char array (it would store unique pointers instead of unique values). If you use std::set<std::string> instead this will both work and only store unique values and solve your variable lifetime problem as std::string takes care of copying (or moving) itself for you where necessary:
auto lambdaFn =
[](const SomeStruct& x) { return std::string(x.extensionName); };
std::set<std::string> xs;
std::transform(availableExtensions.begin(),
availableExtensions.end(),
std::inserter(xs, xs.begin()),
lambdaFn);
One way to do what you want is with the following lambda
auto lambda = [](const SomeStruct& x) -> const char* { return x.extensions.data();};
The problem with this is, that you are saving pointers to memory owned by someone else (those strings). When they are destroyed (this seems to be the case at the end of the function), the pointer will be dangling. You can get around this by allocating memory in your lambda and copying the data:
auto lambda = [](const SomeStruct & x) -> const char* {
char* c = new char[x.extensions.length()+1];
std::strcpy(c, x.extensions.data());
return c;
}
But then you have to do memory management yourself (i.e. remember to free those const char*). And that is a bad idea. You should probably reconsider what you are doing. Why are you using const char* here and not std:: string?
Please remember that the typical use for const char* is to save string literals i C-code, i.e. the code
const char* str = "Hello World!";
creates a char array of sufficient size in the static section of the memory, initializes it with the string (a compile time constant) and then saves a pointer to that in str. This is also why this has to be a const char* since another pointer refering to an equal string literal may (or may not) point the exactly the same char array and you don't want to enable change there. So don't just use const char* because you see strings in C saved in those const char* without anyone needing to free them later.
There are a couple of things you can do here.
If you own the definition of SomeStruct, it is best if you changed that member to std::string.
Short of that, see if you lambda can take by-ref parameter const auto& obj. This will not create a copy and point back to the object the container has. However, I am still afraid of this solution since this smells like bad class design where ownership and lifetime of members is ambiguous.
I've tried to copy like below.
I met crash, though.
Isn't it possible to use the assignment operator at this time?
std::vector<std::string> string_list;
std::string str;
string_list[0] = "abc";
str = string_list[0];
Your vector is empty, therefore attempting to access the first element will result in a crash. You can initialize a vector like so:
std::vector<std::string> list = {"my string"};
std::string s = list[0];
Or if you prefer to dynamically grow the list you can do:
std::vector<std::string> list;
list.push_back("my string");
std::string s = list[0];
Your vector of strings string_list
std::vector<std::string> string_list;
is empty, i.e., it contains no element, no string at all, size() on that std::vector will return zero.
Then, in the statement:
string_list[0] = "abc";
You are writing to an element of the vector that does not exist and that results in undefined behavior (in your case, the program crashes).
Try initializing the vector in the following way instead:
std::vector<std::string> string_list(1);
This way, the vector string_list will contain a single empty string (i.e., a default constructed std::string object), which you can access using the operator[] (as you already did) on your vector of strings string_list:
string_list[0] = "abc";
By doing this, you are using the assignment operator of that empty string.
Since, your vector is empty, you might get a crash when you attempt to access string_list[0] because you are trying to alter/access something that isn't there in the first place.
You can take care of it in two ways :
std::vector<std::string> string_list(10) Mention a non-zero size while declaring the vector( such as 10 ). That way the vector of strings will consists of 10 empty strings inside the vector.
Use string_list.push_back("abc"). This will dynamically allocate memory for your vector even if you don't explicitly declare the vector with a given size.
Option 1 :
std::vector<std::string> string_list(10);
string_list[0] = "abc"; // This is okay.
Options 2 :
std::vector<std::string> string_list; // Note that no size mentioned here.
string_list.push_back("abc"); //This is okay as well. :)
I am trying to convert a string to a const*char* in order to be able to call a library function. My code is as follows:
// myVec is simply a vector<string>
vector<string> myVec;
/* stuff added to myVec
* it is a vector of words that were seperated by whitespace
* for example myVec[0]=="Hey"; myVec[1]=="Buck"; myVec[2]=="Rogers"; etc...
*/
char*const* myT = new char*[500]; //I believe my problem stems from here
for(int z=0; z<myVec.size(); z++) {
string temp=myVec[z]+=" ";
myT[z]=temp.c_str();
}
//execv call here
I am constructing this for the second parameter of execv().
Compiler always throws various errors, and when I fix one another one pops up (seems rather circular from the solutions/google-fu I have employed).
The signature of execv expects the array of arguments to point to modifyable C style strings. So contrary to what the other answers suggest, c_str() is not such a good idea.
While not guaranteed in C++03, the fact is that all implementations of std::string that I know of store the data in a contiguous NULL terminated block of memory (this is guaranteed in C++11), so you can use that to your advantage: Create a vector of pointers to modifiable character arrays, initialize the values with the buffers for the strings in your input vector and pass the address of that block of data to execv:
std::vector<char*> args;
args.reserve(myVec.size()+1);
for (std::vector<std::string>::iterator it=myVec.begin(); it != myVec.end(); ++it) {
args.push_back(&((*it)[0]);
}
args.push_back(0); // remember the null termination:
execv("prog", &args[0]);
There are two fundamental problems which need addressing. The
first is a compiler error: the pointers in the array pointed to
by myT are const, so you cannot assign to them. Make myT
char const** myT;. The second problem is that what you are
assigning to them is a pointer into a local variable, which
will be destructed when it goes out of scope, so the pointers
will dangle.
Does the function you are calling really need the extra white
space at the end? (You mentioned execv somewhere, I think.
If that's the function, the extra whitespace will do more harm
than good.) If not, all you have to do is:
std::vector<char const*> myT( myVec.size() + 1 );
std::transform( myVec.begin(), myVec.end(), myT.begin(),
[]( std::string const& arg ) { return arg.c_str(); } );
execv( programPath, &myT[0] );
If you can't count on C++11 (which is still usually the case),
you can probably do something similar with boost::bind;
otherwise, just write the loop yourself.
If you do need to transform the strings in myVec in some way,
the best solution is still to copy them into a second
std::vector<std::string>, with the transformation, and use
this.
(BTW: do you really want to modify the contents of myVec, by
using += on each element in the loop?)
I was searching about this topic and I found many ways to convert an array[] to an std::vector, like using:
assign(a, a + n)
or, direct in the constructor:
std::vector<unsigned char> v ( a, a + n );
Those solve my problem, but I am wondering if it is possible (and correct) to do:
myvet.resize( 10 );
memcpy( &myvet[0], buffer, 10 );
I am wondering this because I have the following code:
IDiskAccess::ERetRead nsDisks::DiskAccess::Read( std::vector< uint8_t >& bufferRead, int32_t totalToRead )
{
uint8_t* data = new uint8_t[totalToRead];
DWORD totalRead;
ReadFile( mhFile, data, totalToRead, &totalRead, NULL );
bufferRead.resize( totalRead );
bufferRead.assign( data, data + totalRead );
delete[] data;
return IDiskAccess::READ_OK;
}
And I would like to do:
IDiskAccess::ERetRead nsDisks::DiskAccess::Read( std::vector< uint8_t >& bufferRead, int32_t totalToRead )
{
bufferRead.resize( totalToRead );
DWORD totalRead;
ReadFile( mhFile, &bufferRead[0], totalToRead, &totalRead, NULL );
bufferRead.resize( totalRead );
return IDiskAccess::READ_OK;
}
(I have removed the error treatment of the ReadFile function to simplify the post).
It is working, but I am affraid that it is not safe. I believe it is ok, as the memory used by the vector is continuous, but I've never seen someone using vectors this way.
Is it correct to use vectors like this? Is there any other better option?
Yes it is safe with std::vector C++ standard guarantees that the elements will be stored at contiguous memory locations.
C++11 Standard:
23.3.6.1 Class templatevector overview [vector.overview]
A vector is a sequence container that supports random access iterators. In addition,itsupports(amortized) constant time insert and erase operations at the end; insert and erase in the middle take linear time. Storage management is handled automatically, though hints can be given to improve efficiency. The elements of a vector are stored contiguously, meaning that ifv is avector whereT is some type other than bool, then it obeys the identity&v[n] == &v[0] + n for all0 <= n < v.size().
Yes, it is fine to do that. You might want to do myvet.data() instead of &myvet[0] if it looks better to you, but they both have the same effect. Also, if circumstances permit, you can use std::copy instead and have more type-safety and all those other C++ standard library goodies.
The storage that a vector uses is guaranteed to be contiguous, which makes it suitable for use as a buffer or with other functions.
Make sure that you don't modify the vector (such as calling push_back on it, etc) while you are using the pointer you get from data or &v[0] because the vector could resize its buffer on one of those operations and invalidate the pointer.
That approach is correct, it only depends on the vector having contiguous memory which is required by the standard. I believe that in c++11 there is a new data() member function in vectors that returns a pointer to the buffer. Also note that in the case of `memcpy you need to pass the size in bytes not e size of the array
The memory in vector is guaranteed to be allocated contiguously, and unsigned char is POD, therefore it is totally safe to memcpy into it (assuming you don't copy more than you have allocated, of course).
Do your resize first, and it should work fine.
vector<int> v;
v.resize(100);
memcpy(&v[0], someArrayOfSize100, 100 * sizeof(int));
Yes, the solution using memcpy is correct; the buffer held by a vector is contiguous. But it's not quite type-safe, so prefer assign or std::copy.
suppose I declare a dynamic array like
int *dynArray = new int [1];
which is initialized with an unknown amount of int values at some point.
How would I iterate till the end of my array of unknown size?
Also, if it read a blank space would its corresponding position in the array end up junked?
Copying Input From users post below:
Thing is:
a) I'm not allowed to use STL (means: no )
b) I want to decompose a string into its characters and store them. So far I wanted to use a function like this:
string breakLine (string line){
int lineSize = line.size();
const char *aux;
aux=line.data();
int index=0;
while (index<=lineSize){
mySynonyms[index]=aux[index];
index++;
}
I thought that the array aux would end up junked if there was a large blank space between the two numbers to be stored (apparently not). And I was wondering if there was a way to iterate till an undefined end in this type of array. Thanks for you answers.
You don't: wrap the array into a structure that remembers its length: std::vector.
std::vector v(1);
std::for_each( v.begin(), v.end(), ... );
No portable way of doing this. Either pass the size together with the array, or, better, use a standard container such as std::vector
Short answer is that you can't. If you have a pointer to the first element of an array, you can't know what the size of the array is. Why do you want to use a array in the first place. You would be much better off using a std::vector if your array can change size dynamically, or a boost::Array if it will be a fixed size.
I don't understand your second question.
Your code needs to keep to track of the array, so the size would never be unknown. (Or you would have to use some library with code that does this.)
I don't understand the last part of your quesiton. Could you elaborate?
You explained in your post below that you want to look at the guts of a std::string.
If you are expecting your stirng to be like a c-string (aka doesn't contain NULLs), then use line.c_str() instead of line.data(). This will guarantee that aux points to a null terminates c-style string.
After that you can iterate until aux[index] == '\0';
Otherwise, you can use line.data() and string.length/size to get it's size like in your example.
However, "decomposing a string into its characters" is pretty pointless, a string is an array of characters. Just make of copy of the string and store that. You are allowed to do:
char ch = line[index];
Better yet, use iterators on the original string!
for(std::string::const_iterator it = line.begin(); it != line.end(); ++it) {
const char ch = *it;
// do whatever with ch
}
a) I'm not allowed to use STL (means:
no )
What?? Who's moronic idea was that?
std::vector isn't part of the "STL" (which is a copyrighted product of HP), but is (and has been for nearly a decade) part of the C++ Language Standard.
If you're not allowed to use the STL (for whatever reason), the first thing you want to do is actually to implement your own version of it – at least the parts you need, with the level of customizability you need. For example, it's probably overkill to make your own vector class parametrizable with a custom allocator. But nevertheless do implement your own lightweight vector. Everything else will result in a bad, hardly maintainable solution.
This smells like homework, and the teacher's objective is to give you a feeling of what it takes to implement dynamic arrays. So far you're getting an F.
You need to realize that when you allocate memory like this
int *dynArray = new int [1];
you allocate precisely one integer, not an indefinite number of integers to be expanded by some unidentified magic. Most importantly, you can only say
dynArray[0] = 78;
but you cannot say
dynArray[1] = 8973;
The element at index 1 does not exist, you're stepping into memory that was not reserved for you. This particular violation will result in a crash later on, when you deallocate the array, because the memory where you stored 8973 belongs to the heap management data structures, and you corrupted your heap.
As many other responders mention, you must know how many elements you have in the array at all times. So, you have to do something along the lines of
int arraySize = 1;
int *dynArray = new int [arraySize];
arraySize goes together with the array, and is best combined with dynArray in one C++ object.
Now, before you assign to dynarray[1], you have to re-allocate the array:
if (index > arraySize) {
int newSize = index+1;
int *newArray = new int[newSize]
// don't forget to copy the data from old array to new
memcpy(newarray dynArray, sizeof *newArray * arraySize);
arraySize = newSize;
dynArray = newArray;
}
// now you're ready!
dynArray[index] = value;
Now, if you want to make it a bit more efficient, you allocate more than you need, so you don't have to allocate each time you add an element. I'll leave this as an exercise to the reader.
And after doing all this, you get to submit your homework and you get to appreciate the humble std::vector that does all of this for you, plus a lot more.
Use a vector, which has a vector.size() function that returns an integer and a vector.end() function that returns an iterator.
You could create a simple Vector class that has only the methods you need. I actually had to recreate the Vector class for a class that I took this year, it's not very difficult.
If there's a value that cannot be valid, you can use that as a sentinel, and make sure all of your arrays are terminated with that. Of course, it's error-prone and will cause hard-to-find bugs when you happen to miss doing it once, but that's what we used to do while reading files in FORTRAN (back in the all-caps days, and before END= became standard).
Yes, I'm dating myself.