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?)
Related
I had a question on the Pramp platform. The task was to reverse words in a given string(a vector of chars). They expected a solution with no extra space, however the vector passed in the function was a 'const'.
vector<char> reverseWords(const vector<char>& arr )
So is there a way to modify a const vector? Or is there something that I don't get about const?
You need to use a new vector inside the function which uses the values from the const vector, and then returns that.
vector<char> reverseWords(const vector<char>& arr )
{
vector<char> newArr;
// Do stuff with arr and newArr
...
return newArr;
}
you can shoot yourself in foot via:
auto newVec = const_cast<vector<int>*>(&vec);
that means you are breaking your promise.
The right approach here as suggested by the others would be, to make your own copy and modify your copy.
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.
Why is this code wrong? Am I missing something regarding the behaviour of delete and delete[]?
void remove_stopwords(char** strings, int* length)
{
char** strings_new = new char*[*length];
int length_new = 0;
for(int i=0; i<*length; i++) {
if(is_common_keyword(strings[i]) == 0) {
strings_new[length_new] = strings[i];
length_new++;
}
else {
delete strings[i];
strings[i] = nullptr;
}
}
delete[] strings;
strings = new char*[length_new];
for(int i=0; i<length_new; i++) {
strings[i] = strings_new[i];
}
delete[] strings_new;
*length = length_new;
}
Explanations: this code should take an array of C-style strings and remove some particular strings of them; the array of C-style strings was created using new[] and every C-style string was created using new. The result of the code is that no word is canceled, but the array is only sliced.
I don't see any problem in the use of new[] or delete[] in the code shown.
No, wait.
I see a lot¹ of problems, but your intent is clear and the code seems doing what you want it to do.
The only logical problem I notice is that you're passing strings by value (it's a char** and reassigning it in the function will not affect the caller variable containing the pointer). Changing the signature to
void remove_stopwords(char**& strings, int* length)
so a reference is passed instead should fix it.
(1) Using std::vector<const char *> would seem more logical, even better an std::vector<std::string> if possible, that would take care of all allocations and deallocations.
every C-style string was created using new.
I suspect this is your problem -- C style strings are char arrays, so you can't readily create them with new, you need to use new[]. Which means you need to use delete[].
As #6502 pointed out, your basic problem is fairly simple: you're passing a char **, and attempting to modify it (not what it points at) in the function.
You're using that as a dynamically allocated array of strings, so what you're modifying is just the copy of the pointer that was passed into the function. Since you (apparently) want the function to modify what was passed into it, you need to either pass a char *** (ugh!) or char **& (still quite awful).
You really should use a vector<std::string> for the data. At least in my opinion, the code to remove the stop words should be written as a generic algorithm, something on this general order:
template <typename InIt, typename OutIt>
void remove_stop_words(InIt b, InIt e, OutIt d) {
std::remove_copy_if(b, e, d,
[](std:string const &s) { is_stop_word(s); });
}
With this, the calling code would look something like this:
// read input
std::vector<std::string> raw_input { std::istream_iterator<std::string>(infile),
std::istream_iterator<std::string>() };
// Filter out stop words:
std::vector<std::string> filtered_words;
remove_stop_words(raw_input.begin(), raw_input.end(),
std::back_inserter(filtered_words));
In a case like this, however, you don't really need to store the raw input words into a vector at all. You can pass an istream_iterator directly to remove_stop_words, and have it just produce the desired result:
std::ifstream in("raw_input.txt");
std::vector<std::string> filtered_words;
remove_stop_words(std::istream_iterator<std::string>(in),
std::istream_iterator<std::string>(),
std::back_inserter(filtered_words));
As an aside, you could also consider using a Boost filter_iterator instead. This would/will allow you to do the filtering in the iterator as you read the data rather than in an algorithm applied to the iterator.
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.
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.