Analysis of IO format controlled by controller in C + +

Keywords: C

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

297 original articles published, 346 praised, 450000 visitors+
His message board follow

Posted by Markto on Tue, 25 Feb 2020 21:29:25 -0800