3

Is it possible to tweak the common C++ stringification method :

template<typename T> 
std::string Stringify(T const &value) 
{
    std::stringstream ss;
    ss << value;
    return ss.str();
}

so that the actual value will be printed and not a truncation or scientific notation representation of the value, eg :

std::cout << Stringify(std::numeric_limits<float>::max()) << std::endl;

should not print 3.40282e+38 but 3'402'82... (I'm just not mentioning the rest of the digits, I'm not implying that dots should be printed)

Nikos Athanasiou
  • 29,616
  • 15
  • 87
  • 153
  • Are you looking for `setprecision`? @Sneftel That doesn't handle, e.g., 0.0000001. – T.C. Sep 16 '15 at 10:08
  • @T.C. My initial thought was to use it, but then the problem is `setprecision` to what ? I mean this needs to be dynamic and self adjustable – Nikos Athanasiou Sep 16 '15 at 10:13

2 Answers2

4

Yes, add the manipulator(s) you desire to the function signature and forward them to the stream.

template<typename T, typename Manip> 
std::string Stringify(T const &value, Manip manip) 
{
    std::stringstream ss;
    ss << manip << value;
    return ss.str();
}

With the sample code;

int main()
{
    using namespace std;
    // The precision here is set to be sufficient to print the test platform
    cout << Stringify(numeric_limits<float>::max(), setprecision(50)) << endl;
}    

I assume that more than one manipulator will be used. To this end, function overloads can be added for the desired number of manipulators, or you can use (with C++11) variadic templates and perfect forwarding.

template <typename Stream>
Stream& AddManip(Stream& str)
{
    // end the recursion
    return str;
}

template <typename Stream, typename Head, typename... Tails>
Stream& AddManip(Stream& str, Head&& head, Tails&&... tails)
{
    // add the head manipulator and forward the rest
    str << std::forward<Head>(head);
    return AddManip(str, std::forward<Tails>(tails)...);
}

template<typename T, typename... Manip> 
std::string Stringify(T const &value, Manip&&... manip) 
{
    std::stringstream ss;
    // add the manipulators to the stream
    AddManip(ss, std::forward<Manip>(manip)...);
    ss << value;
    return ss.str();
}

int main()
{
    using namespace std;
    cout << Stringify(numeric_limits<int>::max(), setprecision(40), std::hex) << endl;
}    
Niall
  • 30,036
  • 10
  • 99
  • 142
1

I know macros are evil, but I've made one or two exceptions in my works. One of them is this macro:

#include <sstream>

#define SSTR( x ) static_cast< std::ostringstream & >( \
        ( std::ostringstream() << std::dec << x ) ).str()

Detailed explanation available.

One upside of this construct is that manipulators can easily be inlined:

 std::string str = SSTR( "numeric_limits<float>::max is: "
                       << setprecision(50)
                       << numeric_limits<float>::max()
                       );

The other upside is that it's compact, and quite C++98.

The downside is that it's a macro. ;-)

DevSolar
  • 67,862
  • 21
  • 134
  • 209
  • Interestingly it's [consistenty faster](http://coliru.stacked-crooked.com/a/07ba2fc476ff23d5) than the popular method I mention in the question. The c++11 alternatives are [faster though](http://stackoverflow.com/q/23437778/2567683) . The output remains the same and I still can't auto tweak the precision this way though. I also +1 the linked answer cause I like the blog – Nikos Athanasiou Sep 16 '15 at 20:56
  • @NikosAthanasiou: I had the suspicion that the compiler could do some optimizing around the temporary `ostringstream` object, but never bothered to actually benchmark it. (My main use for `SSTR()` is cobbling together exception messages, at which point performance is secondary anyway.) As for the auto-precision, well, you *could* add the `setprecision` to the macro... but anyway, thanks for the feedback! – DevSolar Sep 17 '15 at 06:41
  • 1
    @NikosAthanasiou: I took the liberty of adding your benchmark to my homepage. – DevSolar Sep 17 '15 at 06:53