How to properly use: const char * and std :: string?

TL: dr

How can I concatenate const char*

using std::string

, neat and elegant, without a lot of function calls. Ideally, in one function, call and get the exit const char*

. This is not possible, what is the optimal solution?

Initial task

The biggest barrier I've experienced in C ++ is how it handles strings. In my opinion, of all the widely used languages, it handles strings the worst. I've seen other similar questions that either have an answer that says "use std::string

" or simply indicate that one of the options will be better for your situation.

However, this is useless advice when trying to use strings dynamically, like they are used in other languages. I cannot guarantee that I can always use std::string

, and for the times when I have to use const char*

, I hit the obvious "permanently, you cannot connect it" wall.

Every solution to any string manipulation problem I've seen in C ++ requires repetitive multiple lines of code that only work well for that string format. I want to be able to concatenate any character set with a character, +

or use a simple function format()

like I can in C # or Python. Why isn't there an easy option?

Current situation

Standard output

I am writing a DLL and so far I have been outputting text to cout

using a statement <<

. So far so good, using simple char arrays in the form:

cout << "Hello world!"

      

Runtime strings

Now it comes to the point that I want to build a string at runtime and store it with a class, this class will contain a string that reports some errors so that they can be captured by other classes and maybe sent in cout

later, the string will be set function SetReport(const char* report)

. So I really don't want to use more than one line for this, so I go 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 lines

Right. So I am trying to add two or more const char*

together and this is just not an option. So I find the main suggestion here is to use std::string

, something weird, that by typing "Hello world!"

, you don't just give you one, but give it the option:

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! This is one of the ugliest codes I've seen. We can simplify this:

SetReport(std::string("Failure in ") + __FUNCTION__ + ": foobar was " + std::to_string(foobar) + "\n");

      

Possibly the worst way I've come across to go to simple string concatenation of a single string, but should it be okay now?

Convert back to constant

No, if you are working on a DLL, what I usually do is because I like 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 this report line to a member variable of the class as std::string

, the compiler issues a warning:

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 have found other than "ignore the warning" (bad practice!) Is to use const char*

for a member variable, not std::string

, but that's not really a solution, because now you need to convert your ugly concatenated (but dynamic ) the string back to the const char array you need. But you can't just tag .c_str()

at the end (although why do you want it because the second concatenation gets funnier?), You have to make sure you std::string

don't clear your newly built one and leave you trash. Therefore, you have to do it inside the function that receives the string:

const std::string constString = (input);
m_constChar = constString.c_str();

      

This is madness. Since I was now iterating over several different types of strings, made my code ugly, added more lines than needed, and just to concatenate some characters. Why is it so hard?

Decision?

So what's the solution? I feel like I should be able to do a function that concatenates const char*

together but also handles other types of objects like std::string

, int

or double

I'm sure this should be capable of in one line and but I can't find any examples of achieving it. Should I be working with char*

and not with the persistent option, although I read that you should never change the value char*

, so how would that help?

Are there any experienced C ++ programmers who have solved this problem and are now comfortable with C ++ strings, what is your solution? Is there no solution? It's impossible?

+3


source to share


4 answers


One of the simplest solutions is to use an empty C ++ string. Here I am declaring an empty string variable with a name _

and using it before string concatenation. Make sure you always put it in front.

#include <cstdio>
#include <string>

using namespace std;
string _ = "";

int main() {
        char s[] = "chararray";
        string result =
                _ + "function name = [" + __FUNCTION__ + "] "
                "and s is [" + s + "]\n";
        printf( "%s", result.c_str() );
        return 0;
}

      

Output:



function name = [main] and s is [chararray]

      

As for __FUNCTION__

, I found that in Visual C ++ it is a macro and in GCC it is a variable, so SetReport("Failure in " __FUNCTION__ "; foobar was " + foobar + "\n");

will only work in Visual C ++. See: https://msdn.microsoft.com/en-us/library/b0084kay.aspx and https://gcc.gnu.org/onlinedocs/gcc/Function-Names.html

The solution using an empty string variable above should work in both Visual C ++ and GCC.

+1


source


The standard way to construct a string, formatting non-string types as strings, is a string stream

#include <sstream>

std::ostringstream ss;
ss << "Failure in " << __FUNCTION__ << ": foobar was " << foobar << "\n";
SetReport(ss.str());

      

If you do this often, you can write a variation pattern to do this:

template <typename... Ts> std::string str(Ts&&...);
SetReport(str("Failure in ", __FUNCTION__, ": foobar was ", foobar, '\n'));

      



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 by one; and, assuming that foobar

is std::string

, which can be concatenated with string literals with +

:

SetReport("Failure in " __FUNCTION__ ": foobar was " + foobar + "\n");

      

If foobar

is a numeric type, you can use std::to_string(foobar)

to convert it.

+3


source


Regular string literals (like "abc"

and __FUNCTION__

) and char const*

do not support concatenation. It's just C-style char const[]

and char const*

.

Solutions should use some string formatters or libraries, such as:

  • std::string

    and concatenation with +

    . May include too many unnecessary distributions if operator+

    not using expression templates.
  • std::snprintf

    ... This one does not allocate buffers for you, not a type safe, so people end up creating wrappers for it.
  • std::stringstream

    ... Ubiquitous and standard, but its syntax is awkward at best.
  • boost::format

    ... The type is safe but reportedly slow.
  • cppformat . Reported to be modern and fast.
+2


source


My decision

I kept experimenting with different things, and I have a solution that combines tivn's answer, which involves creating an empty string that will help concatenate long arrays std::string

and characters, and a function that lets you use a single string copying this std::string

into const char*

, which is safe use when the string object leaves the scope.

I would use 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 generic so I can't rely on them.

Here is my solution:

strings.h

#ifndef _STRINGS_H_
#define _STRINGS_H_

#include <string>

// tivn empty string in the header file
extern const std::string _;

// My own version of .c_str() which produces a copy of the contents of the string input
const char* ToCString(std::string input);

#endif

      

Strings.cpp

#include "Strings.h"

const std::string str = "";

const char* ToCString(std::string input)
{
    char* result = new char[input.length()+1];
    strcpy_s(result, input.length()+1, input.c_str());
    return result;
}

      

Using

m_someMemberConstChar = ToCString(_ + "Hello, world! " + someDynamicValue);

      

I think this is pretty neat and works in most cases. Thanks to everyone who helped me with this.

0


source







All Articles