std::string formatting like sprintf

前端 未结 30 2621
野趣味
野趣味 2020-11-22 04:42

I have to format std::string with sprintf and send it into file stream. How can I do this?

30条回答
  •  太阳男子
    2020-11-22 05:19

    UPDATE 1: added fmt::format tests

    I've took my own investigation around methods has introduced here and gain diametrically opposite results versus mentioned here.

    I have used 4 functions over 4 methods:

    • variadic function + vsnprintf + std::unique_ptr
    • variadic function + vsnprintf + std::string
    • variadic template function + std::ostringstream + std::tuple + utility::for_each
    • fmt::format function from fmt library

    For the test backend the googletest has used.

    #include 
    #include 
    #include 
    #include 
    #include 
    
    #include 
    
    inline std::string string_format(size_t string_reserve, const std::string fmt_str, ...)
    {
        size_t str_len = (std::max)(fmt_str.size(), string_reserve);
    
        // plain buffer is a bit faster here than std::string::reserve
        std::unique_ptr formatted;
    
        va_list ap;
        va_start(ap, fmt_str);
    
        while (true) {
            formatted.reset(new char[str_len]);
    
            const int final_n = vsnprintf(&formatted[0], str_len, fmt_str.c_str(), ap);
    
            if (final_n < 0 || final_n >= int(str_len))
                str_len += (std::abs)(final_n - int(str_len) + 1);
            else
                break;
        }
    
        va_end(ap);
    
        return std::string(formatted.get());
    }
    
    inline std::string string_format2(size_t string_reserve, const std::string fmt_str, ...)
    {
        size_t str_len = (std::max)(fmt_str.size(), string_reserve);
        std::string str;
    
        va_list ap;
        va_start(ap, fmt_str);
    
        while (true) {
            str.resize(str_len);
    
            const int final_n = vsnprintf(const_cast(str.data()), str_len, fmt_str.c_str(), ap);
    
            if (final_n < 0 || final_n >= int(str_len))
                str_len += (std::abs)(final_n - int(str_len) + 1);
            else {
                str.resize(final_n); // do not forget to shrink the size!
                break;
            }
        }
    
        va_end(ap);
    
        return str;
    }
    
    template 
    inline std::string string_format3(size_t string_reserve, Args... args)
    {
        std::ostringstream ss;
        if (string_reserve) {
            ss.rdbuf()->str().reserve(string_reserve);
        }
        std::tuple t{ args... };
        utility::for_each(t, [&ss](auto & v)
        {
            ss << v;
        });
        return ss.str();
    }
    

    The for_each implementation is taken from here: iterate over tuple

    #include 
    #include 
    
    namespace utility {
    
        template 
        inline typename std::enable_if::type
            for_each(std::tuple &, const FuncT &)
        {
        }
    
        template
        inline typename std::enable_if::type
            for_each(std::tuple & t, const FuncT & f)
        {
            f(std::get(t));
            for_each(t, f);
        }
    
    }
    

    The tests:

    TEST(ExternalFuncs, test_string_format_on_unique_ptr_0)
    {
        for (size_t i = 0; i < 1000000; i++) {
            const std::string v = string_format(0, "%s+%u\n", "test test test", 12345);
            UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
        }
    }
    
    TEST(ExternalFuncs, test_string_format_on_unique_ptr_256)
    {
        for (size_t i = 0; i < 1000000; i++) {
            const std::string v = string_format(256, "%s+%u\n", "test test test", 12345);
            UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
        }
    }
    
    TEST(ExternalFuncs, test_string_format_on_std_string_0)
    {
        for (size_t i = 0; i < 1000000; i++) {
            const std::string v = string_format2(0, "%s+%u\n", "test test test", 12345);
            UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
        }
    }
    
    TEST(ExternalFuncs, test_string_format_on_std_string_256)
    {
        for (size_t i = 0; i < 1000000; i++) {
            const std::string v = string_format2(256, "%s+%u\n", "test test test", 12345);
            UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
        }
    }
    
    TEST(ExternalFuncs, test_string_format_on_string_stream_on_variadic_tuple_0)
    {
        for (size_t i = 0; i < 1000000; i++) {
            const std::string v = string_format3(0, "test test test", "+", 12345, "\n");
            UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
        }
    }
    
    TEST(ExternalFuncs, test_string_format_on_string_stream_on_variadic_tuple_256)
    {
        for (size_t i = 0; i < 1000000; i++) {
            const std::string v = string_format3(256, "test test test", "+", 12345, "\n");
            UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
        }
    }
    
    TEST(ExternalFuncs, test_string_format_on_string_stream_inline_0)
    {
        for (size_t i = 0; i < 1000000; i++) {
            std::ostringstream ss;
            ss << "test test test" << "+" << 12345 << "\n";
            const std::string v = ss.str();
            UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
        }
    }
    
    TEST(ExternalFuncs, test_string_format_on_string_stream_inline_256)
    {
        for (size_t i = 0; i < 1000000; i++) {
            std::ostringstream ss;
            ss.rdbuf()->str().reserve(256);
            ss << "test test test" << "+" << 12345 << "\n";
            const std::string v = ss.str();
            UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
        }
    }
    
    TEST(ExternalFuncs, test_fmt_format_positional)
    {
        for (size_t i = 0; i < 1000000; i++) {
            const std::string v = fmt::format("{0:s}+{1:d}\n", "test test test", 12345);
            UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
        }
    }
    
    TEST(ExternalFuncs, test_fmt_format_named)
    {
        for (size_t i = 0; i < 1000000; i++) {
            const std::string v = fmt::format("{first:s}+{second:d}\n", fmt::arg("first", "test test test"), fmt::arg("second", 12345));
            UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
        }
    }
    

    The UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR.

    unsued.hpp:

    #define UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(var)   ::utility::unused_param(&var)
    
    namespace utility {
    
        extern const volatile void * volatile g_unused_param_storage_ptr;
    
        extern void
    #ifdef __GNUC__
        __attribute__((optimize("O0")))
    #endif
            unused_param(const volatile void * p);
    
    }
    

    unused.cpp:

    namespace utility {
    
        const volatile void * volatile g_unused_param_storage_ptr = nullptr;
    
        void
    #ifdef __GNUC__
        __attribute__((optimize("O0")))
    #endif
            unused_param(const volatile void * p)
        {
            g_unused_param_storage_ptr = p;
        }
    
    }
    

    RESULTS:

    [ RUN      ] ExternalFuncs.test_string_format_on_unique_ptr_0
    [       OK ] ExternalFuncs.test_string_format_on_unique_ptr_0 (556 ms)
    [ RUN      ] ExternalFuncs.test_string_format_on_unique_ptr_256
    [       OK ] ExternalFuncs.test_string_format_on_unique_ptr_256 (331 ms)
    [ RUN      ] ExternalFuncs.test_string_format_on_std_string_0
    [       OK ] ExternalFuncs.test_string_format_on_std_string_0 (457 ms)
    [ RUN      ] ExternalFuncs.test_string_format_on_std_string_256
    [       OK ] ExternalFuncs.test_string_format_on_std_string_256 (279 ms)
    [ RUN      ] ExternalFuncs.test_string_format_on_string_stream_on_variadic_tuple_0
    [       OK ] ExternalFuncs.test_string_format_on_string_stream_on_variadic_tuple_0 (1214 ms)
    [ RUN      ] ExternalFuncs.test_string_format_on_string_stream_on_variadic_tuple_256
    [       OK ] ExternalFuncs.test_string_format_on_string_stream_on_variadic_tuple_256 (1325 ms)
    [ RUN      ] ExternalFuncs.test_string_format_on_string_stream_inline_0
    [       OK ] ExternalFuncs.test_string_format_on_string_stream_inline_0 (1208 ms)
    [ RUN      ] ExternalFuncs.test_string_format_on_string_stream_inline_256
    [       OK ] ExternalFuncs.test_string_format_on_string_stream_inline_256 (1302 ms)
    [ RUN      ] ExternalFuncs.test_fmt_format_positional
    [       OK ] ExternalFuncs.test_fmt_format_positional (288 ms)
    [ RUN      ] ExternalFuncs.test_fmt_format_named
    [       OK ] ExternalFuncs.test_fmt_format_named (392 ms)
    

    As you can see implementation through the vsnprintf+std::string is equal to fmt::format, but faster than through the vsnprintf+std::unique_ptr, which is faster than through the std::ostringstream.

    The tests compiled in Visual Studio 2015 Update 3 and run at Windows 7 x64 / Intel Core i7-4820K CPU @ 3.70GHz / 16GB.

提交回复
热议问题