How you convert a std::string_view to a const char*? - c++

Compiling with gcc-7.1 with the flag -std=c++17, the following program raises an error:
#include <string_view>
void foo(const char* cstr) {}
void bar(std::string_view str){
foo(str);
}
The error message is
In function 'void bar(std::string_view)':
error: cannot convert 'std::string_view {aka std::basic_string_view<char>}' to 'const char*' for argument '1' to 'void foo(const char*)'
foo(str);
I'm surprised there is no conversion to const char* because other libraries (abseil, bde), provide similar string_view classes which implicitly convert to const char*.

A std::string_view doesn't provide a conversion to a const char* because it doesn't store a null-terminated string. It stores a pointer to the first element, and the length of the string, basically. That means that you cannot pass it to a function expecting a null-terminated string, like foo (how else are you going to get the size?) that expects a const char*, and so it was decided that it wasn't worth it.
If you know for sure that you have a null-terminated string in your view, you can use std::string_view::data.
If you're not you should reconsider whether using a std::string_view in the first place is a good idea, since if you want a guaranteed null-terminated string std::string is what you want. For a one-liner you can use std::string(object).data() (note: the return value points to a temporary std::string instance that will get destroyed after the end of the expression!).

Simply do a std::string(string_view_object).c_str() to get a guaranteed null-terminated temporary copy (and clean it up at the end of the line).
This is required because string view doesn't guarantee null termination. You can have a view into the middle of a longer buffer, for example.
If this use case is expensive and you have proven it to be a bottleneck, you can write an augmented string_view that tracks if it is null terminated (basically, if it was constructed from a raw char const*).
Then you can write a helper type that takes this augmented string_view and either copies it to a std::string or stores the augmented string_view directly, and has an implicit cast-to-char const* that returns the properly null-terminated buffer.
Then use that augmented helper type everywhere in your code base instead of string_view, possibly augmenting string view interaction with std string as well to catch the cases where you have a view that goes to the end of the std string buffer.
But really, that is probably overkill.
A better approach is probably rewriting the APIs that take const char* to take string_view.

You can call foo(std::string(str).c_str()).

using object.data() solved my problem with compiler error C2664

Related

Passing a string to a function in C++(object vs string)

What is the difference between the following two in C++?
fun(L"text1")
VS
std::wstring var = "text1"
fun(var)
In the first case, it is being passed as an object while the second case passes it as a wstring.
How should fun() be defined to handle both?
EDIT:
I have two function definitions
fun(void*)
fun(std::wstring)
std::wstring t = "bla";
fun(t);
fun(L"msg");
When fun(t) is called it goes to the definition of fun(std::wstring)
But when fun(L"msg") is called it goes to fun(Void*). Instead I want it to goto fun(std::wstring)
The first is passed as a wide-string literal. In the second case, you pass by value (and hence copy) or by reference an std::wstring object.
To handle both, you have to define two overloads of your function:
void fun(const wchar_t* s);
void fun(const std::wstring& s);
or you can just define the wstring version, because the literal will implicitly convert to a wstring.
To handle both you should define the fun method as:
void fun(const std::wstring& str);
then in both cases you would be passing a const reference to a wstring because the compiler is allowed to implicitly cast one type to another if the type being cast to has a constructor that takes one argument of the type being cast from unless that constructor is marked as explicit.
Example:
class wstring
{
public:
// constructor not marked as explicit and takes one argument of type whar_t*
wstring(const wchar_t* str);
};
wstring myString = L"hello world"; // implicit cast from wchar_t* to wstring
The only difference between the two examples you've given is that in the first you are passing an rvalue (which you can only bind to a const reference) and in the second you are passing an lvalue (which you can bind to both const and non-const reference).
In given examples there is no much difference, because compiler will generate constant data containing your literal and use it in both cases.
In the first case, raw literal will be used from string table of your module. This is as fast as possible code without heap allocation (actually no allocations).
In the second case, compiler will allocate string buffer in the heap, which results into malloc() call and strcpy(). This will increase time of your code and cause more memory fragmentation.
You should use std string classes only when you really need to use their useful methods, otherwise, TCHAR buffers are just excellent choice.

Disable warning "deprecated conversion from string constant to 'char*' [-Wwrite-strings]"

