background
For the package from C language to C + +, the format control of output stream is a headache. Is the string format control of printf not good? Indeed, many C + + open-source libraries have implemented the format control of printf. Function signature is similar to format (const STD:: String &,...), and even other languages have borrowed the format control method of printf.
The input and output of C + + are stream based, similar to STD:: cout < "flushhip height: [" < 3 < "]". The format control of C + + is for input and output streams, and the format control of printf is for strings. The flow format control is controlled by the Manipulator, which is more powerful than printf. for instance:
output bool value
printf
:printf("%s", f ? "true" : "false")
;- Controller:
std::cout << std::boolalpha << f
;
If you are familiar with the controller, you will definitely like the controller to control the IO format.
What is the actual controller in C + +
First of all, the manipulator is actually a function, which changes the interpretation of input and output by setting the state of the flow.
Source code analysis
The frequently used newline std::endl is a manipulator. Similarly, std::ends, std::flush. The source code is as follows:
// MANIPULATORS template<class _Elem, class _Traits> inline basic_ostream<_Elem, _Traits>& __CLRCALL_OR_CDECL endl(basic_ostream<_Elem, _Traits>& _Ostr) { // insert newline and flush stream _Ostr.put(_Ostr.widen('\n')); _Ostr.flush(); return (_Ostr); } template<class _Elem, class _Traits> inline basic_ostream<_Elem, _Traits>& __CLRCALL_OR_CDECL ends(basic_ostream<_Elem, _Traits>& _Ostr) { // insert null character _Ostr.put(_Elem()); return (_Ostr); } template<class _Elem, class _Traits> inline basic_ostream<_Elem, _Traits>& __CLRCALL_OR_CDECL flush(basic_ostream<_Elem, _Traits>& _Ostr) { // flush stream _Ostr.flush(); return (_Ostr); }
Take a look at the source code of std::boolalpha and some other manipulators. Through setf, unsetf to change the flag variable, so as to achieve the purpose of controlling the format.
// MANIPULATORS inline ios_base& __CLRCALL_OR_CDECL boolalpha(ios_base& _Iosbase) { // set boolalpha _Iosbase.setf(ios_base::boolalpha); return (_Iosbase); } inline ios_base& __CLRCALL_OR_CDECL noboolalpha(ios_base& _Iosbase) { // clear boolalpha _Iosbase.unsetf(ios_base::boolalpha); return (_Iosbase); } inline ios_base& __CLRCALL_OR_CDECL dec(ios_base& _Iosbase) { // set basefield to dec _Iosbase.setf(ios_base::dec, ios_base::basefield); return (_Iosbase); } inline ios_base& __CLRCALL_OR_CDECL defaultfloat(ios_base& _Iosbase) { // clear floatfield _Iosbase.unsetf(ios_base::floatfield); return (_Iosbase); } inline ios_base& __CLRCALL_OR_CDECL fixed(ios_base& _Iosbase) { // set floatfield to fixed _Iosbase.setf(ios_base::fixed, ios_base::floatfield); return (_Iosbase); } inline ios_base& __CLRCALL_OR_CDECL hex(ios_base& _Iosbase) { // set basefield to hex _Iosbase.setf(ios_base::hex, ios_base::basefield); return (_Iosbase); } inline ios_base& __CLRCALL_OR_CDECL hexfloat(ios_base& _Iosbase) { // set floatfield to hexfloat _Iosbase.setf(ios_base::hexfloat, ios_base::floatfield); return (_Iosbase); }
STD:: Boolean PHA (STD:: cout) can be used in this way. For the convenience of the standard library, the manipulator overloads the flow operator < < with functions signed by different functions. Therefore, STD:: cout < < STD:: Boolean PHA can be used in this way.
typedef basic_istream<_Elem, _Traits> _Myt; typedef basic_ios<_Elem, _Traits> _Myios; _Myt& __CLR_OR_THIS_CALL operator<<(_Myt& (__cdecl *_Pfn)(_Myt&)) { // call basic_ostream manipulator _DEBUG_POINTER(_Pfn); return ((*_Pfn)(*this)); } _Myt& __CLR_OR_THIS_CALL operator<<(_Myios& (__cdecl *_Pfn)(_Myios&)) { // call basic_ios manipulator _DEBUG_POINTER(_Pfn); (*_Pfn)(*(_Myios *)this); return (*this); } _Myt& __CLR_OR_THIS_CALL operator<<(ios_base& (__cdecl *_Pfn)(ios_base&)) { // call ios_base manipulator _DEBUG_POINTER(_Pfn); (*_Pfn)(*(ios_base *)this); return (*this); }
There are no parameters for these manipulators. If there are parameters, it will be more complicated. Let's see the implementation of std::setw. The manipulators with parameters are all implemented in the header file < iomanip >.
_MRTIMP2 _Smanip<streamsize> __cdecl setw(streamsize); template<class _Elem, class _Traits, class _Arg> inline basic_ostream<_Elem, _Traits>& operator<<( basic_ostream<_Elem, _Traits>& _Ostr, const _Smanip<_Arg>& _Manip) { // insert by calling function with output stream and argument (*_Manip._Pfun)(_Ostr, _Manip._Manarg); return (_Ostr); } // TEMPLATE STRUCT _Smanip template<class _Arg> struct _Smanip { // store function pointer and argument value _Smanip(void (__cdecl *_Left)(ios_base&, _Arg), _Arg _Val) : _Pfun(_Left), _Manarg(_Val) { // construct from function pointer and argument value } void (__cdecl *_Pfun)(ios_base&, _Arg); // the function pointer _Arg _Manarg; // the argument value };
Custom manipulator
After understanding the above source code, it is much easier to write a controller by yourself.
- For the manipulator without parameters, it only needs to implement according to the above signature, and then reload < can be done;
- For manipulators with parameters, you need to return the function itself and parameters as return values, and overload < is enough.
However, we usually don't need to overload the controllers. The controllers provided in the standard library are enough for us to handle most of the format operations.
Manipulators in standard library
overview
Here is an example of the controller. For each controller's precautions and usage, we will not expand it here. Can refer to https://en.cppreference.com/w/cpp/io/manip.
Simple example
Print multiplication table
for (int i = 1; i < 10; ++i) for (int j = 1; j < i + 1; ++j) std::cout << j << " * " << i << " = " << std::setw(2) << std::right << i * j << " \n"[i == j];
Other
There are so many other things that I can't think of. In a word, I can easily use these things by reading more documents and examples.
Reference resources
- https://en.cppreference.com/w/cpp/io/manip
- C + + standard library (version 2)