Write a C ++ function format_string for formatting, for example sprintf std :: string

For convenient use, I want to write a formatting function similar to sprintf, only returning std :: string, for example:

std::string format_string(const char* format, ...)

I can use vsnprintfthere, but there is a problem - I do not know in advance how long the temp buffer should be. Microsoft has a feature _vscprintfthat can do this, but I think it is not portable?

One option is that the temp buffer starts some known size, and then increases it if it is not enough with vsnprintf. Is there a better approach? Thanks


PS Please give an answer without Boost. I know about Boost, but I'm curious how to implement it without.

+3
source share
4 answers

One option is that the temp buffer starts some known size and then increases it if it is not enough vsnprintf. Is there a better approach? Thanks

You can use vasprintf(), but it makes unnecessary heap allocation - it is unlikely to be faster on average. Using alloca, you can avoid heaps. Or you can write directly in the return string: NRVO should avoid copying, and with the semantics of movement C ++ 11 limits the cost of sans-NRVO to a few sets of pointers.

#include <cstdio>
#include <cstdarg>
#include <alloca.h>

#include <string>
#include <iostream>

std::string stringf(const char* format, ...)
{
    va_list arg_list;                                                           
    va_start(arg_list, format);                                                 

    // SUSv2 version doesn't work for buf NULL/size 0, so try printing
    // into a small buffer that avoids the double-rendering and alloca path too...
    char short_buf[256];                                                        
    const size_t needed = vsnprintf(short_buf, sizeof short_buf,
                                    format, arg_list) + 1;
    if (needed <= sizeof short_buf)
        return short_buf;

    // need more space...

    // OPTION 1
    std::string result(needed, ' ');
    vsnprintf(result.data(), needed, format, arg_list);
    return result;  // RVO ensures this is cheap
 OR
    // OPTION 2
    char* p = static_cast<char*>(alloca(needed)); // on stack
    vsnprintf(p, needed, format, arg_list);
    return p;  // text copied into returned string
}

int main()                                                                      
{                                                                               
    std::string s = stringf("test '%s', n %8.2f\n", "hello world", 3.14);       
    std::cout << s;                                                             
}

A simpler and initially faster option:

    std::string result(255, ' ');  // 255 spaces + NUL
    const size_t needed = vsnprintf(result.data(), result.size() + 1,
                                    format, arg_list);
    result.resize(needed); // may truncate, leave or extend...
    if (needed > 255) // needed doesn't count NUL
        vsnprintf(result.data(), needed + 1, format, arg_list);
    return result;

, 256 , : , /. , [ shrink_to_fit] http://en.cppreference.com/w/cpp/string/basic_string/shrink_to_fit), , ( " " ). , char.

+2

C99 snprintf , , vsnprintf. (v)snprintf, . vasprintf, .

++ Format, printf, Boost Format, .

+3

(.. , , ), , asprintf vasprintf GNU (: C, POSIX, GCC glibc).

printf vsprintf, , .

int asprintf( char **strp, const char *fmt, ... );
int vasprintf( char **strp, const char *fmt, va_list ap );

, . snprintf.

0

- . printf -like, . , .

This is not an all or nothing sentence; you can use sprintffor selected types. For instance. it may be easier to use sprintf(buf, "%6.4f", dbltemp);when your input format string contains an argument %6.4fbut is %shandled better by itself (simple memcpy).

0
source

Source: https://habr.com/ru/post/1774892/


All Articles