tl:dr
How can I concatenate
const char*
withstd::string
, neatly and elegantly, without multiple function calls. Ideally in one function call and have the output be aconst char*
. Is this impossible, what is an optimum solution?
Initial Problem
The biggest barrier I have experienced with C++ so far is how it handles strings. In my opinion, of all the widely used languages, it handles strings the most poorly. I've seen other questions similar to this that either have an answer saying "use std::string
" or simply point out that one of the options is going to be best for your situation.
However this is useless advice when trying to use strings dynamically like how they are used in other languages. I cannot guaranty to always be able to use std::string
and for the times when I have to use const char*
I hit the obvious wall of "it's constant, you can't concatenate it".
Every solution to any string manipulation problem I've seen in C++ requires repetitive multiple lines of code that only work well for that format of string.
I want to be able to concatenate any set of characters with the +
symbol or make use of a simple format()
function just how I can in C# or Python. Why is there no easy option?
Current Situation
Standard Output
I'm writing a DLL and so far I've been output text to cout
via the <<
operator. Everything has been going fine so far using simple char arrays in the form:
cout << "Hello world!"
Runtime Strings
Now it comes to the point where I want to construct a string at runtime and store it with a class, this class will hold a string that reports on some errors so that they can be picked up by other classes and maybe sent to cout
later, the string will be set by the function SetReport(const char* report)
. So I really don't want to use more than one line for this so I go ahead and write something like:
SetReport("Failure in " + __FUNCTION__ + ": foobar was " + foobar + "\n"); // __FUNCTION__ gets the name of the current function, foobar is some variable
Immediately of course I get:
expression must have integral or unscoped enum type
and...'+': cannot add two pointers
Ugly Strings
Right. So I'm trying to add two or more const char*
s together and this just isn't an option. So I find that the main suggestion here is to use std::string
, sort of weird that typing "Hello world!"
doesn't just give you one of those in the first place but let's give it a go:
SetReport(std::string("Failure in ") + std::string(__FUNCTION__) + std::string(": foobar was ") + std::to_string(foobar) + std::string("\n"));
Brilliant! It works! But look how ugly that is!! That's some of the ugliest code I've every seen. We can simplify to this:
SetReport(std::string("Failure in ") + __FUNCTION__ + ": foobar was " + std::to_string(foobar) + "\n");
Still possibly the worst way I've every encounter of getting to a simple one line string concatenation but everything should be fine now right?
Convert Back To Constant
Well no, if you're working on a DLL, something that I tend to do a lot because I like to unit test so I need my C++ code to be imported by the unit test library, you will find that when you try to set that report string to a member variable of a class as a std::string
the compiler throws a warning saying:
warning C4251: class 'std::basic_string<_Elem,_Traits,_Alloc>' needs to have dll-interface to be used by clients of class'
The only real solution to this problem that I've found other than "ignore the warning"(bad practice!) is to use const char*
for the member variable rather than std::string
but this is not really a solution, because now you have to convert your ugly concatenated (but dynamic) string back to the const char array you need. But you can't just tag .c_str()
on the end (even though why would you want to because this concatenation is becoming more ridiculous by the second?) you have to make sure that std::string
doesn't clean up your newly constructed string and leave you with garbage. So you have to do this inside the function that receives the string:
const std::string constString = (input);
m_constChar = constString.c_str();
Which is insane. Because now I traipsed across several different types of string, made my code ugly, added more lines than should need and all just to stick some characters together. Why is this so hard?
Solution?
So what's the solution? I feel that I should be able to make a function that concatenates const char*
s together but also handle other object types such as std::string
, int
or double
, I feel strongly that this should be capable in one line, and yet I'm unable to find any examples of it being achieved. Should I be working with char*
rather than the constant variant, even though I've read that you should never change the value of char*
so how would this help?
Are there any experienced C++ programmers who have resolved this issue and are now comfortable with C++ strings, what is your solution? Is there no solution? Is it impossible?
One of the simplest solution is to use an C++ empty string. Here I declare empty string variable named
_
and used it in front of string concatenation. Make sure you always put it in the front.Output:
Regarding
__FUNCTION__
, I found that in Visual C++ it is a macro while in GCC it is a variable, soSetReport("Failure in " __FUNCTION__ "; foobar was " + foobar + "\n");
will only work on Visual C++. See: https://msdn.microsoft.com/en-us/library/b0084kay.aspx and https://gcc.gnu.org/onlinedocs/gcc/Function-Names.htmlThe solution using empty string variable above should work on both Visual C++ and GCC.
My Solution
I've continued to experiment with different things and I've got a solution which combines tivn's answer that involves making an empty string to help concatenate long
std::string
and character arrays together and a function of my own which allows single line copying of thatstd::string
to aconst char*
which is safe to use when the string object leaves scope.I would have used Mike Seymour's variadic templates but they don't seem to be supported by the Visual Studio 2012 I'm running and I need this solution to be very general so I can't rely on them.
Here is my solution:
Strings.h
Strings.cpp
Usage
I think this is pretty neat and works in most cases. Thank you everyone for helping me with this.
Plain string literals (e.g.
"abc"
and__FUNCTION__
) andchar const*
do not support concatenation. These are just plain C-stylechar const[]
andchar const*
.Solutions are to use some string formatting facilities or libraries, such as:
std::string
and concatenation using+
. May involve too many unnecessary allocations, unlessoperator+
employs expression templates.std::snprintf
. This one does not allocate buffers for you and not type safe, so people end up creating wrappers for it.std::stringstream
. Ubiquitous and standard but its syntax is at best awkward.boost::format
. Type safe but reportedly slow.The standard way to build a string, formatting non-string types as strings, is a string stream
If you do this often, you could write a variadic template to do that:
The implementation is left as an exercise for the reader.
In this particular case, string literals (including
__FUNCTION__
) can be concatenated by simply writing one after the other; and, assumingfoobar
is astd::string
, that can be concatenated with string literals using+
:If
foobar
is a numeric type, you could usestd::to_string(foobar)
to convert it.