Skip to content

Instantly share code, notes, and snippets.

@ethouris
Created November 17, 2023 08:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ethouris/2b431e1086c2197f516e609b1b4bf023 to your computer and use it in GitHub Desktop.
Save ethouris/2b431e1086c2197f516e609b1b4bf023 to your computer and use it in GitHub Desktop.
C++ formatting helper
#include <iostream>
#include <iomanip>
#include <tuple>
template<class TYPE, typename... Args>
struct fmt_sender_proxy
{
const TYPE& valref;
typedef std::tuple<const Args&...> TupleType;
TupleType mani;
static const size_t manip_size = std::tuple_size<TupleType>::value;
template<class OUTSTR, size_t IX>
struct fmt_sender_helper
{
static void manipulate(OUTSTR& stream, const TupleType& mani)
{
stream << std::get<IX>(mani);
fmt_sender_helper<OUTSTR, IX+1>::manipulate(stream, mani);
}
};
template<class OUTSTR>
struct fmt_sender_helper<OUTSTR, manip_size>
{
static void manipulate(OUTSTR& , const TupleType& ) {}
};
template <class OUTSTR>
void sendto(OUTSTR& stream) const
{
std::ios old(nullptr);
old.copyfmt(stream);
fmt_sender_helper<OUTSTR, 0>::manipulate(stream, this->mani);
stream << valref;
stream.copyfmt(old);
}
};
template<class TYPE, typename... Args>
inline fmt_sender_proxy<TYPE, Args...> fmt(const TYPE& val, const Args&... manips)
{
return fmt_sender_proxy<TYPE, Args...> {
val,
std::tuple<const Args&...> { manips... }
};
}
template<class TR, typename... Args>
inline TR& operator<<(TR& ot, const fmt_sender_proxy<Args...>& formatter)
{
formatter.sendto(ot);
return ot;
}
int main( int argc, char** argv )
{
const double pi10 = 31.415926536;
const double twelve = 12;
using namespace std;
cout << "default: " << pi10
<< " fixed: " << fmt(pi10, fixed)
<< " sci/prec12: " << fmt(pi10, scientific, setprecision(12))
<< " default: " << pi10
<< endl;
cout << "default: " << twelve
<< " width4: " << fmt(twelve, setw(4), setfill('0'))
<< " default: " << twelve
<< endl;
return 0;
}
@ethouris
Copy link
Author

ethouris commented Nov 17, 2023

This is a helper for iostream formatting, which allows you to format values always reliably without leaving the stream in an undefined formatting state, as long as you never use stream manipulators directly. Requires C++11.

This works also in iostream wrapping functions, like this one, for example:

template <class Stream>
inline Stream& Print(Stream& in) { return in;}

template <class Stream, class Arg1, class... Args>
inline Stream& Print(Stream& sout, Arg1&& arg1, Args&&... args)
{
    sout << arg1;
    return Print(sout, args...);
}

template <class... Args>
inline std::string Sprint(Args&&... args)
{
    std::ostringstream sout;
    Print(sout, args...);
    return sout.str();
}

For a value=17 simply use cout << value to have it formatted with default flags as "17" and cout << fmt(value, hex, showbase) to have "0x11". Same for the above Sprint("flags hex=", fmt(value, hex, showbase), " dec=", value).

Additionally, fmt may be forcefully set the type of the value, if needed in particular case, for example:

char val = 32;
cout << fmt<int>(val, hex);

without the explicitly specified type, val would be printed as a character.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment