How to convert an std::string_view to std::string? [duplicate] - c++

This question already has answers here:
How to correctly create std::string from a std::string_view?
(3 answers)
Closed 1 year ago.
An std::string_view is kind of a replacement for char* in C. It's a string that is not copied from place to place, just as char* is just a place in memory that is referenced from time to time.
However, sometimes we need to transform it in a string for functions that accept a string.
How do I do that?

std::string has constructors that will accept a std::string_view as input, eg:
std::string_view sv{...};
std::string s1{sv};
std::string s2{sv, index, length};
Alternatively, you can use the std::string constructor that accepts a char* and length as input, eg:
std::string_view sv{...};
std::string s1{sv.data(), sv.size()};
std::string s2{sv.data()+index, length};

A std::string as a constructor for that, but only an explicit one.
void f(std::string s);
std::string_view sv;
f(sv); // Error! Cannot convert implicitly
f(std::string{sv}); // Works fine.
This has been designed like this to prevent accidental memory allocations.
See documentation for std::basic_string::basic_string (10)

Just use std::string's constructor:
std::string{my_string_view}

Related

How to make istringstream more efficient? [duplicate]

This question already has answers here:
Creating an input stream from constant memory
(2 answers)
Closed 6 years ago.
#include <sstream>
using namespace std;
const char* GetHugeString();
int main()
{
const char* p = GetHugeString();
//
// Below will copy the huge string into a std::string object!
//
istringstream sstrm{p};
return {};
}
istringstream doesn't need a copy of the huge string; a null-terminated string pointer is enough. But istringstream's ctor only take std::string, rather than std::string_view(c++1z only), as its argument.
Is there a work-around to make std::istringstream more efficient in such a case?
You can simply assign the buffer used internally in the istringstream:
istringstream stream;
stream.rdbuf()->pubsetbuf(p, strlen(p));
This does not copy the string. Do note that pubsetbuf() wants char* not const char*, but it doesn't actually modify the string, so you might const_cast your C string pointer before passing it.

Deep copy std::string::c_str() to char * [duplicate]