I have these two lines in my code:
RFM2G_STATUS result;
result = RFM2gOpen( "\\\\.\\rfm2g1", &rH );
I get the error message:
"warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]
result = RFM2gOpen( "\\\\.\\rfm2g1", &rH );"
Actually I can not modify it to
const RFM2G_STATUS result;
because RFM2G_STATUS is pre-defined in another file and does not accept const before. Is there another way to disable this warning message?
Like the message says, conversion from const char* to char* (which C++ inherited from ancient C language which didn't have const) has been deprecated.
To avoid this, you can store the parameter in a non-const string, and pass that to the function:
char parameter[] = "\\\\.\\rfm2g1";
RFM2G_STATUS result;
result = RFM2gOpen( parameter, &rH );
That way you avoid the ugly casts.
You seem to be absolutely sure that RFM2gOpen does not modify the input string, otherwise you would have undefined behavior in your code as it stands now.
If you are sure that the input data will not be written to, you can const_cast the constness away safely:
result = RFM2gOpen(const_cast<char*>("\\\\.\\rfm2g1"), &rH );
Again, this is only safe if the routine does not write to the input string, ever, otherwise this is undefined behavior!
If you are not completely sure that this method will never write to the character array, copy the string to an std::vector<char> and pass the .data() pointer to the function (or use a simple char array as Bo Persson suggests, that would most likely be more efficient/appropriate than the vector).
One possible fix is:
RFM2gOpen(const_cast<char*>("\\.\rfm2g1"), &rH);
This may cause a runtime fault if RFM2gOpen tries to modify the string.
The following is less likely to cause a memory fault, but it still undefined behavior:
std::string s("\\.\rfm2g1");
RFM2gOpen(const_cast<char*>(s.c_str()), &rH);
To be fully conformant you need to copy the "\.\rfm2g1" to a mutable buffer. Something like:
char *s = alloca(strlen("\\.\rfm2g1")+1);
strcpy(s, "\\.\rfm2g1");
RFM2gOpen(s, &rH);
The real fix, of course, is for RFM2gOpen to be updated to take a const char*.
It would seem that the function RFM2gOpen() expects a non-const char* as first parameter (see here), as it can sometimes happen with legacy API's (or API's written by lazy coders), and string litterals are of type const char*
so a deprecated implicit conversion is happening (getting rid of the const qualifier).
If you're *100% sure that the function won't modify the pointed-to memory, then and only then can you just put an explicit conversion, e.g. const_cast<char*>("\\\\.\\rfm2g1") or (C-style) (const char*)"\\\\.\\rfm2g1"

Is there a way to pass a string literal as reference in C++

