I have to write a library which contains a function that takes two strings parameters:
void foo(const std::string& arg1, const std::string& arg2);
My library is going to be used by some people that don't like C++ and are only used to const char*.
To satisfy their likes, I changed the prototype:
void foo(const char* arg1, const char* arg2);
And made my first version a simple inline call:
inline void foo(const std::string& arg1, const std::string& arg2)
{
foo(arg1.c_str(), arg2.c_str());
}
Of course, thanks to the std::string constructors, it would have worked almost the same way with just the first version. Writing this overload just avoids instanciating useless std::string in the case someone passes only const char*.
But now I wonder: is this overload really necessary or is it just premature optimization ?
Also, I'm feeling like this is somehow incomplete: should I write also void foo(const char* arg1, const std::string& arg2) and void foo(const std::string& arg1, const char* arg2) overloads ? What if I have 3, 4 or more (n) parameters ? Should I write 2n overloads ?
In short, have you ever faced a similar situation ? What choices did you made and why ?
Thank you.
IMO it's reasonable to have two overloads to handle two different programming styles that co-exist within your organization.
Technically it's not just an optimization:
the const char* can be extern "C", so might be useful for bindings from other languages, or even from other C++ code across a dll boundary with incompatible standard library implementation.
the conversion to std::string can throw bad_alloc, so if it cannot otherwise fail then the const char* version is useful if you have a C-style string and want to use it in a nothrow context.
Also beware that in principle c_str() can throw bad_alloc, but I doubt that any implementations actually do.
2n overloads doesn't seem worthwhile to me - if someone is mixing string and char* then it's not just a programming style difference, they're actually using mixed formats for some reason. Since they're using both already, they can convert for themselves.
This all assumes that the function is simple enough that you're happy to implement the actual work with const char* parameters. If the implementation based on string would be significantly simpler (for example if it takes significant code to make it exception-safe otherwise), then that probably wins over efficiency anyway, so don't provide any overloads, let their const char* convert to string.
As long as you want to work with C++ (and not in C), the second one seems unneccessary, if you've this:
void foo(const std::string& arg1, const std::string& arg2);
You can call this with all combinations of std::string and char* as:
char *ca, *cb;
std::string sa, sb;
//...
foo(ca,cb); //char*, char*
foo(sa,cb); //std:string, char*
foo(sa,sb); //std::string, std::string
foo(ca,sb); //char*, std::string
If const char * text is never null, you could use string, because it has implictit constructor, so string class version could be enough. In other hand: if const char * text could be null, it generates runtime error.
One more thing: on DLL interface STL is not really good option. So if the function is part of a DLL interface, you may use C style strings.
I may do it in that way:
#include <string>
using namespace std;
void foo( const string arg0, const string arg1 )
extern "C" _declspec( dllexport ) void foo( const char * arg0, const char * arg1 )
{
string string_arg0, string_arg1;
if ( arg0 != 0 )
string_arg0 = arg0;
if ( arg1 != 0 )
string_arg1 = arg1;
foo(string_arg0, string_arg1);
}
In that case, you could probably just have the (const char*,const char*) and ask everyone using std::string to use c_str(). That way there won't be any memory allocation overhead, only the extra c_str() that people will have to code up.
My library is going to be used by some people that don't like C++ and
are only used to const char*.
Tell them to man up and learn the language that their compiler compiles. You take std::string, because that's how to do it in C++, and if they don't like it, then they can get a C compiler.
I think your present approach is correct. Where you overload for const char* and provide a wrapper which also serves std::string.
If your colleagues are going to use C-style character pointer than it's better to do that way (present way). See following case;
char *p = 0;
foo(p);
void foo (const string &s)
{ // runtime error
}
Demo. As you can see that character pointers may not go well always with std::string; so why to take chance. Even though people may not do such coding; still you should avoid such potential hazards for long time maintainability.
Inside the function you can always choose to have std::string from const char* and write your algorithm.
Related
I understand that it is always advisable to use std::string compared to const char * because of the inadvertent changes that may happen because of using a pointer. But for simple usages where there can't be any inadvertent changes by any other engineer, is it still a big deal. For e.g. see the code below -
So in this example below, I could have used
std::string BothUserNameAndPassword = "abracadabra";
I see time to time having to deal with code reviews that has code such as the above.
#include<iostream>
using namespace std;
void authenticatePassword(std::string const & username, std::string
const &password);
int main()
{
const char* BothUserNameAndPassword = "abracadabra";
authenticatePassword(BothUserNameAndPassword, BothUserNameAndPassword);
}
void authenticatePassword(std::string const & username, std::string const &password)
{
cout << "username = "<<username<<" and password = "<<password <<endl;
}
Is there any reason to change from using const char* to string [...]?
Yes, there is. The function signature reveals that two const std::string& arguments are expected. Now what happens if you pass in a const char* object? A temporary std::string instance is implicitly constructed. By going with
const std::string BothUserNameAndPassword = "abracadabra";
authenticatePassword(BothUserNameAndPassword, BothUserNameAndPassword);
you don't make this extra effort. I wouldn't argue that this is a performance-critical tweak, but it might be considered more readable, as you don't have to think about conversions when having a look at the function signature and the variable you are passing to it.
Note that from C++17 on, you might also want to consider std::string_view for such cases (both function arguments and binding the literal to a variable).
Your user will probably specify the username and password externally, which will be read in a string. So no reason not to use a string from the start. The double string creation artifact of this example may not be a real world or production case (at least I hope it's not...).
In modern C++ (post C++17), there is no good reason to use const char* in new code anymore, you should use string_view instead. This efficiently replaces the pointer as it has the size of the string and can also efficiently slice bits of the string. It is NOT suited for something that will end up being stored in a string. In that case, you should use a string from the start.
So there are two use cases, one fitted for string and one for string_view:
storing the data
using the data
In this specific case, I would do:
int main()
{
constexpr std::string_view BothUserNameAndPassword = "abracadabra";
authenticatePassword(BothUserNameAndPassword, BothUserNameAndPassword);
}
void authenticatePassword(std::string_view username, std::string_view password)
{
cout << "username = "<<username<<" and password = "<<password <<endl;
}
I'm using some C functions in my C++ environment. And I get the following warnings because C++ doesn't allow assigning string literal to char * type.
C++11 does not allow conversion from string literal to char *
My Code:
void sys_vgui(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
//do something
va_end(ap);
}
void open(t_buf *x)
{
sys_vgui("wm deiconify .x%lx\n", x);
sys_vgui("raise .x%lx\n", x);
sys_vgui("focus .x%lx.text\n", x);
}
And I could remove these warnings by const casting the string literals as the following.
sys_vgui(const_cast<char *>("wm deiconify .x%lx\n"), x);
sys_vgui(const_cast<char *>("raise .x%lx\n"), x);
sys_vgui(const_cast<char *>("focus .x%lx.text\n"), x);
But I'm not sure if it's really safe as I've seen many people saying not to directly cast string literal to char *.
So I came up with the following which seems like a safer solution.
char str1[] = "wm deiconify .x%lx\n";
sys_vgui(str1, x);
char str2[] = "raise .x%lx\n";
sys_vgui(str2, x);
char str3[] = "focus .x%lx.text\n";
sys_vgui(str3, x);
But it makes my code dirty and harder to maintain as I have to create multiple variables using different names(e.g. str1, str2, str3...) whenever I use the function.
So my questions are:
1) Is it really not safe to use const_cast<char *> in my case?
2) Any solution to write a clean code using char arrays without having to create multiple variables using different names?
It is safe as long as sys_vgui does not modify the string, which presumably it doesn't because modifying a string literal has undefined behavior in C, const or not. So if you have something like
sys_vgui("wm deiconify .x%lx\n", x);
in C, then the C++ version with const_cast is just as safe.
However, to de-uglify the C++ code, I'd probably write a wrapper function:
template<typename ...Ts>
void xsys_vgui(const char *fmt, Ts ...args) {
sys_vgui(const_cast<char *>(fmt), args ...);
}
Now
xsys_vgui("wm deiconify .x%lx\n", x);
should "just work" in C++.
Don't abuse the const_cast intentions (and even if not abusing it, try and contrive a way to avoid it in the first place).
This is rather hideous, but may do what you intend: a template version of the API you desire, designed to manufacture the requisite mutable array and forward it appropriately thereafter. For callers actually supplying non-const arrays or pointers, it should just invoke the api directly.
#include <iostream>
#include <algorithm>
void do_function(char *ptr)
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
template<size_t N>
void do_function(const char (&ar)[N])
{
std::cout << __PRETTY_FUNCTION__ << '\n';
char inside[N];
std::copy(ar, ar+N, inside);
do_function(inside+0);
}
int main()
{
char msg[] = "array test";
do_function(msg);
do_function("Something const");
do_function("Nothing");
}
Output
void do_function(char *)
void do_function(const char (&)[N]) [N = 16]
void do_function(char *)
void do_function(const char (&)[N]) [N = 8]
void do_function(char *)
Note: I've not really put this through the wringer, but it will likely achieve what you desire. This biggest bonus is you need not change any of the prior calls (beyond removing those const_cast misnomers). the original calls passing string literals will simply start working. All you need to is hang the template version and let the compiler sort out the rest.
If you really know that sys_vgui doesn’t use the passed pointer to modify something, it’s fine in practice, although dirty, because that assumption is just in your head.
In c++17 you can use std::string::data:
sys_vgui(std::string{"wm deiconify .x%lx\n"}.data(), x);
Prior to that (in c++11 and c++14) you have to specify the size manually to std::array (including null terminator, but the compiler will probably complain anyway if it’s wrong)
sys_vgui(std::array<char, 20>{"wm deiconify .x%lx\n"}.data(), x);
If you do not want to change sys_vgui then you can make a wrapper:
template<typename... Args>
void sys_vgui_c(char const *fmt, Args&&... args)
{
sys_vgui( const_cast<char *>(fmt), args... );
}
and then call it with the string literal: sys_vgui_c("wm deiconify .x%lx\n", x);
How about a variadic macro:
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#define my_vgui(fmt_,...) \
do { \
char myfmt[strlen(fmt_) + 1]; \
strcpy(myfmt,fmt_); \
sys_vgui(myfmt,##__VA_ARGS__); \
} while (0)
void
sys_vgui(char *fmt,...)
{
va_list ap;
va_start(ap,fmt);
fmt[0] |= 0;
vprintf(fmt,ap);
va_end(ap);
}
int
main(void)
{
my_vgui("hello\n");
my_vgui("hello %s\n","world");
return 0;
}
Note that there may be cleaner ways to do the macro, so see: https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html
So I've just recently started working with some embedded devices that don't fully support the C++ STL library and have had to rely on using some libraries which may or may not be attempting to be compliant with the old C standards where I believe did not have the keyword const defined and so functions accepting string literals would have to deal with char* and I think you were just supposed to "trust" that the method wouldn't write to your "string".
I'm also not fond of seeing const_cast<char*>("abc") anywhere I would otherwise just write "abc" so for my purposes, it was easy enough to define a simple macro:
#define S_(x) const_cast<char*>(x)
and use it as follows:
print(S_("abc"));
It's much more concise, assuming your namespace doesn't already have lots of single capital letter macros such as the one I chose to use.
class Student {
public:
string name;
};
vs
class Student {
public:
char* name;
};
Please correct me if I'm wrong. If we were to use char* instead of string, we will have to write our very own copy-constructor because we need to every time we have pointer variables as data members. Right?
So, my question is: Why use char* at all?
Using string, in the constructor, we can directly do:
Student(string s) {
name = s;
}
which is simpler compared to char*, which needs:
Student(string s) {
name = new char[strlen(s)+1]; // extra 1 to store the '\n'
strcpy(name,s);
}
Why not use string at all times instead of char* when being used as a data member of a class?
I think the only reason char* is used in C++ as a string is because of C. I'm sure if it was a new language, one which didn't strive to be compatible with C, char* would not be used like that. You will notice that functions that handle char* as if it were a string all come from C.
Note that in C, there is no string, so
struct Student { char* name; };
is perfectly valid C code, whereas
struct Student { string name; };
is not. Therefore, it is not unusual, when dealing with code which previously target C, to see those char* types.
There are usually little reason for using char* as a string, unless you are either writing a new string class, interfacing C functions, or dealing with legacy code.
You use char * instead of string, because a string is a string and a char * is a pointer to a character-aligned address.
Expanding on that, a string is an abstraction of a vector of characters with defined semantics. In C land, and in a lot of C++ programs, it represents an allocated block of memory along with a guarantee that it's terminated with the ascii NUL character 0x00. But a C++ implementation of string could instead use, say, a Pascal string with associated length, or it could represent strings in a string pool as a linked list.
A char * isn't providing that guarantee at all, and in fact might not be a string -- for example, it might be a collection of data with embedded 0x00 values. All it promises is that it's an address of something that the underlying architecture thinks is a character.
If you need a string, use std::string, not char*.
An obvious exception is interfacing to legacy code that uses char* to represent strings. Still, don't use char* outside of the interface layer.
You need to use char* when your data isn't a string, but a raw unstructured array of bytes. This is the case when you read or write binary data from files or network interfaces.
Sometimes, it is simpler to use char* instead of string, for example, when you are dealing with network and you need to transform a string full of bytes into a integer, float, etc.. I think that it's simpler to use a char*, instead of a string :
Using a char*
char* buffer[4];
read(buffer, 4); // A random read operation
int value = *((int*)(&buffer[0]); // Not sure if it was like that...
Using a string
std::string buffer;
buffer.resize(4);
read(buffer.data(), 4); // Will not work as buffer.data() returns a const char*
int value = *((int*)(&buffer.data()[0]));
The problem of string is that it's designed to prevent bad usage or strange manipulations. As someone said, it's also because C++ is inherited from C. So there is functions (from libc/glibc) which takes a char* and not a string.
EDIT
Even if char* is different from char**, it's pretty complex to build a bi-dimensional array using std::vector or std::string, you should either make your proper class, use char**, or library specific implementation (Boost, Maths libs, etc...)
About the only place where a competent C++ programmer will use char* is in the interface of an extern "C" program, or in very low level code, like an implementation of malloc (where you need to add a number of bytes to a void*). Even when calling into a C ABI, the char* needed by the interface will generally come from a &s[0], where s is an std::string, or if the interface is not const aware (and a lot of C interfaces aren't), then the results of a const_cast.
char const* is a bit more frequent: a string literal is, after all, a char const[], and I will occasionally define something like:
struct S
{
int value;
char const* name;
};
But only for static data, eg:
S const table[] =
{
{ 1, "one" },
{ 2, "two" },
// ...
};
This can be used to avoid order of initialization issues; in the above, the initialization is static, and guaranteed to take place before any dynamic initialization.
There are few other cases: I've used char const*, for example, when marshalling between to C ABIs. But they are rare.
I have just read about the overloading functions on a beginner book.
Just out of curiosity I 'd like to ask whether it is safe to overload between char* and std::string.
I played with the below code and get some result. But I was not sure whether it is an undefined behavior.
void foo(std::string str) {
cout << "This is the std::string version. " << endl;
}
void foo(char* str) {
cout << "This is the char* version. " << endl;
}
int main(int argc, char *argv[]) {
foo("Hello"); // result shows char* version is invoked
std::string s = "Hello";
foo(s); // result shows std::string version
return 0;
}
Yes, it's safe, as long as you make it const char*, and actually often useful. String literals cannot be converted to char* since C++11 (and it was deprecated before that).
The const char* overload will be picked for a string literal because a string literal is a const char[N] (where N is the number of characters). Overloads have a kind of priority ordering over which one will be picked when multiple would work. It's considered a better match to perform array-to-pointer conversion than to construct a std::string.
Why can overloading std::string and const char* be useful? If you had, for example, one overload for std::string and one for an bool, the bool would get called when you passed a string literal. That's because the bool overload is still considered a better match than constructing a std::string. We can get around this by providing a const char* overload, which will beat the bool overload, and can just forward to the std::string overload.
Short Answer: Perfectly safe. Consider the following uses:
foo("bar");//uses c string
foo(std::string("bar") );//uses std::string
char* bar = "bar";
foo(bar);//uses c string
std::string bar_string = "bar";
foo(bar_string);//uses std::string
foo(bar_string.c_str()); //uses c string
Word of warning, some compilers (namely those with c++11 enabled) require the const keyword in parameter specification in order to allow temporary strings to be used.
For instance, in order to get this:
foo("bar");
You need this:
void foo(const char* bar);
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);