vsprintf-like function for strings?

I am trying to create a function like so:
1
2
3
4
5
6
7
8
void out(std::string format, ...)
{
va_list args;
va_start(args, format);
std::string formatted_string = v_format_string(format, args);
ostream_object << formatted_string;
va_end(args);
}

In the body of "v_format_string()", I allocate a certain amount of memory, store it in a std::unique_ptr<char[]> called 'p' by way of a call to 'p.reset(allocated_memory)', and then attempt to call:
 
n = vsnprintf(&p[0], allocated_size, format.c_str(), args);

and if 'n' is positive and less than 'allocated_size' the function allocates twice as much memory, storing it in 'p' with yet another call to 'reset()', and attempts another 'vsprintf()' call, repeating the process until enough memory is used; that's the plan. It seems, however, a segmentation fault occurs when I attempt to do so. Is my logic correct? Can a 'va_list' parameter not be referenced multiple times in the same function? More importantly, is there a vsprintf-like function for std::string's? Note: Since I am trying to get 'out()' to work similar to 'printf()', I cannot know the format strings ahead of time, if this fact makes any difference.

I appreciate Your help in advance.
Last edited on
If you want to use a std::string somewhere that requires a c-string, you have to use .c_str()
@Yay295: Thanks for pointing this out. However, as noted, in the body of "v_format_string()", I do use that member function. Or are You referring to another use?
You could give boost::format a shot. It looks like this:
1
2
boost::format f("%1% Not even %2% type specifier!");
std::string s = (f % std::string("Hey!") % 1).str();
It also accepts printf()-like format strings.
@helios: Thanks for the idea but I don't see how this would work inside 'v_format_string()'.
@xuinkrbin: I did notice that. It just seemed like you were asking if there was another way to do it. There isn't as far as I know (which admittedly isn't that far), so I was just confirming that what you did was correct.
I think he was suggesting it as an alternative to using v_format_string().
Actually, I was suggesting it instead of out(). Variadic functions are rather passé.
Sorry, that's what I meant... I don't know what I was thinking :)
Could vsnprintf() be used?
@helios: I don't see how One concludes "Variadic functions are rather passé".

@Yay295: Actually, I do have that call in code. I have a typo in the original post which I will correct if I can.

@Everyone: What I have posted is a stripped down version of the actual function, which performs other actions including, but not limited to, outputting the resulting string to a second stream, if this fact makes any difference.
I don't see how One concludes "Variadic functions are rather passé".
If you were programming in C, you'd have no comfortable alternative to variadic functions for passing an indeterminate amount of parameters of indeterminate type to an object. With more expressive power of C++ (function overloading, operator overloading, templates, etc. etc.) variadic functions are a step backwards in almost every situation.
Last edited on
@helios: Interesting explanation. In this particular project, I have literally over a thousand calls to 'printf()' which need to be replaced with this new function. In order to ease transition to the use of this new function, the hope was a drop-in replacement could be crafted. Do You have any additional insights to help in this regard?
Well, as a zero-effort drop-in replacement, you could do something like #defineing printf to refer to an object of your own that overloads the comma operator. That should be possible with variadic macros.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <cstdio>
#include <string>
#include <cstdarg>
#include <stdexcept>

std::string out( std::string format, ... )
{
    va_list args, args_copy ;
    va_start( args, format ) ;
    va_copy( args_copy, args ) ;

    const auto sz = std::vsnprintf( nullptr, 0, format.c_str(), args ) + 1 ;

    try
    {
        std::string result( sz, ' ' ) ;
        std::vsnprintf( &result.front(), sz, format.c_str(), args_copy ) ;

        va_end(args_copy) ;
        va_end(args) ;

        // do whatever else with result

        return result ;
    }

    catch( const std::bad_alloc& )
    {
        va_end(args_copy) ;
        va_end(args) ;
        throw ;
    }
}

http://coliru.stacked-crooked.com/a/511842875075aa26
@JLBorges: YES!!! VA_COPY!!! I plunked that into 'v_format_string()' and all is well. ... At least as far as I can tell. ;-) Thank You!

@helios: I will definitely keep Your points in Mind going forward and see if I cannot nudge the project in the direction to which You have hinted/implied/suggested/whatever ...
As far as possible, unless you are dealing with a toy code-base, avoid #define of well-known identifiers like printf().
And avoid overloading the sequencing (comma) operator; it engenders behaviour that is unintuitive.

A set of overloaded functions is all that is required.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <string>
#include <boost/format.hpp>

namespace impl
{
    namespace
    {
        boost::format& out( boost::format& fmt ) { return fmt ;}

        template < typename T >
        boost::format& out( boost::format& fmt, const T& v ) { return fmt % v ;}

        template < typename FIRST, typename... REST >
        boost::format& out( boost::format& fmt, const FIRST& first, const REST&... rest )
        { return out( out( fmt, first ), rest... ) ;}
    }
}

template < typename... ARGS > std::string out( std::string format, const ARGS&... args )
{
    boost::format fmt(format) ;
    impl::out( fmt, args... ) ;

    const std::string result = fmt.str() ;
    // do whatever else with result

    return result ;
}

int main()
{
    std::cout << out( "trivial\n" ) ;
    std::cout << out( "non-trivial: %+d %c %2.2f '%s'\n", 12345, 'A', 67.899, "hello" ) ;
}

http://coliru.stacked-crooked.com/a/5388f19b580b66a3

Caveat: With boost format, 100% fidelity to printf() is compromised.
http://www.boost.org/doc/libs/1_55_0/libs/format/doc/format.html#printf_differences
Adding to what JLBorges says about macro's and 'printf', defining 'printf' (or any other identifier in the standard library or any other reserved identifier) to be a macro results in undefined behavior according to the ISO C/C++ standards. As such, I might advise NEVER defining a macro with such a name.
Topic archived. No new replies allowed.