This question already has answers here:
How to convert a std::string to const char* or char*
(11 answers)
Closed 9 years ago.
Currently I have a complex function that myself and our team are not wanting to refactor to utilize std::string and it takes a char* which is modified. How would I properly make a deep-copy of string::c_str() into a char*? I am not looking to modify the string's internally stored char*.
char *cstr = string.c_str();
fails because c_str() is const.
You can do it like this:
const std::string::size_type size = string.size();
char *buffer = new char[size + 1]; //we need extra char for NUL
memcpy(buffer, string.c_str(), size + 1);
Rather than modify the existing function, I'd just create an overload that acts as a wrapper. Assuming the existing function is ret_type f(char *), I'd write the overload something like:
ret_type f(std::string s) {
return f(&s[0]);
}
Note passing s by value instead of reference, minimizing the effort expended to get a copy of the string.
In theory, this isn't guaranteed to work (i.e., a string's buffer isn't guaranteed to be contiguous) until C++03. In reality, that guarantee was fairly easy for the committee to add primarily because nobody knew of an implementation of std::string that did anything else.
Likewise, it could theoretically be missing the NUL terminator. If you're concerned about that possibility you could use return f(const_cast<char *>(s.c_str())); instead, or add an s.push_back('\0'); before the return:
ret_type f(std::string s) {
s.push_back('\0');
return f(&s[0]);
}
The obvious solution is:
std::vector<char> tmp( string.begin(), string.end() );
tmp.push_back( '\0' );
function( &tmp[0] );
(I rather like Jerry Coffin's solution, however.)

string.c_str() is const? [duplicate]

This question already has answers here:
Can I get a non-const C string back from a C++ string?
(14 answers)
Closed 5 years ago.
I have a function in a library that takes in a char* and modifies the data.
I tried to give it the c_str() but c++ docs say it returns a const char*.
What can I do other than newing a char array and copying it into that?
You can use &str[0] or &*str.begin() as long as:
you preallocate explicitly all the space needed for the function with resize();
the function does not try to exceed the preallocated buffer size (you should pass str.size() as the argument for the buffer size);
when the function returns, you explicitly trim the string at the first \0 character you find, otherwise str.size() will return the "preallocated size" instead of the "logical" string size.
Notice: this is guaranteed to work in C++11 (where strings are guaranteed to be contiguous), but not in previous revisions of the standard; still, no implementation of the standard library that I know of ever did implement std::basic_string with noncontiguous storage.
Still, if you want to go safe, use std::vector<char> (guaranteed to be contiguous since C++03); initialize with whatever you want (you can copy its data from a string using the constructor that takes two iterators, adding a null character in the end), resize it as you would do with std::string and copy it back to a string stopping at the first \0 character.
Nothing.
Because std::string manages itself its contents, you can't have write access to the string's underlying data. That's undefined behavior.
However, creating and copying a char array is not hard:
std::string original("text");
std::vector<char> char_array(original.begin(), original.end());
char_array.push_back(0);
some_function(&char_array[0]);
If you know that the function will not modify beyond str.size() you can obtain a pointer in one of different ways:
void f( char* p, size_t s ); // update s characters in p
int main() {
std::string s=...;
f( &s[0], s.size() );
f( &s.front(), s.size() );
}
Note, this is guaranteed in C++11, but not in previous versions of the standard where it allowed for rope implementations (i.e. non-contiguous memory)
If your implementation will not try to increase the length of the string then:
C++11:
std::string data = "This is my string.";
func(&*data.begin());
C++03:
std::string data = "This is my string.";
std::vector<char> arr(data.begin(), data.end());
func(&arr[0]);
Here's a class that will generate a temporary buffer and automatically copy it to the string when it's destroyed.
class StringBuffer
{
public:
StringBuffer(std::string & str) : m_str(str)
{
m_buffer.push_back(0);
}
~StringBuffer()
{
m_str = &m_buffer[0];
}
char * Size(int maxlength)
{
m_buffer.resize(maxlength + 1, 0);
return &m_buffer[0];
}
private:
std::string & m_str;
std::vector<char> m_buffer;
};
And here's how you would use it:
// this is from a crusty old API that can't be changed
void GetString(char * str, int maxlength);
std::string mystring;
GetString(StringBuffer(mystring).Size(MAXLEN), MAXLEN);
If you think you've seen this code before, it's because I copied it from a question I wrote: Guaranteed lifetime of temporary in C++?

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 do I pass an std::string to a function that expects char*? [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
Can I get a non-const C string back from a C++ string?
Do I need to convert it first? I saw in another post that .c_str() can be used if the function expected const char*. What about for just char*?
std::vector<char> buffer(s.begin(), s.end());
foo(&buffer[0], buffer.size());
s.assign(buffer.begin(), buffer.end());
There is no way to get a char* from a string that is guaranteed to work on all platforms, for the simple fact that string is not required to use contiguous storage.
Your safest, most portable course of action is to copy the string somewhere that does use contigious storage (a vector perhaps), and use that instead.
vector<char> chars(my_string.begin(), my_string.end());
char* ptr = &chars[0];
If you want to be hacky and non-portable and decidedly unsafe, you can confirm that your string implementation does in fact use contigious storage, and then maybe use this:
&my_str[0]
But I would punch any developer that worked for me that did this.
EDIT:
I've been made aware that there are currently no known STL implementations that do not store the string data in a contiguous array, which would make &my_str[0] safe. It is also true (and I was asked to state this) that in the upcoming C++0x standard, it will be required for the storage to be contiguous.
It's been suggested that because if these facts that my post is factually incorrect.
Decide for yourself, but I say no. This is not in the current C++ standard, and so it is not required. I will still in practice do things the way I have suggested, and in any code review I will flag any code that assumes the underlying storage is contigious.
Consider this. Suppose there were a question about vtable pointers. Someone wants to examing a class and get the pointer to a virtual function by looking at the vtable. I would immediately tell them not to do this because there is no mention of how virtual methods are implemented in C++. Every implementation I know uses vtables, and I can't think of a better way to do it. It is likely that polymorphism will forever be implemented using vtables. Does that make it ok to examing the vtable directly?
IMO no, because this depends on undocumented implementation details. You have no control over this, and it could change at any time. Even if you expect it will never change, it is still bad engineering to rely on these implementation details.
Decide for yourself.
There are three scenarios:
If the function is outside of your control, and it either modifies the string, or you don't and can't know if it modifies the string:
Then, copy the string into a temporary buffer, and pass that to the function, like so:
void callFoo(std::string& str);
{
char* tmp = new char str(str.length() +1);
strncpy(tmp, str.c_str(), str.length());
foo(tmp);
// Include the following line if you want the modified value:
str = tmp;
delete [] tmp;
}
If the function is outside of your control, but you are certain it does not modify the string, and that not taking the argument as const is simply a mistake on the API's part.
Then, you can cast the const away and pass that to the function
void callFoo(const std::string& str)
{
foo(const_cast<char*> str.c_str());
}
You are in control of the function (and it would not be overly disruptive to change the signature).
In that case, change the function to accept either a string& (if it modifies the input buffer) or either const char* or const string& if it does not.
When a parameter is declared as char* there it is implicitly assumed that the function will have as a side effect the modification of the string that is pointed. Based in this and the fact that c_str() does not allow modifications to the enclosed string you cannot explicitly pass an std::string to such a method.
Something like this can be achived by following the following approach:
#include <cstdlib>
#include <string>
#include <iostream>
void modify_string(char* pz)
{
pz[0] = 'm';
}
class string_wrapper
{
std::string& _s;
char* _psz;
string_wrapper(const string_wrapper&);
string_wrapper& operator=(const string_wrapper&);
public:
string_wrapper(std::string& s) : _s(s), _psz(0) {}
virtual ~string_wrapper()
{
if(0 != _psz)
{
_s = _psz;
delete[] _psz;
}
}
operator char*()
{
_psz = new char[_s.length()+1];
strcpy(_psz,_s.c_str());
return _psz;
}
};
int main(int argc, char** argv)
{
using namespace std;
std::string s("This is a test");
cout << s << endl;
modify_string(string_wrapper(s));
cout << s << endl;
return 0;
}
If you are certain that the char* will not be modified, you can use const_cast to remove the const.
It's a dirty solution but I guess it works
std::string foo("example");
char* cpy = (char*)malloc(foo.size()+1);
memcpy(cpy, foo.c_str(), foo.size()+1);