I am loosing my mind and new to C++ I know C# where I know that it is as simple as
var cat = "cat";
dvar(0,0, "hi" +cat+ "hi");
My issue here is I am developing a game and need to put a string into a function call like so:
string host = "HIST";
dvar(0,0, "s \"test" + host.c_str() + "connection\"");
Also about the threading I am going nuts because my game I can only call in one function at a time but I have a function that is on scree instructions that has a constant while loop so it's to busy handing that looping thread for me to activate any other functions via buttons.
You should do call the function without the c_str() in order to use the non-member string concatenation function.
dvar(0,0, ("s \"test" + host + "connection\"").c_str());
Since host is a std::string type the + operator will result in calling the non-member function operator+ for std::string.
E.g. host + "connection" will result in calling the following function, where "connection" is implicitly converted into a std::string:
std::string operator+(const std::string& lhs, std::string&& rhs);
However, if you would do host.c_str() + "connection", the compiler would be looking for a function that looks like:
??? operator+(const char* lhs, const char* rhs);
Which doesn't exist in the standard library.
The expression:
"s \"test" + host.c_str() + "connection\"
will try to add pointers to char. This cannot work. You might be
looking for a string class?
std::string host = "bar";
// this is quite inefficient
func("foo" + host + "baz");
// this is somewhat better
std::string x = "foo";
x.append(host);
x.append("baz");
func(x);
Related
This question already has answers here:
String concatenation
(5 answers)
Concatenate two string literals
(7 answers)
Closed 9 years ago.
I am a novice in C++ and i am referring Accelerated C++. While trying one of its exercise questions which says:
Are the following definitions valid? Why or why not?
const std::string exclam = "!";
const std::string message = "Hello" + ", world" + exclam;
When i tried & executed the program i am getting an error as:
invalid operands of types to binary operator +.
But the following code works perfectly fine:
const std::string hello = "Hello";
const std::string message = hello + ", world" + "!";
I am not clear with its execution! why this concatenation in the first case not working?
Thanks!I am using DEV C++.
In the first expression
"Hello" + ", world"
the compiler needs to find a suitable function such as operator+(const char *, const char *).
No such function exists, so it cannot compile.
Conversely,
hello + ", world"
is looking for one matching operator+(const std::string&, const char*), and that overload does exist (it is provided by std::string).
Note that you even if you could write your own overload:
std::string operator+ (const char *left, const char *right)
{
return std::string(left) + right;
}
(and you can't, as Praetorian points out) it wouldn't be a good idea.
Firstly, with primitive arguments, you'd lose ADL (ie, if the standard library put the operator in namespace std, it wouldn't normally be visible outside).
Secondly, if you have other libraries with their own string representations (eg. pre-STL stuff like RogueWave, or Qt, etc. etc.) they'd be equally justified in providing an overload for their string type, and the compiler would have no way to choose between them.
It's because "Hello" is not, when the compiler reads it, a std::string, but const char * - which means you can't use it for +.
You can trivial fix it with:
const std::string message = std::string("Hello") + ...
"Hello" is not a string, in the sense that it's not an object of type std::string. It's a string literal, which is an array of characters. You can't concatenate two literals with +, but you can concatenate a std::string with an array (and vice versa).
"Hello" + ", world" + exclam is equivalent to ("Hello" + ", world") + exclam, and so doesn't work because it tries to concatenate two literals. However, you could concatenate them without the + operator: "Hello" ", world" + exclam
hello + ", world" + "!"; is equivalent to (hello + ", world") + "!". It concatenates a std::string with a literal; the result is a new std::string which is then concatenated with the second literal. Both concatenations are allowed.
The reason is that C++ is a member of a language family that has slowly evolved over the last half century or so, and still has vestigial remnants of ancient languages around the edges.
"Hello" and ", world" in the first example are const char* objects. There is no defined way to add two character arrays together due to the nature of pointers, although logically it seems fine.
In the second example, hello is a std::string objects which has a + operator defined so that when you write:
hello + ", world"
it creates a new std::string object containing both pieces of content.
I need a char* for my filename. It should be something like this: cards/h11.bmp
i have a function I cobbled together from various SO articles:
char* getFileName(int* pc1_no, char* suite)
{
int number;
char pCard1[80];
strcpy_s(pCard1, "cards/");
strcat_s(pCard1, suite);
number = *pc1_no;
cout << number << endl;
string str = to_string(number);
char const *pchar = str.c_str();
strcat_s(pCard1, pchar);
strcat_s(pCard1, ".bmp");
return pCard1;
}
Which of course, returns garbage. I don't quite get getting the pointer value. I pretty sure I have made a dumb mistake with the pointer. Thanks in advance.
The best way to do all this is get rid of all the pointers and use a string all of the way through:
std::string getFileName(int pc1_no,
const std::string & suite)
{
std::string pCard1 = "cards/" + suite + std::to_string(pc1_no) + ".bmp";
return pCard1;
}
or if building to older C++ standards where std::to_string is not available:
std::string getFileName(int pc1_no,
const std::string & suite)
{
std::stringstream pCard1;
pCard1<< "cards/" << suite << pc1_no << ".bmp";
return pCard1.str();
}
Rational
char pCard1[80];
is a local variable. It will die at the end of the function, so the function returns a pointer to invalid memory. Many bad things can happen as a result and few good. Watch out for the few good. They are liars waiting for the most opportune time to strike.
The simplest solution maintaining OP's structure is use a std::string to perform string manipulation inside the function.
std::string getFileName(int* pc1_no, char* suite)
{
std::string pCard1 = "cards/";
pCard1 += std::string(suite);
pCard1 += std::to_string(*pc1_no);
pCard1 += std::string(".bmp");
return pCard1;
}
The above is Horrible Code, both excessively verbose and inefficient, but we've already covered the right way in the intro. This is just a touch-point on the logical progression to that right way.
Faster and less complex is to take advantage of std::stringstream's ability to format c-style strings and numbers without any outside help. This approach is probably the best up until std::to_string became available in the C++11 standard.
std::string getFileName(int* pc1_no, char* suite)
{
std::stringstream pCard1;
pCard1<< "cards/" << suite << *pc1_no << ".bmp";
return pCard1.str();
}
There is the possibility of a performance penalty returning the string by value, but the compilers of the past few decades are good at detecting and taking advantage of opportunities to omit unnecessary copying and employing move semantics behind the scenes.
Returning string by value is also far less error-prone than dynamically allocating storage and returning the storage to a caller with the expectation that the caller release it. Any performance penalty that may remain is highly likely to be worth the price. Profile the code to be sure.
Improving the function call:
Passing pc1_no in as a pointer is not helping in any way. Unless you need to modify the value inside the function, just pass by value. If you do need to change the value, prefer a reference.
std::string getFileName(int pc1_no, char* suite)
If you try to pass a string literal: eg:
getFileName(&somenumber, "string literal");
string literals may be in non-writable memory and are always const char * in C++. Passing a const value into a space that could attempt to change the value is bad form. This was allowed for backwards compatibility with C under older C++ Standards, though it may generate an warning, but is illegal in after the C++ 11 Standard.
If your function does not need to modify the contents of the char array, and this doesn't, it's a good practice to tag the string as const and allow the compiler to prevent accidents regardless of whether your compiler allows const to non-const assignments:
std::string getFileName(int pc1_no, const char* suite)
You may have more versatility if you use a reference to a const std::string as it is implicitly convertible from both const and non-const char arrays and allows the rest of your program to take advantage of the many benefits of std::string without needless calls to c_str and data.
std::string getFileName(int pc1_no,
const std::string & suite)
That brings us back to where this answer came in.
Here is how this code would look like in C++:
#include <string>
std::string getFileName(int* n, const std::string& suite) {
return "cards/" + suite + std::to_string(*n) + ".bmp";
}
Even better would be to take the first parameter by value (int n) and dereference at the call site if necessary.
char* getFileName(int pc1_no, char* suite)
{
static char pCard1[80]; // Guarantee safe pointer return
strcpy_s(pCard1, "cards/");
strcat_s(pCard1, suite);
string str = to_string(pc1_no);
char const *pchar = str.c_str();
strcat_s(pCard1, pchar);
strcat_s(pCard1, ".bmp");
return pCard1; // Now you don't lose the result.
}
Or you can use recommended C++ style which is already answered.
I'm working through an old book of C++ at the moment, and in it was developed a "rational numbers" class to introduce the idea of operator overloading, etc. Here's some example code from the book:
interface:
const Rational operator+(const Rational& Rhs) const;
implementation:
const Rational Rational::operator+(const Rational& Rhs) const
{
Rational Answer(*this);
Answer += Rhs;
return Answer;
}
The copy constructor does what you think it would do, and the += operator is correctly overloaded.
I decided to practice a bit by implementing a string class, and so I took a similar approach. My += overload works fine, but the + seems to have no effect ultimately.
interface:
const String operator+(const String&) const;
implementation:
const String String::operator+(const String& Rhs) const
{
String Answer (*this);
Answer += Rhs;
return Answer;
}
where the copy constructor (which works) is defined as such:
String::String(const String& str)
{
unsigned _strlen = str.len() + 1;
content = new char[_strlen];
std::memcpy(content, str.content, _strlen);
length = _strlen - 1;
content[length] = '\0';
}
and += is overloaded by the following:
const String& String::operator+=(const String& Rhs)
{
unsigned _Addl = Rhs.len();
unsigned newLen = _Addl + length; //length is member variable -- current length of content
content = (char*) realloc( content, newLen+1 );
std::memcpy(content+length, Rhs.content, _Addl);
content[newLen] = '\0';
return *this;
}
However -- while I can get proper output for +=, the + operator is failing to actually return a concatenated string. With debugging outputs inside of the function, Answer is holding the correct content, but it returns the original String instead of the concatenated one. I have a feeling this is something to do with const's being everywhere, but I've tried without it as well to no good fortune.
Test Code:
(in main):
String s1 ("Hello");
String s2 (" World!");
String s3 = (s1+s2); //prints "Hello" when s3 is output
cout << (s1+s2) << endl; //prints "Hello"
String constructor for const char*
String::String(const char* str)
{
unsigned _strlen = strlen(str) + 1;
content = new char[_strlen];
std::memcpy(content, str, _strlen);
length = _strlen - 1;
content[length] = '\0';
}
Your String::operator+=(), despite your claim it is properly implemented, is not properly implemented.
Firstly, realloc() returns NULL if it fails, and your code is not checking for that.
Second, and more critical, is that the length member is not being updated. Since your code calls the len() member function to get the length of one string, and uses the length member to get the length of the other, all of your functions need to ensure those two methods are in sync (i.e. that they give consistent results for a given instance of String). Since length is not being updated, your code does not ensure that.
There are probably better approaches than using C-style memory allocation as well, but (assuming this is a learning exercise) I'll leave that alone.
You've given no pertinent code for your Rational class but, if it is not working, your code presumably exhibits similar inconsistencies between what various constructors and member functions do.
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.
I have my own class that represents a custom string class. I'm using VS2012RC. I have overloaded some operators of my class CustomString.
Here's some code:
CustomString::CustomString(string setstr)
{
str = setstr;
}
CustomString::operator const char *()
{
return (this->str.c_str());
}
CustomString &CustomString::operator = (char *setstr)
{
str = setstr;
return *this;
}
I can define my object and use it like this:
CustomString str = "Test string";
and i can print the result as:
printf(str);
printf((string)(str).c_str());
printf((string)(str).data());
printf("%s\n",(string)(str).c_str());
printf("%s\n",(string)(str).data());
And there is not any error.
But if i use it like this:
printf("%s\n", str);
There is an exception in msvcr110d.dll (error in memory access)
Why printf(str) is ok, but printf("%s\n",str) is not ok?
How can i modify my code to use printf("%s\n",str) ?
...
After hours of googling, I found that explict cast (string), static_cast (str) and _str() method are add a null-terminated chars: '\0';
i've modified my code as:
printf("%s\n",str + '\0');
and it's worked!
Is there any way to modify my custom constructor to add a null-terminated string and pass a correct value with null-terminated chars to get working the following code:
printf("%s\n",str);
Don't use printf, its more C-like than C++. Instead, use iostreams, which provide a facility for you to format your own custom classes and send the to a file or stdout.
Here's a quick (untested) example that might work for you:
std::ostream& operator<< (std::ostream &os, const CustomString& str)
{
os << str.data();
return os;
}
and you'd print your custom string to stdout by doing something like
CustomString str;
// put some text in the custom string, then:
std::cout << str << std::endl;
You can't (at least not in a portable way). printf looks at the object passed as parameter and treats it as a %s, which is a char array. You run into undefined behavior. Also, the parameters passed to printf are, sort of say, type-less.
Why printf(str) is ok?
Because the first parameter is types, and is a const char*. The implicit cast is made via your operator. The rest of the parameters don't behave the same.
I'd use cout instead, and overload operator << (ostream&, const CustomString&).
Don't do this:
I said you can't, in a portable way. For a class like
class CustomString
{
char* str;
//...
};
that might work, because of how classes are represented in memory. But, again, it's still undefined behavior.
printf is defined as
int printf(char const *fmt, ...)
passing a class or structure to a ... argument list has undefined behaviour and may work or crash, or just do something random (I've seen all 3) depending on the class and the compiler.
printf(str)
requires a char *, and the compiler finds you have an appropriate casting operator, so it invokes it. Note that this is pretty dodgy as you have no idea if str might or might not have a % in it.
So, you to do want printf("%s", str) but as you have said, that doesn't work. Some compilers will give you a warning (though 'warning: This will crash' as produced by gcc isn't, in my opinion, terribly well thought out), so you have to force it to be cast to a string. So, your best solution is to explicitly cast it yourself,
printf("%s", static_cast<char const *>(str));
I'm not sure how much code all of the examples you've got there would require, as most of them are going to involve constructing a std::string from your custom string, then outputting it, then deleting the std::string.
You have to use printf("%s\n", str.c_str());. %s expects a char array and you gave it a CustomString object which is something different. You have to get char array from the string by calling c_str() function.