In my previous post I compared the speed of different methods of converting integer to string. The problem was that these solutions both relied on the environment somehow and cannot be used on an adhoc manner like messages[i] = std::string("Error found at line #") + IntToString(lineNumbers[i]) + ".";
. These both need some circumstancial preparation like creating a buffer (char buffer[...]
) or a std::stringstream
outside the loop and resetting it after usage. So I started on a journey to create a kitchen-ready preparation-less yet not too slow implementation for my imaginary IntToString()
function.
#include <iostream> #include <limits> #include <sstream> #define __STDC_FORMAT_MACROS #include <inttypes.h> // Utility functions and structs template<typename Unsigned> char * DigitsToString(Unsigned i, char * endPtr) { char *&cursor(endPtr); *cursor = 0; if(!i) { *--cursor = '0'; return cursor; } for(;i;i/=10) { *--cursor = char(i%10+'0'); } return cursor; } template<typename Unsigned> std::string UnsignedToString(Unsigned i) { const size_t bufsize = std::numeric_limits<Unsigned>::digits10 + 1; char buffer[bufsize]; return DigitsToString(i,buffer+bufsize-1); } template<typename Signed> std::string SignedToString(Signed i) { if(i>=0) { return UnsignedToString(i); } const size_t bufsize = std::numeric_limits<Signed>::digits10 + 2; char buffer[bufsize]; char *cursor = DigitsToString(-i,buffer+bufsize-1); *--cursor = '-'; return cursor; } template<bool is_signed,typename Integral> struct ToStringConverter { }; template<typename Integral> struct ToStringConverter<false,Integral> { static std::string Do(Integral i){ return UnsignedToString(i); } }; template<typename Integral> struct ToStringConverter<true,Integral> { static std::string Do(Integral i){ return SignedToString(i); } }; // The IntToString() template<typename Integral> std::string IntToString(Integral i) { return ToStringConverter<std::numeric_limits<Integral>::is_signed,Integral>::Do(i); } // Testing my IntToString() template<typename Integral, Integral count> void testFun() { for(Integral i=0; i<count; ++i) { IntToString(i); } } // An std::stringstream based integer to string conversion template<typename T> std::string StreamedToString(const T& i) { std::stringstream ss; ss << i; return ss.str(); } // Testing of the std::stringstream based integer to string conversion template<typename Integral, Integral count> void testStream() { for(Integral i=0; i<count; ++i) { StreamedToString(i); } } #ifndef TEST_CASE #define TEST_CASE 1 #endif int main() { #if TEST_CASE == 1 testFun<uint64_t,100*1000*1000>(); #endif #if TEST_CASE == 2 testStream<uint64_t,100*1000*1000>(); #endif return 0; }
This is far from being a market-ready product, but it might be useful. Speed test:
$ g++ -W -Wall -Wextra -pedantic -O2 int_to_str_speed2.cpp -DTEST_CASE=1 -o int_to_str_speed2 $ time ./int_to_str_speed2 real 0m12.404s user 0m12.397s sys 0m0.008s $ g++ -W -Wall -Wextra -pedantic -O2 int_to_str_speed2.cpp -DTEST_CASE=2 -o int_to_str_speed2 $ time ./int_to_str_speed2 real 1m36.204s user 1m36.174s sys 0m0.008s