tags:

views:

159

answers:

6

I have some integers, let's say one two and three. I wish to create a string such as

char* example = "There are " + one + " bottles of water on " + 
    two + " shelves in room number " + three + "\n".`  

This doesn't work in C/C++. How can I store that type of value in a char*?

+6  A: 

C++ isn't VB. But you have numerous options.

stringstream:

#include <sstream>
#include <string>

stringstream ss;
ss<< "There are " << one << " bottles of water on " << two << " shelves in room number " << three;
string s = ss.str();

boost/format:

#include <boost/format.hpp>
#include <string>

string s = (boost::format("There are %1% bottles on %2% shelves in room number %3%")%one%two%three).str();

old-school (hacky, unsafe, bad, dont do it)

char buffer[1024] = {};
sprintf(buffer, "There are %d bottles on %d shelves in room number %d", one, two, three); 
John Dibling
You can also `lexical_cast` the numbers and concatenate.
GMan
+1  A: 

sprintf is an option (as Ólafur wrote), but deprecated. Use snprintf when you can - sprintf can lead to buffer overruns and cause awful crashes and bugs if you're not very careful with the size of your buffer and inputs.

char output[256];
snprintf(output, sizeof(output), "There are %d bottles of water on %d shelves 
    in room number %d\n", one, two, three);
EboMike
"sprintf is good" yeah, ok. How did you come up with the magic number `256`? Is it big enough? It it wasteful? How can you be sure that `one`, `two` and `three` are `ints`? If somee maint programmer comes along and changes one of the types are you prepared to explode?
John Dibling
`snprintf` also deprecated in C++
Steve Townsend
@Steve: I don't believe it is formally deprecated (and neither is `sprintf`). People prefer not to use it.
Steve Jessop
+1  A: 
#include <sstream>

std::stringstream strVal;

strVal << std::string("There are ") << one << std::string(" bottles of water on ") << 
    two << std::string(" shelves in room number ") << three << std::endl;

std::string copyStr(strVal.str());
const char * example = copyStr.c_str();
Steve Townsend
I think you need to put that in to a `string` and take the `c_str()`
John Dibling
@John - Yes, or preferably forget the `char*` altogether
Steve Townsend
@Steve: Even better
John Dibling
@Steve: But now you put it in to a temporary that has evaporated after the `;`
John Dibling
@John, yes I forgot the `strdup` call. Thanks for the code review.
Steve Townsend
May I ask what are those temporary strings for? I mean the ones constructed from the string literals.
Maciej Hehl
@Maciej - clarification for OP - not necessary to make it work
Steve Townsend
A: 

I like the stringstream approach, but I use a class to simplify its usage. See my answer to another SO question for more details:

string myString = MakeString() << "There are " << one << " 
      bottles of water on " << two << " shelves in room number " << three;

char * example = myString.c_str(); // if you really need a char *
e.James
+6  A: 

In C there's more than one way to do it, depending how you would like to allocate the memory[*]. For the straightforward option of allocating it from the heap:

len = snprintf(0, 0, "%d bottles, %d shelves, room %d\n", one, two, three);
char *result = malloc(len+1);
if (result == 0) { /* handle error */ }
snprintf(result, len+1, "%d bottles, %d shelves, room %d\n", one, two, three);

/* some time later */
free(result);

Beware non-standard implementations of snprintf, that don't return the length when the buffer is exceeded. Check your documentation.

In C++, snprintf is not in the standard, and even where it is available, the above code would need to cast the result of malloc[**]. C++ adds the option of using stringstreams:

std::stringsteam r;
r << one << " bottles, " << two << " shelves, room " << three << "\n";
std::string result = r.str();

// if you absolutely need a char*, use result.c_str(), but don't forget that
// the pointer becomes invalid when the string, "result" ceases to exist.

This saves messing with buffer lengths, makes resource management easier, and avoids the risk with printf and friends that you could pass an argument of the wrong type for the format specifier. It's usually the preferred option.

It is however less flexible in some circumstances: the format is hard-wired into the code rather than contained in a format string, so it's harder to make the text configurable. It can also be a bit harder to read, for instance it's not at all uncommon to leave out a space character on the first version of any such line of code. But if you want to use the snprintf approach in C++, and snprintf is available in your implementation, then you can take advantage of C++'s easier memory management as follows:

len = std::snprintf(0, 0, "%d bottles, %d shelves, room %d\n", one, two, three);
std::vector<char> r(len+1);
std::snprintf(&r[0], r.size(), "%d bottles, %d shelves, room %d\n", one, two, three);

char *result = &r[0];
// again, "result" is only valid as long as "r" is in scope

[*] Note that you cannot "store" a string in a char*, because a char* is just a pointer. You can store a pointer to a string in a char*, but the string itself is a completely separate thing.

[**] because C and C++ ARE DIFFERENT LANGUAGES!

Steve Jessop
A: 

My solution:

template <typename T> std::string toStr(const T& something) {
    std::stringstream ss;
    ss << something;
    return ss.str();
}
...
std::string example = "There are " + toStr(one) + " bottles of water on " + 
                toStr(two) + " shelves in room number " + toStr(three) + "\n";
01d