c++ snprintf - std::cadena de formato como sprintf





convertir entero (25)


[edit '17 / 8/31] Agregando una versión variada templada 'vtspf (..)':

template<typename T> const std::string type_to_string(const T &v)
{
    std::ostringstream ss;
    ss << v;
    return ss.str();
};

template<typename T> const T string_to_type(const std::string &str)
{
    std::istringstream ss(str);
    T ret;
    ss >> ret;
    return ret;
};

template<typename...P> void vtspf_priv(std::string &s) {}

template<typename H, typename...P> void vtspf_priv(std::string &s, H h, P...p)
{
    s+=type_to_string(h);
    vtspf_priv(s, p...);
}

template<typename...P> std::string temp_vtspf(P...p)
{
    std::string s("");
    vtspf_priv(s, p...);
    return s;
}

que en realidad es una versión delimitada por comas (en su lugar) de los << operadores que a veces dificultan, utilizados de esta manera:

char chSpace=' ';
double pi=3.1415;
std::string sWorld="World", str_var;
str_var = vtspf("Hello", ',', chSpace, sWorld, ", pi=", pi);


[editar] Adaptado para hacer uso de la técnica en la respuesta de Erik Aronesty (arriba):

#include <string>
#include <cstdarg>
#include <cstdio>

//=============================================================================
void spf(std::string &s, const std::string fmt, ...)
{
    int n, size=100;
    bool b=false;
    va_list marker;

    while (!b)
    {
        s.resize(size);
        va_start(marker, fmt);
        n = vsnprintf((char*)s.c_str(), size, fmt.c_str(), marker);
        va_end(marker);
        if ((n>0) && ((b=(n<size))==true)) s.resize(n); else size*=2;
    }
}

//=============================================================================
void spfa(std::string &s, const std::string fmt, ...)
{
    std::string ss;
    int n, size=100;
    bool b=false;
    va_list marker;

    while (!b)
    {
        ss.resize(size);
        va_start(marker, fmt);
        n = vsnprintf((char*)ss.c_str(), size, fmt.c_str(), marker);
        va_end(marker);
        if ((n>0) && ((b=(n<size))==true)) ss.resize(n); else size*=2;
    }
    s += ss;
}

[respuesta anterior]
Una respuesta muy tardía, pero para aquellos que, como yo, les gusta la manera 'sprintf': he escrito y están usando las siguientes funciones. Si te gusta, puedes expandir las% -options para que se ajusten más a las sprintf; los que hay actualmente son suficientes para mis necesidades. Utiliza stringf () y stringfappend () igual que lo haría con sprintf. Solo recuerda que los parámetros para ... deben ser tipos de POD.

//=============================================================================
void DoFormatting(std::string& sF, const char* sformat, va_list marker)
{
    char *s, ch=0;
    int n, i=0, m;
    long l;
    double d;
    std::string sf = sformat;
    std::stringstream ss;

    m = sf.length();
    while (i<m)
    {
        ch = sf.at(i);
        if (ch == '%')
        {
            i++;
            if (i<m)
            {
                ch = sf.at(i);
                switch(ch)
                {
                    case 's': { s = va_arg(marker, char*);  ss << s;         } break;
                    case 'c': { n = va_arg(marker, int);    ss << (char)n;   } break;
                    case 'd': { n = va_arg(marker, int);    ss << (int)n;    } break;
                    case 'l': { l = va_arg(marker, long);   ss << (long)l;   } break;
                    case 'f': { d = va_arg(marker, double); ss << (float)d;  } break;
                    case 'e': { d = va_arg(marker, double); ss << (double)d; } break;
                    case 'X':
                    case 'x':
                        {
                            if (++i<m)
                            {
                                ss << std::hex << std::setiosflags (std::ios_base::showbase);
                                if (ch == 'X') ss << std::setiosflags (std::ios_base::uppercase);
                                char ch2 = sf.at(i);
                                if (ch2 == 'c') { n = va_arg(marker, int);  ss << std::hex << (char)n; }
                                else if (ch2 == 'd') { n = va_arg(marker, int); ss << std::hex << (int)n; }
                                else if (ch2 == 'l') { l = va_arg(marker, long);    ss << std::hex << (long)l; }
                                else ss << '%' << ch << ch2;
                                ss << std::resetiosflags (std::ios_base::showbase | std::ios_base::uppercase) << std::dec;
                            }
                        } break;
                    case '%': { ss << '%'; } break;
                    default:
                    {
                        ss << "%" << ch;
                        //i = m; //get out of loop
                    }
                }
            }
        }
        else ss << ch;
        i++;
    }
    va_end(marker);
    sF = ss.str();
}

//=============================================================================
void stringf(string& stgt,const char *sformat, ... )
{
    va_list marker;
    va_start(marker, sformat);
    DoFormatting(stgt, sformat, marker);
}

//=============================================================================
void stringfappend(string& stgt,const char *sformat, ... )
{
    string sF = "";
    va_list marker;
    va_start(marker, sformat);
    DoFormatting(sF, sformat, marker);
    stgt += sF;
}

Tengo que formatear std::string con sprintf y enviarlo al flujo de archivos. ¿Cómo puedo hacer esto?




A continuación la versión ligeramente modificada de la respuesta de @iFreilicht, actualizada a C++14 (uso de la make_uniquefunción en lugar de la declaración sin formato) y soporte adicional para std::stringargumentos (basado en el article Kenny Kerr )

#include <iostream>
#include <memory>
#include <string>
#include <cstdio>

template <typename T>
T process_arg(T value) noexcept
{
    return value;
}

template <typename T>
T const * process_arg(std::basic_string<T> const & value) noexcept
{
    return value.c_str();
}

template<typename ... Args>
std::string string_format(const std::string& format, Args const & ... args)
{
    const auto fmt = format.c_str();
    const size_t size = std::snprintf(nullptr, 0, fmt, process_arg(args) ...) + 1;
    auto buf = std::make_unique<char[]>(size);
    std::snprintf(buf.get(), size, fmt, process_arg(args) ...);
    auto res = std::string(buf.get(), buf.get() + size - 1);
    return res;
}

int main()
{
    int i = 3;
    float f = 5.f;
    char* s0 = "hello";
    std::string s1 = "world";
    std::cout << string_format("i=%d, f=%f, s=%s %s", i, f, s0, s1) << "\n";
}

Salida:

i = 3, f = 5.000000, s = hello world

Siéntase libre de fusionar esta respuesta con la original si lo desea.




Esto puede ser probado. sencillo. Aunque realmente no usa matices de la clase de cadena.

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include <string>
#include <exception>
using namespace std;

//---------------------------------------------------------------------

class StringFormatter
{
public:
    static string format(const char *format, ...);
};

string StringFormatter::format(const char *format, ...)
{
    va_list  argptr;

    va_start(argptr, format);

        char   *ptr;
        size_t  size;
        FILE   *fp_mem = open_memstream(&ptr, &size);
        assert(fp_mem);

        vfprintf (fp_mem, format, argptr);
        fclose (fp_mem);

    va_end(argptr);

    string ret = ptr;
    free(ptr);

    return ret;
}

//---------------------------------------------------------------------

int main(void)
{
    string temp = StringFormatter::format("my age is %d", 100);
    printf("%s\n", temp.c_str());

    return 0;
}



_return.desc = (boost::format("fail to detect. cv_result = %d") % st_result).str();



Utilizando C++11 sprintf , esto se convierte en una tarea bastante fácil y segura. Veo muchas respuestas a esta pregunta que aparentemente fueron escritas antes del tiempo de C ++ 11 que usan longitudes y vargs de búfer fijos, algo que no recomendaría por razones de seguridad, eficiencia y claridad.

#include <memory>
#include <iostream>
#include <string>
#include <cstdio>

using namespace std; //Don't if you're in a header-file

template<typename ... Args>
string string_format( const std::string& format, Args ... args )
{
    size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0'
    unique_ptr<char[]> buf( new char[ size ] ); 
    snprintf( buf.get(), size, format.c_str(), args ... );
    return string( buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
}

El fragmento de código anterior está licenciado bajo CC0 1.0 .

Explicación línea por línea:

Objetivo: escribir en un char* utilizando std::snprintf y luego convertirlo en un std::string .

Primero, determinamos la longitud deseada de la matriz de caracteres.

Desde sprintf :

Valor de retorno

[...] Si la cadena resultante se trunca debido al límite buf_size, la función devuelve el número total de caracteres (sin incluir el byte-nulo de terminación) que se habría escrito, si no se impusiera el límite.

Esto significa que el tamaño deseado es el número de caracteres más uno , de modo que el terminador nulo se ubicará después de todos los demás caracteres y que el constructor de cadenas puede recortarlo. Este problema fue explicado por @ alexk7 en los comentarios.

Luego, asignamos una nueva matriz de caracteres y la asignamos a std::unique_ptr . Esto generalmente se recomienda, ya que no tendrá que delete nuevo manualmente.

Tenga en cuenta que esta no es una forma segura de asignar un unique_ptr con tipos definidos por el usuario ya que no puede desasignar la memoria si el constructor lanza una excepción.

Después de eso, podemos, por supuesto, simplemente usar snprintf para su uso previsto y escribir la cadena formateada al char[] y luego crear y devolver una nueva std::string partir de eso.

Puedes ver un ejemplo en acción here .

Si también desea usar std::string en la lista de argumentos, eche un vistazo a esta idea .

Información adicional para usuarios de Visual Studio :

Como se explica en esta respuesta , Microsoft cambió el nombre de std::snprintf a _snprintf (sí, sin std:: _snprintf . MS lo establece como obsoleto y recomienda utilizar _snprintf_s en _snprintf_s lugar, sin embargo, _snprintf_s no aceptará que el búfer sea cero o más pequeño que la salida formateada y no calculará la longitud de las salidas si eso ocurre. Entonces, para deshacerse de las advertencias de desaprobación durante la compilación, puede insertar la siguiente línea en la parte superior del archivo que contiene el uso de _snprintf :

#pragma warning(disable : 4996)



string no tiene lo que necesitas, pero std :: stringstream tiene. Use una cadena de caracteres para crear la cadena y luego extraiga la cadena. Here hay una lista completa de las cosas que puedes hacer. Por ejemplo:

cout.setprecision(10); //stringstream is a stream like cout

le dará 10 decimales de precisión cuando imprima un doble o flotante.




Para formatear std::string de una manera 'sprintf', llame a snprintf (argumentos nullptr y 0 ) para obtener la longitud del búfer necesario. Escriba su función usando la plantilla variadic C ++ 11 como esta:

#include <cstdio>
#include <string>
#include <cassert>

template< typename... Args >
std::string string_sprintf( const char* format, Args... args ) {
  int length = std::snprintf( nullptr, 0, format, args... );
  assert( length >= 0 );

  char* buf = new char[length + 1];
  std::snprintf( buf, length + 1, format, args... );

  std::string str( buf );
  delete[] buf;
  return std::move(str);
}

Compile con soporte para C ++ 11, por ejemplo en GCC: g++ -std=c++11

Uso:

  std::cout << string_sprintf("%g, %g\n", 1.23, 0.001);



template<typename... Args>
std::string string_format(const char* fmt, Args... args)
{
    size_t size = snprintf(nullptr, 0, fmt, args...);
    std::string buf;
    buf.reserve(size + 1);
    buf.resize(size);
    snprintf(&buf[0], size + 1, fmt, args...);
    return buf;
}

Usando C99 snprintf y C ++ 11




Si solo desea una sintaxis similar a printf (sin llamar a printf usted mismo), eche un vistazo a Boost Format .




Solución de C ++ 11 que usa vsnprintf() internamente:

#include <stdarg.h>  // For va_start, etc.

std::string string_format(const std::string fmt, ...) {
    int size = ((int)fmt.size()) * 2 + 50;   // Use a rubric appropriate for your code
    std::string str;
    va_list ap;
    while (1) {     // Maximum two passes on a POSIX system...
        str.resize(size);
        va_start(ap, fmt);
        int n = vsnprintf((char *)str.data(), size, fmt.c_str(), ap);
        va_end(ap);
        if (n > -1 && n < size) {  // Everything worked
            str.resize(n);
            return str;
        }
        if (n > -1)  // Needed size returned
            size = n + 1;   // For null char
        else
            size *= 2;      // Guess at a larger size (OS specific)
    }
    return str;
}

Un enfoque más seguro y eficiente (lo probé y es más rápido):

#include <stdarg.h>  // For va_start, etc.
#include <memory>    // For std::unique_ptr

std::string string_format(const std::string fmt_str, ...) {
    int final_n, n = ((int)fmt_str.size()) * 2; /* Reserve two times as much as the length of the fmt_str */
    std::unique_ptr<char[]> formatted;
    va_list ap;
    while(1) {
        formatted.reset(new char[n]); /* Wrap the plain char array into the unique_ptr */
        strcpy(&formatted[0], fmt_str.c_str());
        va_start(ap, fmt_str);
        final_n = vsnprintf(&formatted[0], n, fmt_str.c_str(), ap);
        va_end(ap);
        if (final_n < 0 || final_n >= n)
            n += abs(final_n - n + 1);
        else
            break;
    }
    return std::string(formatted.get());
}

El valor de fmt_str se pasa para cumplir con los requisitos de va_start .

NOTA: La versión "más segura" y "más rápida" no funciona en algunos sistemas. Por lo tanto, ambos siguen en la lista. Además, "más rápido" depende completamente de que el paso de preasignación sea correcto; de lo contrario, strcpy hace más lento.




Así es como lo hace Google: StringPrintf (Licencia BSD)
y Facebook lo hace de una manera bastante similar: StringPrintf (Licencia Apache)
Ambos proporcionan un conveniente StringAppendF también.




Muy, muy simple solución.

std::string strBuf;
strBuf.resize(256);
int iCharsPrinted = sprintf_s((char *)strPath.c_str(), strPath.size(), ...);
strBuf.resize(iCharsPrinted);



Usualmente uso esto:

std::string myformat(const char *const fmt, ...)
{
        char *buffer = NULL;
        va_list ap;

        va_start(ap, fmt);
        (void)vasprintf(&buffer, fmt, ap);
        va_end(ap);

        std::string result = buffer;
        free(buffer);

        return result;
}

Desventaja: no todos los sistemas soportan vasprint




Una solución que he preferido es hacer esto con sprintf directamente en el búfer std :: string, después de hacer que dicho búfer sea lo suficientemente grande:

#include <string>
#include <iostream>

using namespace std;

string l_output;
l_output.resize(100);

for (int i = 0; i < 1000; ++i)
{       
    memset (&l_output[0], 0, 100);
    sprintf (&l_output[0], "\r%i\0", i);

    cout << l_output;
    cout.flush();
}

Entonces, cree la cadena std ::, cambie su tamaño, acceda a su búfer directamente ...




Desafortunadamente, la mayoría de las respuestas aquí utilizan varargs que son intrínsecamente inseguras a menos que se use algo como el atributo de format de GCC que solo funciona con cadenas de formato literal. Puede ver por qué estas funciones no son seguras en el siguiente ejemplo:

std::string format_str = "%s";
string_format(format_str, format_str[0]);

donde string_format es una implementación de la respuesta de Erik Aronesty. Este código se compila, pero lo más probable es que se bloquee cuando intentes ejecutarlo:

$ g++ -Wall -Wextra -pedantic test.cc 
$ ./a.out 
Segmentation fault: 11

Es posible implementar una printf segura y extenderla al formato std::string utilizando plantillas (variadic). Esto se ha hecho en la biblioteca fmt , que proporciona una alternativa segura a sprintf std::string :

std::string format_str = "The answer is %d";
std::string result = fmt::sprintf(format_str, 42);

fmt realiza un seguimiento de los tipos de argumentos y, si el tipo no coincide con la especificación de formato, no existe un error de segmentación, solo una excepción.

Descargo de responsabilidad : Soy el autor de esta biblioteca.




Mis dos centavos en esta pregunta muy popular.

Para citar la página de manual de funciones similares a printf :

Al regresar con éxito, estas funciones devuelven el número de caracteres impresos (excluyendo el byte nulo usado para finalizar la salida de las cadenas).

Las funciones snprintf () y vsnprintf () no escriben más que bytes de tamaño (incluido el byte nulo de terminación ('\ 0')). Si la salida se truncó debido a este límite, entonces el valor de retorno es el número de caracteres (excluyendo el byte nulo de terminación) que se habría escrito en la cadena final si hubiera suficiente espacio disponible. Por lo tanto, un valor de retorno de tamaño o más significa que la salida se truncó.

En otras palabras, una implementación sana de C ++ 11 debería ser la siguiente:

#include <string>
#include <cstdio>

template <typename... Ts>
std::string fmt (const std::string &fmt, Ts... vs)
{
    char b;
    size_t required = std::snprintf(&b, 0, fmt.c_str(), vs...) + 1;
        // See comments: the +1 is necessary, while the first parameter
        //               can also be set to nullptr

    char bytes[required];
    std::snprintf(bytes, required, fmt.c_str(), vs...);

    return std::string(bytes);
}

Funciona bastante bien :)

Las plantillas Variadic solo son compatibles con C ++ 11. La respuesta de pixelpoint muestra una técnica similar utilizando estilos de programación más antiguos.

Es extraño que C ++ no tenga tal cosa fuera de la caja. Recientemente agregaron to_string() , que en mi opinión es un gran paso adelante. Me pregunto si agregarán un operador .format a la std::string eventualmente ...

Editar

Como señaló alexk7, se necesita A +1 en el valor de retorno de std::snprintf , ya que necesitamos espacio para el byte \0 . De manera intuitiva, en la mayoría de las arquitecturas que faltan el +1 , el entero required se sobrescribirá parcialmente con un 0 . Esto sucederá después de la evaluación del parámetro required como real para std::snprintf , por lo que el efecto no debería ser visible.

Sin embargo, este problema podría cambiar, por ejemplo, con la optimización del compilador: ¿qué sucede si el compilador decide utilizar un registro para la variable required ? Este es el tipo de errores que a veces resultan en problemas de seguridad.




Basado en la respuesta proporcionada por Erik Aronesty:

std::string string_format(const std::string &fmt, ...) {
    std::vector<char> str(100,'\0');
    va_list ap;
    while (1) {
        va_start(ap, fmt);
        auto n = vsnprintf(str.data(), str.size(), fmt.c_str(), ap);
        va_end(ap);
        if ((n > -1) && (size_t(n) < str.size())) {
            return str.data();
        }
        if (n > -1)
            str.resize( n + 1 );
        else
            str.resize( str.size() * 2);
    }
    return str.data();
}

Esto evita la necesidad de desechar la const del resultado de .c_str() que estaba en la respuesta original.




Escribí mi propio uso de vsnprintf para que devuelva la cadena en lugar de tener que crear mi propio búfer.

#include <string>
#include <cstdarg>

//missing string printf
//this is safe and convenient but not exactly efficient
inline std::string format(const char* fmt, ...){
    int size = 512;
    char* buffer = 0;
    buffer = new char[size];
    va_list vl;
    va_start(vl, fmt);
    int nsize = vsnprintf(buffer, size, fmt, vl);
    if(size<=nsize){ //fail delete buffer and try again
        delete[] buffer;
        buffer = 0;
        buffer = new char[nsize+1]; //+1 for /0
        nsize = vsnprintf(buffer, size, fmt, vl);
    }
    std::string ret(buffer);
    va_end(vl);
    delete[] buffer;
    return ret;
}

Así que puedes usarlo como

std::string mystr = format("%s %d %10.5f", "omg", 1, 10.5);






boost::format() proporciona la funcionalidad que desea:

A partir de la sinopsis de las bibliotecas de formatos de Boost:

Un objeto de formato se construye a partir de una cadena de formato, y luego se le dan argumentos mediante llamadas repetidas al operador%. Cada uno de esos argumentos se convierte en cadenas, que a su vez se combinan en una cadena, de acuerdo con la cadena de formato.

#include <boost/format.hpp>

cout << boost::format("writing %1%,  x=%2% : %3%-th try") % "toto" % 40.23 % 50; 
// prints "writing toto,  x=40.230 : 50-th try"



Puede formatear la salida de C ++ en cout usando el archivo de cabecera iomanip. Asegúrese de incluir el archivo de cabecera iomanip antes de usar cualquiera de las funciones de ayuda, como setprecision, setfill, etc.

Aquí hay un fragmento de código que he usado en el pasado para imprimir el tiempo de espera promedio en el vector, que he "acumulado".

#include<iomanip>
#include<iostream>
#include<vector>
#include<numeric>

...

cout<< "Average waiting times for tasks is " << setprecision(4) << accumulate(all(waitingTimes), 0)/double(waitingTimes.size()) ;
cout << " and " << Q.size() << " tasks remaining" << endl;

Aquí hay una breve descripción de cómo podemos formatear los flujos de C ++. http://www.cprogramming.com/tutorial/iomanip.html




Lo intenté, con expresiones regulares . Lo implementé para las cadenas de caracteres ints y const, como ejemplo, pero puede agregar cualquier otro tipo ( tipos POD pero con punteros puede imprimir cualquier cosa).

#include <assert.h>
#include <cstdarg>

#include <string>
#include <sstream>
#include <regex>

static std::string
formatArg(std::string argDescr, va_list args) {
    std::stringstream ss;
    if (argDescr == "i") {
        int val = va_arg(args, int);
        ss << val;
        return ss.str();
    }
    if (argDescr == "s") {
        const char *val = va_arg(args, const char*);
        ss << val;
        return ss.str();
    }
    assert(0); //Not implemented
}

std::string format(std::string fmt, ...) {
    std::string result(fmt);
    va_list args;
    va_start(args, fmt);
    std::regex e("\\{([^\\{\\}]+)\\}");
    std::smatch m;
    while (std::regex_search(fmt, m, e)) {
        std::string formattedArg = formatArg(m[1].str(), args);
        fmt.replace(m.position(), m.length(), formattedArg);
    }
    va_end(args);
    return fmt;
}

Aquí hay un ejemplo de uso de él:

std::string formatted = format("I am {s} and I have {i} cats", "bob", 3);
std::cout << formatted << std::endl;

Salida:

Soy bob y tengo 3 gatos




Probado, respuesta de calidad de producción

Esta respuesta maneja el caso general con técnicas que cumplen con los estándares. El mismo enfoque se ofrece como ejemplo en CppReference.com cerca de la parte inferior de la página.

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

// requires at least C++11
const std::string vformat(const char * const zcFormat, ...) {

    // initialize use of the variable argument array
    va_list vaArgs;
    va_start(vaArgs, zcFormat);

    // reliably acquire the size
    // from a copy of the variable argument array
    // and a functionally reliable call to mock the formatting
    va_list vaArgsCopy;
    va_copy(vaArgsCopy, vaArgs);
    const int iLen = std::vsnprintf(NULL, 0, zcFormat, vaArgsCopy);
    va_end(vaArgsCopy);

    // return a formatted string without risking memory mismanagement
    // and without assuming any compiler or platform specific behavior
    std::vector<char> zc(iLen + 1);
    std::vsnprintf(zc.data(), zc.size(), zcFormat, vaArgs);
    va_end(vaArgs);
    return std::string(zc.data(), iLen); }

#include <ctime>
#include <iostream>
#include <iomanip>

// demonstration of use
int main() {

    std::time_t t = std::time(nullptr);
    std::cerr
        << std::put_time(std::localtime(& t), "%D %T")
        << " [debug]: "
        << vformat("Int 1 is %d, Int 2 is %d, Int 3 is %d", 11, 22, 33)
        << std::endl;
    return 0; }



Puede haber problemas, si el búfer no es lo suficientemente grande como para imprimir la cadena. Debe determinar la longitud de la cadena formateada antes de imprimir un mensaje formateado allí. Hago ayuda propia para esto (probado en Windows y Linux GCC ), y puedes intentar usarlo.

String.cpp: http://pastebin.com/DnfvzyKP
String.h: http://pastebin.com/7U6iCUMa

String.cpp:

#include <cstdio>
#include <cstdarg>
#include <cstring>
#include <string>

using ::std::string;

#pragma warning(disable : 4996)

#ifndef va_copy
#ifdef _MSC_VER
#define va_copy(dst, src) dst=src
#elif !(__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__))
#define va_copy(dst, src) memcpy((void*)dst, (void*)src, sizeof(*src))
#endif
#endif

///
/// \breif Format message
/// \param dst String to store formatted message
/// \param format Format of message
/// \param ap Variable argument list
///
void toString(string &dst, const char *format, va_list ap) throw() {
  int length;
  va_list apStrLen;
  va_copy(apStrLen, ap);
  length = vsnprintf(NULL, 0, format, apStrLen);
  va_end(apStrLen);
  if (length > 0) {
    dst.resize(length);
    vsnprintf((char *)dst.data(), dst.size() + 1, format, ap);
  } else {
    dst = "Format error! format: ";
    dst.append(format);
  }
}

///
/// \breif Format message
/// \param dst String to store formatted message
/// \param format Format of message
/// \param ... Variable argument list
///
void toString(string &dst, const char *format, ...) throw() {
  va_list ap;
  va_start(ap, format);
  toString(dst, format, ap);
  va_end(ap);
}

///
/// \breif Format message
/// \param format Format of message
/// \param ... Variable argument list
///
string toString(const char *format, ...) throw() {
  string dst;
  va_list ap;
  va_start(ap, format);
  toString(dst, format, ap);
  va_end(ap);
  return dst;
}

///
/// \breif Format message
/// \param format Format of message
/// \param ap Variable argument list
///
string toString(const char *format, va_list ap) throw() {
  string dst;
  toString(dst, format, ap);
  return dst;
}


int main() {
  int a = 32;
  const char * str = "This works!";

  string test(toString("\nSome testing: a = %d, %s\n", a, str));
  printf(test.c_str());

  a = 0x7fffffff;
  test = toString("\nMore testing: a = %d, %s\n", a, "This works too..");
  printf(test.c_str());

  a = 0x80000000;
  toString(test, "\nMore testing: a = %d, %s\n", a, "This way is cheaper");
  printf(test.c_str());

  return 0;
}

String.h:

#pragma once
#include <cstdarg>
#include <string>

using ::std::string;

///
/// \breif Format message
/// \param dst String to store formatted message
/// \param format Format of message
/// \param ap Variable argument list
///
void toString(string &dst, const char *format, va_list ap) throw();
///
/// \breif Format message
/// \param dst String to store formatted message
/// \param format Format of message
/// \param ... Variable argument list
///
void toString(string &dst, const char *format, ...) throw();
///
/// \breif Format message
/// \param format Format of message
/// \param ... Variable argument list
///
string toString(const char *format, ...) throw();

///
/// \breif Format message
/// \param format Format of message
/// \param ap Variable argument list
///
string toString(const char *format, va_list ap) throw();



Esta respuesta de desbordamiento de pila fue la única que atrapó el espacio y los caracteres del tablero:

# For null cmd arguments checking   
to_check=' -t'
space_n_dash_chars=' -'
[[ $to_check == *"$space_n_dash_chars"* ]] && echo found






c++ string stl formatting