2

All the examples I have sees on the web for creating a string with sprintf use a statically declared array whose size is fixed.

#include <stdio.h>
#include <math.h>

int main()
{
   char str[80];

   sprintf(str, "Value of Pi = %f", M_PI);
   puts(str);

   return(0);
}

I want to be able to do this with a dynamically sized array in the simplest way possible. I have to write some code that will print the values that compose the array:

    printf("id=%s %s-array is: ", id.value(), name);
    for (unsigned int i = 0; i < depths.size(); i++) {
        printf("%f,", depths[i]);
    }
    printf("\n");

But I don't want to do this with separate printfs.. I want to be able to put it all in a buffer that fits the string I am writing at runtime. I am inclined to think that sprintf is the best way to do this, but if there are other functions I can use in C++. Let me know.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
Flethuseo
  • 5,969
  • 11
  • 47
  • 71

4 Answers4

5

The idiomatic C++ way (as @Troy points out) is using a string stream:

#include <cmath>
#include <iostream>
#include <sstream>
#include <string>

int main()
{
   std::ostringstream ss;
   ss << "Value of Pi = " << M_PI;

   std::string str = ss.str();

   std::cout << str << '\n';

   return(0);
}
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • 1
    Why didn't you use std::endl? – SoapBox Oct 03 '13 at 21:37
  • @SoapBox Is there a reason to flush immediately? Always remember that `std::endl` does "print newline and flush." – Angew is no longer proud of SO Oct 03 '13 at 21:37
  • Do you always answer questions with questions? :-P I've always been told to prefer std::endl to \n just for "C++iness" since it almost never matters whether you flush right away or not. (When it does matter, obviously then you'd chose the one that provides the semantics you need.) – SoapBox Oct 03 '13 at 21:38
  • @SoapBox No, why? Just kidding, sorry. I just meant "I don't think there's a reason for the flush." – Angew is no longer proud of SO Oct 03 '13 at 21:40
  • I don't understand, it isn't working on my test app. I'm using visual studio 2010, building a win32 console app – Flethuseo Oct 03 '13 at 21:42
  • Error 1 error C2079: 'ss' uses undefined class 'std::basic_ostringstream<_Elem,_Traits,_Alloc>' c:\users\egutarra\documents\visual studio 2010\projects\testapp1\testapp1\testapp1.cpp 12 1 TestApp1 – Flethuseo Oct 03 '13 at 21:42
  • @Flethuseo Sorry, I missed one header; added it in the meantime. – Angew is no longer proud of SO Oct 03 '13 at 21:43
  • It's not recognizing M_PI.. Error 1 error C2065: 'M_PI' : undeclared identifier c:\users\egutarra\documents\visual studio 2010\projects\testapp1\testapp1\testapp1.cpp 14 1 TestApp1 – Flethuseo Oct 03 '13 at 21:44
  • @Flethuseo Add a define `_USE_MATH_DEFINES` before including ``. It's a Microsoft-specific thing. And I'd suggest you accept Troy's answer instead of mine - I only added mine (as wiki) because I thought he'd leave it as a comment. – Angew is no longer proud of SO Oct 03 '13 at 21:54
4

Go the more idiomatic way and use a std::ostringstream:

#include <sstream>
#include <iomanip>
#include <iostream>

int main()
{    
    std::ostringstream os;    
    os << "id=" << id.value() << " " << name << "-array is: ";
    for (unsigned int i = 0; i < depths.size(); i++) {
        os << std::fixed << depths[i] << ",";
    }    
    os << "\n";

    std::cout << os.str();
}

No need to worry out buffer size or memory allocation then ..

goji
  • 6,911
  • 3
  • 42
  • 59
2

You can use snprintf with a string length of zero to determine how many characters would have been printed. Then, allocate a buffer of this length, and re-traverse the list with the allocated buffer.

MooseBoys
  • 6,641
  • 1
  • 19
  • 43
1

You can build a C++ string using a printf-like call with a utility function such as:

#include <cstdarg>
#include <string>
#include <vector>

std::string build_string(const char* fmt, ...) {
    va_list args;
    va_start(args, fmt);
    size_t len = vsnprintf(NULL, 0, fmt, args);
    va_end(args);
    std::vector<char> vec(len + 1);
    va_start(args, fmt);
    vsnprintf(vec.data(), len + 1, fmt, args);
    va_end(args);
    return std::string(vec.begin(), vec.end() - 1);
}

std::string msg = build_string("Value of Pi = %f", M_PI) will work as expected, and you can use c_str() to pass the corresponding char * to functions that expect it (so long as you are careful that the string object is not destroyed before they are done).

user4815162342
  • 141,790
  • 18
  • 296
  • 355
  • I'm getting this warning: 1>c:\users\egutarra\documents\visual studio 2010\projects\testapp1\testapp1\testapp1.cpp(12): warning C4996: 'vsnprintf': This function or variable may be unsafe. Consider using vsnprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. – Flethuseo Oct 03 '13 at 21:34
  • it's suggesting me to use vsprintf_s.. is it better to use that one? – Flethuseo Oct 03 '13 at 21:34
  • @Flethuseo `vsnprintf` is no less safe than `vsnprintf_s`, so you probably want to add that `#define`, at least for that function. Ultimately it's up to you - the function as written in the answer is portable C++, while `vsnprintf_s` is a Microsoft-specific extension. – user4815162342 Oct 03 '13 at 21:35
  • I think this code will include the trailing NULL in the `std::string`. You could just use a correctly-sized `std::string` directly instead of the intermediate `std::vector` and `pop_back()` to strip the trailing NULL. – Blastfurnace Oct 03 '13 at 21:38
  • @Blastfurnace You're right about the trailing `\0`, thanks for the correction. I was wary of the further optimization of removing the `vector` since my memory seems to tell me that an `std::string` can be implemented as a non-contiguous buffer. – user4815162342 Oct 03 '13 at 21:43
  • You're correct, I've been spoiled by the C++11 standard behavior. – Blastfurnace Oct 03 '13 at 21:46