I understand that C++ stream functions are built on top of C's stdio
library.
What do I have to do in C to get the same result as cin.ignore(n)
?
For example, should I use stdio
function fseek(stdin, n, 0)
or is there some other method that cin.ignore
is using?
No, there is not. But let's look what happens behind the curtain called cin.ignore()
. Let's take llvm libcxx sources, I find them faster to look through then gcc's.
The extern istream cin; is in iostream, but it is initialized on application startup in iostream.cpp using statically allocated buffer and __stdoutbuf object constructed from the good' old' FILE *
stdin
:
_ALIGNAS_TYPE (istream) char cin [sizeof(istream)];
ios_base::Init::Init() {
istream* cin_ptr = ::new(cin) istream(::new(__cin) __stdinbuf <char>(stdin) );
...
The istream::ignore()
function can be found in istraem. It's pretty simple, first we check if the user wants to clean all chars from the stream or just some of them (if (__n == numeric_limits<streamsize>::max())
). Then the function calls this->rdbuf()->sbumpc()
in a loop predefined amount of counts (or endless, in case __n
is equal to numeric_limits<steramsize::max()
). We can find sbumpc()
to be a member of std::basic_streambuf
, from cppreference:
int_type sbumpc();
Reads one character and advances the input sequence by one character.
If the input sequence read position is not available, returns uflow(). Otherwise returns Traits::to_int_type(*gptr()).
So we can simply deduce that this->rdbuf()
returns handle to __stdinbuf<char>(stdin)
. In the cin::ignore
function the call to __stdinbuf<char>(stdin)::sbumpc()
is made that many times, as many characters we want to ignore. So let's go to sbumpc()
! First let's take a look at streambuf:
int_type sbumpc() {
if (__ninp_ == __einp_)
return uflow();
return traits_type::to_int_type(*__ninp_++);
}
So if (__ninp_ == __einp_)
is doing some internal buffering in streambuf
object, not to call uflow()
if there are already buffered characters in our buffer. __ninp__
pointer get's incremented after each read, that must be it. uflow()
is overloaded by __stdinbuf : public basic_streambuf< .... >
, from __std_stream:
template <class _CharT>
typename __stdinbuf<_CharT>::int_type
__stdinbuf<_CharT>::uflow()
{
return __getchar(true);
}
Puff, let's go to __getchar
and find out what the true
parameter is. It's right below in __std_stream.
It's a long function, with the main functionality, which takes care of some buffering. But we can spot the hearth of this function right away:
template <class _CharT>
typename __stdinbuf<_CharT>::int_type
__stdinbuf<_CharT>::__getchar(bool __consume) {
....
int __c = getc(__file_);
if (__c == EOF)
return traits_type::eof();
...
}
Let's go from the beginning:
cin
is an istraem
object and is initialized from __stdinbuf<char>(stdin)
istream::ignore()
calls basic_streambuf::sbumpc()
predefined number of times, probably on an object initalized using stdin
basic_streambuf::sbumpc()
takes care of some bufering and calls basic_streambuf::uflow()
if the buffer is empty.basic_streambuf::uflow()
is overloaded by __stdinbuf::uflos()
and calls __stdinbuf::__getchar()
__sinbuf::__getchar()
calls getc(__file__)
so probably getc(stdin)
to read one character from the streamTo sumarize:
void stdin_ignore(size_t n, int delim)
{
while (n--) {
const int c = getc(stdin);
if (c == EOF)
break;
if (delim != EOF && delim == c) {
break;
}
}