Within C++ it is common to pass by reference instead of pointer if a value can not be NULL.
Suppose I have a function with the following signature, which is often used with a string literal.
void setText( const char* text );
I was wondering how I could change the function in such a way that it accepts a reference (and has the advantage not to accept NULL)?
If I would change it to (const char& text) then it would be a ref to a single char. From which the address can ba taken inside the function... but feels not nice.
Another option would be (const std::string& text) which has the disadvantage that it always calls a constructor and does some dynamic memory allocation.
Any other common ways, or just stick to the std::string& or the char* ?
Honestly, I would just keep the const char* text function and add an overload const std::string& text function that calls the first one with setText(text.c_str())
There's a slight problem here in that C++ and references-to-arrays aren't the best pair. For reference, see: C++ pass an array by reference
Since you're talking about binding a reference to a string, and a string is an array of characters, we run into that problem head-on. In light of this, the best we can really do is bind a ref to a const char*, which looks like this:
void ref(const char* const& s);
But this doesn't do what you want; this binds a reference to a pointer, and all it guarantees is that the pointer itself exists, not that it's pointing to a valid string literal.
This same problem is present in the std::string& examples: those only guarantee that you've bound to a std::string object, but that string could very well be empty, so you still haven't guaranteed yourself a string that has anything of value in it.
In the end, I'll second what Zan says. const char* is a well respected idiom passing string literals, and then having a second overload that binds to strings is a nice convenience.
(One last note: std::string doesn't "always" allocate memory. Implementations with the small string optimization will skip it for strings as long as 23 characters.)

std::string declared function does not complain returning char *

This is not my program so don't start berating me :-). Some random program I got. A globally declared buffer is being returned by MyFunc(). I use VS2008 and it does not complain
static char buffer[1024];
std::string MyFunc() {
....
....
return buffer;
}
However when I add this line of code
char * ret;
ret = MyFunc()
It complains: "error: no suitable conversion function from "std::string" to "char *" exists"
My question is why is the compiler complaining now? Why this inconstancy in syntax checking? Again I dont have the freedom to change MyFunc(). In my program if I can make
std::string ret;
ret = MyFunc();
and get rid of the syntax error but would really like to understand this strange behavior.
string() has a constructor that accepts a char*, so you get an automatic conversion. There is no automatic conversion from a string to a char*. You have to call string::c_str() to get the char*.
Edit
Although you asked only for an explanation of the behavior, others in this forum seem to think I have short-changed you by not mentioning that string::c_str returns a const char*, not a simple char*. But the explanation remains: there is no implicit/automatic conversion from string to char* or const char*. Feel free to read about c_str here if it's important to you.
It is not the syntax, it is the structure of the std::string that makes the compiler behave differently.
When you are returning a char* from a function returning std::string, the compiler notices that there is a constructor of std::string that takes char*, calls that constructor, and quietly returns the result.
When you are trying to return a std::string from a char* - returning function, the compiler tries to see if there is a conversion operator to make char* from a std::string, finds that there is no such operator, and reports an error.
If you want to convert a string to char*, you need to make a copy of the string's buffer, like this:
char* ret_ch = new char[ret.size()+1];
memcpy(ret_ch, ret.c_str(), ret.size()+1);
return ret_ch;
You could think that it is OK to return c_str() by itself, but it is not a good idea: the buffer that "backs up" this C string belongs to std::string object, so once the string gets deallocated, accessing the buffer starts producing undefined behavior. That is why you need to make an explicit copy when you access the buffer of a string. Of course you are also responsible for calling delete[] on the copied result.
std::string is designed as implicitly constructable from char const* because this supports using string literals and typical C style code strings as initializer values.
If this was not supported then one would just have to use some intermediate function, which would add nothing but verbosity and inefficiency.
In the other direction, however, std::string is intentionally designed to not convert implicitly to char const*. Part of the rationale is probably that with std::string being logically mutable, the returned raw pointer is only valid as long as no operations are performed that might cause a buffer replacement or string destruction. For example,
char const* s = foo().c_str();
where foo produces a std::string, makes s point to a buffer that no longer exists, a dangling pointer that is invalid.
The c_str() member function call makes the conversion stand out.
Consider how more common that problem could be if one could write just
char const* s = foo();
and have that compile.
Regarding that strike-through (deleted) text, I realized that it's completely irrelevant whether the string is logically mutable or immutable. Sorry. Need more coffee!

C++: Is "my text" a std::string, a *char or a c-string?

I have just done what appears to be a common newbie mistake:
First we read one of many tutorials that goes like this:
#include <fstream>
int main() {
using namespace std;
ifstream inf("file.txt");
// (...)
}
Secondly, we try to use something similar in our code, which goes something like this:
#include <fstream>
int main() {
using namespace std;
std::string file = "file.txt"; // Or get the name of the file
// from a function that returns std::string.
ifstream inf(file);
// (...)
}
Thirdly, the newbie developer is perplexed by some cryptic compiler error message.
The problem is that ifstream takes const * char as a constructor argument.
The solution is to convert std::string to const * char.
Now, the real problem is that, for a newbie, "file.txt" or similar examples given in almost all the tutorials very much looks like a std::string.
So, is "my text" a std::string, a c-string or a *char, or does it depend on the context?
Can you provide examples on how "my text" would be interpreted differently according to context?
[Edit: I thought the example above would have made it obvious, but I should have been more explicit nonetheless: what I mean is the type of any string enclosed within double quotes, i.e. "myfilename.txt", not the meaning of the word 'string'.]
Thanks.
So, is "string" a std::string, a c-string or a *char, or does it depend on the context?
Neither C nor C++ have a built-in string data type, so any double-quoted strings in your code are essentially const char * (or const char [] to be exact). "C string" usually refers to this, specifically a character array with a null terminator.
In C++, std::string is a convenience class that wraps a raw string into an object. By using this, you can avoid having to do (messy) pointer arithmetic and memory reallocations by yourself.
Most standard library functions still take only char * (or const char *) parameters.
You can implicitly convert a char * into std::string because the latter has a constructor to do that.
You must explicitly convert a std::string into a const char * by using the c_str() method.
Thanks to Clark Gaebel for pointing out constness, and jalf and GMan for mentioning that it is actually an array.
"myString" is a string literal, and has the type const char[9], an array of 9 constant char. Note that it has enough space for the null terminator. So "Hi" is a const char[3], and so forth.
This is pretty much always true, with no ambiguity. However, whenever necessary, a const char[9] will decay into a const char* that points to its first element. And std::string has an implicit constructor that accepts a const char*. So while it always starts as an array of char, it can become the other types if you need it to.
Note that string literals have the unique property that const char[N] can also decay into char*, but this behavior is deprecated. If you try to modify the underlying string this way, you end up with undefined behavior. Its just not a good idea.
std::string file = "file.txt";
The right hand side of the = contains a (raw) string literal (i.a. a null-terminated byte string). Its effective type is array of const char.
The = is a tricky pony here: No assignment happens. The std::string class has a constructor that takes a pointer to char as an argument and this is called to create a temporary std::string and this is used to copy-construct (using the copy ctor of std::string) the object file of type std::string.
The compiler is free to elide the copy ctor and directly instantiate file though.
However, note that std:string is not the same thing as a C-style null-terminated string. It is not even required to be null-terminated.
ifstream inf("file.txt");
The std::ifstream class has a ctor that takes a const char * and the string literal passed to it decays to a pointer to the first element of the string.
The thing to remember is this: std::string provides (almost seamless) conversion from C-style strings. You have to look up the signature of the function to see if you are passing in a const char * or a std::string (the latter because of implicit conversions).
So, is "string" a std::string, a c-string or a char*, or does it depend on the context?
It depends entirely on the context. :-) Welcome to C++.
A C string is a null-terminated string, which is almost always the same thing as a char*.
Depending on the platforms and frameworks you are using, there might be even more meanings of the word "string" (for example, it is also used to refer to QString in Qt or CString in MFC).
The C++ standard library provides a std::string class to manage and represent character sequences. It encapsulates the memory management and is most of the time implemented as a C-string; but that is an implementation detail. It also provides manipulation routines for common tasks.
The std::string type will always be that (it doesn't have a conversion operator to char* for example, that's why you have the c_str() method), but it can be initialized or assigned to by a C-string (char*).
On the other hand, if you have a function that takes a std::string or a const std::string& as a parameter, you can pass a c-string (char*) to that function and the compiler will construct a std::string in-place for you. That would be a differing interpretation according to context as you put it.
Neither C nor C++ have a built-in string data type.
When the compiler finds, during the compilation, a double-quoted strings is implicitly referred (see the code below), the string itself is stored in program code/text and generates code to create even character array:
The array is created in static storage because it must persist to be referred later.
The array is made to constant because it must always contain the original data (Hello).
So at last, what you have is const char * to this constant static character array.
const char* v()
{
char* text = “Hello”;
return text;
// Above code can be reduced to:
// return “Hello”;
}
During the program run, when the control finds opening bracket, it creates “text”, the char* pointer, in the stack and constant array of 6 elements (including the null terminator ‘\0’ at the end) in static memory area. When control finds next line (char* text = “Hello”;), the starting address of the 6 element array is assigned to “text”. In next line (return text;), it returns “text”. With the closing bracket “text” will disappear from the stack, but array is still in the static memory area.
You need not to make return type const. But if you try to change the value in static array using non constant char* it will still give you an error during the run time because the array is constant. So, it’s always good to make return constant to make sure, it cannot be referred by non constant pointer.
But if the compiler finds a double-quoted strings is explicitly referred as an array, the compiler assumes that the programmer is going to (smartly) handle it. See the following wrong example:
const char* v()
{
char text[] = “Hello”;
return text;
}
During the compilation, compiler checks, quoted text and save it as it is in the code to fill the generated array during the runt time. Also, it calculate the array size, in this case again as 6.
During the program run, with the open bracket, the array “text[]” with 6 elements is created in stack. But no initialization. When the code finds (char text[] = “Hello”;), the array is initialized (with the text in compiled code). So array is now on the stack. When the compiler finds (return text;), it returns the starting address of the array “text”. When the compiler find the closing bracket, the array disappears from the stack. So no way to refer it by the return pointer.
Most standard library functions still take only char * (or const char *) parameters.
The Standard C++ library has a powerful class called string for manipulating text. The internal data structure for string is character arrays. The Standard C++ string class is designed to take care of (and hide) all the low-level manipulations of character arrays that were previously required of the C programmer. Note that std::string is a class:
You can implicitly convert a char * into std::string because the
latter has a constructor to do that.
You can explicitly convert a std::string into a const char * by using the c_str() method.
As often as possible it should mean std::string (or an alternative such as wxString, QString, etc., if you're using a framework that supplies such. Sometimes you have no real choice but to use a NUL-terminated byte sequence, but you generally want to avoid it when possible.
Ultimately, there simply is no clear, unambiguous terminology. Such is life.
To use the proper wording (as found in the C++ language standard) string is one of the varieties of std::basic_string (including std::string) from chapter 21.3 "String classes" (as in C++0x N3092), while the argument of ifstream's constructor is NTBS (Null-terminated byte sequence)
To quote, C++0x N3092 27.9.1.4/2.
basic_filebuf* open(const char* s, ios_base::openmode mode);
...
opens a file, if possible, whose name is the NTBS s