Converting std::string from const_iterator to char*? - c++

I have an iterator of type:
std::map<int, std::string>::const_iterator
and as you can see the map is storing std::strings.
I am trying to extract a value using:
x.second.c_str()
and pass it to a function to call the regex.h function regexec(), which accepts a char*. What is the best way to do this? I am getting basic_string compilation errors from this approach.
Update:
std::map<int, std::string> map;
.
.
.
std::map<int, std::string>::const_iterator f = map.find("something");
if (f != map.end()) {
my_func(f->second.c_str());
.
.
.
.
void my_func(char* c){
std::cout << c << std::endl; //This causes a segmentation fault
}

You should do
int error=regexec(&regex,cppstring.c_str(),nmatch,pmatch,eflags);
The second argument of regexec() and the return value of std::string::c_str() are both (const char *), so that's probably not the cause of your issue.
You should post the compiler error message and the part of the code that you suspect to be causing the problem.

Here's a very wild guess, though I could be mislead by the contradictory and incomplete information given:
I assume you have a function that takes a char* as parameter, e.g. like the my_func provided in the question.
I further assume you try to dereference a const_iterator of some kind, ultimately getting a reference to a const string, and call c_str() on it.
The next assumption is that you want to pass the result of that call to your function.
That would look somewhat like this:
void my_func(char*);
int main() {
std::map<int, std::string> theMap;
// ...
std::map<int, std::string>::const_iterator cit = theMap.find(3);
assert(cit != theMap.end());
my_func(cit->second.c_str()); //problematic line
}
In this case, you should get a compilation error, because c_str() returns a const char*. while your function expects a char*, and the two are not compatible.
However, my assumptions have several flaws:
The error should be about the conversion from const char* to char* and should not have to do anything with basic_string
It has nothing to do with regexec (but neither has your example code)
It cannot lead to segfaults, since it does not compile (but that contradiction is already present in your question)
If you shed some more light on the question, I will be able to give more specific hints on what is going wrong.

Related

Is there a way to use operator+ to concatenate std::strings in c++? [duplicate]

This question already has answers here:
What is the difference between these two cases of adding a string?
(5 answers)
Closed 7 years ago.
I have a function like this
void foo (const char* myString){
...
}
and I want to use it like this
std::string tail = "something";
foo("my" + "string" + tail);
But I just can't find an easy way. For sure I can make the string somewhere else and pass it to foo(). But I prefer to find a way to do it inline, because foo() is called several times in the code, I don't want to make a string for each time. I tried
foo(std::string ("my" + "string" + tail).c_str())
but you can guess that it doesn't work.
"my"and "string" are C style string literals, which have the odd rule that they will be concatenated if they are written without a +.
So "my" "string" + tail will work, and produce a std::string. However, it will still not be the correct type for your function, unless you use .c_str() on the result.
"my" and "string" are C-style strings. Their types are const char *, you cannot use operator + for these operands. But you can use this operator is any of the operands is string.
So the most elegant way for you it to add parenthesis:
foo(("my" + ("string" + tail)).c_str());
You will also have to change function foo to
void foo (const char* myString)
Just make sure "my" is a std::string, then you can use the std::string::operator+ operator on it. Later you use .c_str() on the resulting std::string.
Then, if you can change foo, the best is to make it accept const char*:
void foo (const char* myString)
{
...
}
std::string tail = "something";
foo( (std::string("my") + "string" + tail).c_str() );
If you can't change foo, then you'll have to do a cast because c_str() returns a const char* and foo wants a char*:
void foo (char* myString)
{
...
}
std::string tail = "something";
foo( const_cast<char*>( (std::string("my") + "string" + tail).c_str() ) );
Your function accepts a modifiable array (not anymore, OP changed that in an edit) of char and std::string is not an array of char, but some unrelated object (that provides read-access to its internal char buffer, but that does not make it some kind of pretty array).
Additionally, using .c_str()-pointers into destroyed string objects is a common bug. Even if your function was to accept a const char* instead, you need to be aware that the pointer passed into it would only be valid until the end of the full expression the temporary std::string object was created in. This might or might not be what you want here, but is something you really need to watch out for. As I said, people get it wrong quite often.
So std::string probably (in the new const char* setting, it might) is not the right tool for this job as it is described right now.
The best solution would be to make the argument of foo() an std::string (of some reference variant, depending on what it is doing). Then you can concatenate the inputs with + as long as one of the first summands already is an std::string.
If this should not be possible, copy the characters into an std::vector<char> which actually is the C++ way to get a char array (again, unlike string).
The code foo("my" + "string" + tail); does not work for two reasons. First is order of operators. The code tries to execute "my" + "string", and since both of those are string literals, code fails. (you can not add up two string literals). The first issue is that if you magically make "my" + "string" working, code will concatenate it with tail, produce valid std::string, but fail to pass it to foo().
How to fix the issue?
Change foo() signature. make it foo(const char* ) if you have to use char*, or, better, replace it with foo(const std::string&).
Use following to concatenate: foo(std::string("my") + "string" + tail) if you followed my advice and made foo accepting const std::string&, or foo((std::string("my") + "string" + tail).c_str()) if you did not.
On a side note, since both "my" and "string" are known at compile time, it's better to have simple foo("mystring" + tail) - easier to read, better performance.

Error trying to find const char* key from std::map

I have a map declared like this:
std::map<const char*, const char*> map2D;
The map is filled by the output from an API function, which returns const char*:
map2D.insert(std::pair<const char*, const char*>(TempA, TempB));
Now there are 50 TempA values and 50 TempB values, and there is one key with the name of "months". When I am searching for this key, I am getting "not found". E.g.:
std::map<const char*, const char*>::iterator it;
it = map2D.find("months");
if (it != map2D.end())
{
std::cout << "Found " << it->first << " " << it->second << '\n';
}
else
{
std::cout << "Not found\n";
}
But when I am doing it like this:
map2D.insert(std::pair<const char*, const char*>("months", "June");
I can find the respective month. After searching the web, I understand that this problem may be related to the use of const char*. Can anyone further clarify this?
Comparing two const char* for equality does not do what you think it does; it compares pointer values, not the strings that the pointers point to. With string literals this may occasionally "work", but you have no way of knowing that two string literals even with the same characters in it will be stored at the same address. You would have to provide a custom comparator that invokes strcmp, in order to make that work reliably.
You're much better off with a std::map<std::string, std::string>. It doesn't matter that your third-party API gives you const char*: you can simply construct std::strings from those.
This container will have elements with clear ownership and lifetime semantics, and be automatically ordered properly. In short, all your problems will simply go away.
If you still really need to store const char*, be aware that such a requirement is exceedingly rare and should only be even fleetingly considered if you are prepared to litter your code with explanatory comments detailing precisely how and why your container is safe; hint: it almost certainly is not.
The built-in comparison operator for const char* compares the pointer address, not the strings it points to:
std::map<const char*, const char*> map2D; //don't use that!
map2D.emplace("a", "b");
//...
auto key = std::string{"a"};
assert(map2D.find(key.c_str()) == map2D.end()); //not found
If you create a map from string literals (which you can assume the lifetime of a program) use C++17 std::string_view instead of const char* as the key. This class has custom operator= which does the job in a way you'd expect when using algorithms:
std::map<std::string_view, const char*> map2D;
map2D.emplace("a", "b");
//...
auto key = std::string{"a"};
assert(map2D.find(key)->second == std::string_view{"b"});
On the other hand, if you can't say nothing about the lifetime of the string pointed by const char*, use std::string which will make a deep copy. It is less-performant but more general solution:
std::map<std::string, const char*> map2D;
map2D.emplace("a", "b");
//...
auto key = std::string{"a"};
assert(map2D.find(key)->second == std::string_view{"b"});

how to define an array of chars in c++

I have this code and it's compiling correctly :
char classfname[512] = "classifiers/cabmodel_VOC05motorbikes.xml";
strcpy(classfname,argv[i]);
but when I tried to define an array contains strings from the same size of the above size
and with your all help it didn't work !
std::vector<std::string> classfname = {
"classifiers/cabmodel_VOC05motorbikes.xml",
"classifiers/cabmodel_interm_nst100_VOC06person01train5_orienthistmod.xml" ,
"classifiers/cabmodel_interm_nst40_VOC06cat01train5_trainval_orienthistmod_nopert_facereg.xml",
"classifiers/cabmodel_interm_nst100_VOC06bicycle01train5_trainval_orienthistmod.xml",
"classifiers/cabmodel_VOC06carside.xml",
"classifiers/cabmodel_interm_nst100_VOC06horse01train5_trainval_orienthistmod_randsel0100.xml"
};
char *classfname[6]={-----}
std::vector<std::string> classfname;
classfname.push_back(",,,");
with the function strcpy(classfname,argv[i]);
I got the error:
Error 2 error C2664: 'strcpy' : cannot convert parameter 1 from 'std::string' to 'char *
Converting string literals to a char* is no longer allowed, since it was never safe. Instead, make an array of const char*. (Although I'm not 100% positive this is the cause of your error, but your code doesn't match your error well, I think you changed something to put it on SO). std::string has a constructor from const char*, so this should work fine.
Also, it's good to note that (const::std string & is not right, so we know you changed stuff when you posted it here. Don't do that, or we can't help you much at all. It should be (const std::string&.
Also, MrC64 notes that you should use RAII instead of raw arrays and pointers. It's easier to use, and harder to mess up.
std::vector<std::string> classfname = {
"classifiers/cabmodel_VOC05motorbikes.xml",
"classifiers/cabmodel_interm_nst100_VOC06person01train5_orienthistmod.xml" ,
"classifiers/cabmodel_interm_nst40_VOC06cat01train5_trainval_orienthistmod_nopert_facereg.xml",
"classifiers/cabmodel_interm_nst100_VOC06bicycle01train5_trainval_orienthistmod.xml",
"classifiers/cabmodel_VOC06carside.xml",
"classifiers/cabmodel_interm_nst100_VOC06horse01train5_trainval_orienthistmod_randsel0100.xml"
};
If your compiler can't handle that syntax yet (many can't), use the code that Mr_C64 suggested.
[EDIT] You have changed your question dramatically to be a completely different question. Generally this is bad, because anyone who comes to this page looking for answers will see that our answers don't match your question anymore. If you have additional questions, you should use the search feature, or make a new question page.
Now your code has a std::vector of std::strings. Treat a std::string like you would an int. Just copy it, or pass it around with no worries. You don't have do use a special function to copy a int, so you don't need a special function to copy a string. Just do std::string newstring = classfname[0]; to get a copy of the string at index 0 in the array classfname.
Your "old" code makes an array of chars initialized to a string literal, and over-rights it with the input from argv[i] The best way to do that code is:
std::string classfname = "classifiers/cabmodel_VOC05motorbikes.xml";
classfname = argv[i];
If you just want to make an array of each of the arguments, that's easy:
int main() {int argc, const char** argv) {
std::vector<std::string> classfname(argv, argv+argc);
One solution is to use const char* like this:
const char *classfname[7]={"classifiers/cabmodel_VOC05motorbikes.xml",
"classifiers/cabmodel_interm_nst100_VOC06person01train5_orienthistmod.xml" ,
"classifiers/cabmodel_interm_nst40_VOC06cat01train5_trainval_orienthistmod_nopert_facereg.xml",
"classifiers/cabmodel_interm_nst100_VOC06bicycle01train5_trainval_orienthistmod.xml",
"classifiers/cabmodel_VOC06carside.xml",
"classifiers/cabmodel_interm_nst100_VOC06horse01train5_trainval_orienthistmod_randsel0100.xml",
};
Also if you want to have a std::vector containing these strings use can initialize it with the following statement:
const std::vector<std::string> classfname_vector(classfname, classfname + 7);
One more thing I noticed is that you declared an array with 7 elements but initialized it only with 6 string literals.
I'd just use std::vector<std::string> instead of a "raw" C array:
#include <string>
#include <vector>
std::vector<std::string> classfname;
classfname.push_back("classifiers/cabmodel_VOC05motorbikes.xml");
classfname.push_back("classifiers/cabmodel_interm_nst100_VOC06person01train5_orienthistmod.xml");
...
std::vector overloads operator[], so your call xmlloadmodel(classfname[i],model); should work.

How to pass a vector of strings to execv

I have found that the easiest way to build my program argument list is as a vector of strings. However, execv expects an array of chars for the second argument. What's the easiest way to get it to accept of vector of strings?
execv() accepts only an array of string pointers. There is no way to get it to accept anything else. It is a standard interface, callable from every hosted language, not just C++.
I have tested compiling this:
std::vector<string> vector;
const char *programname = "abc";
const char **argv = new const char* [vector.size()+2]; // extra room for program name and sentinel
argv [0] = programname; // by convention, argv[0] is program name
for (int j = 0; j < vector.size()+1; ++j) // copy args
argv [j+1] = vector[j] .c_str();
argv [vector.size()+1] = NULL; // end of arguments sentinel is NULL
execv (programname, (char **)argv);
The prototype for execv is:
int execv(const char *path, char *const argv[]);
That means the argument list is an array of pointers to null-terminated c strings.
You have vector<string>. Find out the size of that vector and make an array of pointers to char. Then loop through the vector and for each string in the vector set the corresponding element of the array to point to it.
I stumbled over the same problem a while ago.
I ended up building the argument list in a std::basic_string<char const*>. Then I called the c_str() method and did a const_cast<char* const*> on the result to obtain the list in a format that execv accepts.
For composed arguments, I newed strings (ordinary strings made of ordinary chars ;) ), took their c_str() and let them leak.
The const_cast is necessary to remove an additional const as the c_str() method of the given string type returns a char const* const* iirc. Typing this, I think I could have used std::basic_string<char*> but I guess I had a reason...
I am well aware that the const-casting and memory leaking looks a bit rude and is indeed bad practise, but since execv replaces the whole process it won't matter anyway.
Yes, it can be done pretty cleanly by taking advantage of the internal array that vectors use. Best to not use C++ strings in the vector, and const_cast string literals and string.c_str()'s to char*.
This will work, since the standard guarantees its elements are stored contiguously (see https://stackoverflow.com/a/2923290/383983)
#include <unistd.h>
#include <vector>
using std::vector;
int main() {
vector<const char*> command;
// do a push_back for the command, then each of the arguments
command.push_back("echo");
command.push_back("testing");
command.push_back("1");
command.push_back("2");
command.push_back("3");
// push NULL to the end of the vector (execvp expects NULL as last element)
command.push_back(NULL);
// pass the vector's internal array to execvp
execvp(command[0], const_cast<char* const*>(command.data()));
return 1;
}
Code adapted from: How to pass a vector to execvp
Do a const_cast to avoid the "deprecated conversion from string constant to 'char*'". String literals are implemented as const char* in C++. const_cast is the safest form of cast here, as it only removes the const and does not do any other funny business. execvp() will not edit the values anyway.
If you want to avoid all casts, you have to complicate this code by copying all the values to char* types not really worth it.
Although if the number of arguments you want to pass to execv/execl is known, it's easier to write this in C.
You can't change the how execv works (not easily anyway), but you could overload the function name with one that works the way you want it to:
int execv(const string& path, const vector<string>& argv) {
vector<const char*> av;
for (const string& a : argv) {
av.push_back(a.c_str());
av.push_back(0);
return execv(path.c_str(), &av[0]);
}
Of course, this may cause some confusion. You would be better off giving it a name other than execv().
NB: I just typed this in off the top of my head. It may not work. It may not even compile ;-)

how to pass vector of string to foo(char const *const *const)?

Edit: After implementing James Hopkin' suggestion, I still get the warnings of "invalid name 'null'", which is much better than those weird characters. Then, I went back and read the library document again, and it turns out that for that particular function, it's argument should have one more element of non-NULL string than the size of aNames. That additional string has some other purpose. After adding one more string, the code runs fine. It's all my fault, and I am so sorry.
Original post:
Hi,
This is my first post so please be nice. I searched in this forum and googled but I still can not find the answer. This problem has bothered me for more than a day, so please give me some help. Thank you.
I need to pass a vector of string to a library function foo(char const *const *const). I can not pass the &Vec[0] since it's a pointer to a string. Therefore, I have an array and pass the c_str() to that array. The following is my code (aNames is the vector of string):
const char* aR[aNames.size()];
std::transform(aNames.begin(), aNames.end(), aR,
boost::bind(&std::string::c_str, _1));
foo(aR);
However, it seems it causes some undefined behavior:
If I run the above code, then the function foo throw some warnings about illegal characters ('èI' blablabla) in aR.
If I print aR before function foo like this:
std::copy(aR, aR+rowNames.size(),
std::ostream_iterator<const char*>(std::cout, "\n"));
foo(aR);
Then, everything is fine.
My questions are:
Does the conversion causes undefined behavior? If so, why?
What is the correct way to pass vector of string to foo(char const *const *const)?
Since foo only takes a pointer, my wild guess is that it requires a NULL-terminated array. Allocate an extra element for aR and assign NULL to it.
My suggestion would be:
std::vector<const char*> c_strings;
c_strings.reserve(aNames.size() + 1);
std::transform(
aNames.begin(), aNames.end(),
std::back_inserter(c_strings),
boost::bind(&std::string::c_str, _1)
);
c_strings.push_back(0);
foo(&c_strings[0]);
Well it's seems right to me. c_str create a null terminated array of char that is constant until the next non-const string operation. You store the c_str pointers in a const char* array.
I'm no boost specialist so the problem might be there, but my guess is that your strings in the vector are in an encoding that is incompatible with the one expected in function foo.
Check your code from the encoding point of view.
my2c
Try this:
std::vector<char*> arr(aNames.size()+1);
for(size_t i = 0; i < aNames.size(); ++i)
arr[i] = aNames[i].c_str();
arr[arr.size()-1] = NULL; // just in case
foo(&arr[0]);