| |||
| Home > The C and C++ Libraries > Tailoring the input/output functions > Dependencies on low-level functions |
Table 2.11 shows
the dependencies of the higher-level functions on lower-level functions.
If you define your own versions of the lower-level functions, you can
use the library versions of the higher-level functions directly. fgetc() uses __FILE, but fputc() uses __FILE and ferror().
You must provide definitions of __stdin and __stdout if
you use any of their associated high-level functions. This applies
even if your re-implementations of other functions such as fgetc() and fputc() do
not reference any data stored in those objects.
Table key:
Table 2.11. Input/output dependencies
| High-level function | Low-level object | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
| fgets | x | - | - | - | x | x | - | - | - | - |
| fgetws | x | - | - | - | - | - | x | - | - | - |
| fprintf | x | - | - | x | x | - | - | - | - | - |
| fputs | x | - | - | x | - | - | - | - | - | - |
| fputws | x | - | - | - | - | - | - | x | - | - |
| fread | x | - | - | - | - | x | - | - | - | - |
| fscanf | x | - | - | - | - | x | - | - | x | - |
| fwprintf | x | - | - | - | x | - | - | x | - | - |
| fwrite | x | - | - | x | - | - | - | - | - | - |
| fwscanf | x | - | - | - | - | - | x | - | - | x |
| getchar | x | x | - | - | - | x | - | - | - | - |
| gets | x | x | - | - | x | x | - | - | - | - |
| getwchar | x | x | - | - | - | - | x | - | - | - |
| perror | x | - | x | x | - | - | - | - | - | - |
| printf | x | - | x | x | x | - | - | - | - | - |
| putchar | x | - | x | x | - | - | - | - | - | - |
| puts | x | - | x | x | - | - | - | - | - | - |
| putwchar | x | - | x | - | - | - | - | x | - | - |
| scanf | x | x | - | - | - | x | - | - | x | - |
| vfprintf | x | - | - | x | x | - | - | - | - | - |
| vfscanf | x | - | - | - | - | x | - | - | x | - |
| vfwprintf | x | - | - | - | x | - | - | x | - | - |
| vfwscanf | x | - | - | - | - | - | x | - | - | x |
| vprintf | x | - | x | x | x | - | - | - | - | - |
| vscanf | x | x | - | - | - | x | - | - | x | - |
| vwprintf | x | - | x | - | x | - | - | x | - | - |
| vwscanf | x | x | - | - | - | - | x | - | - | x |
| wprintf | x | - | x | - | x | - | - | x | - | - |
| wscanf | x | x | - | - | - | - | x | - | - | x |
See the ISO C Reference for the syntax of the low-level functions.
If you choose to reimplement fgetc(), fputc(),
and __backspace(), be aware that fopen() and
related functions use the ARM layout for the __FILE structure.
You might also have to reimplement fopen() and
related functions if you define your own version of __FILE.
The printf family consists of _printf(), printf(), _fprintf(), fprintf(), vprintf(), and vfprintf().
All these functions use __FILE opaquely and depend
only on the functions fputc() and ferror().
The functions _printf() and _fprintf() are
identical to printf() and fprintf() except
that they cannot format floating-point values.
The standard output functions of the form _printf(...) are
equivalent to:
fprintf(& __stdout, ...)
where __stdout has type __FILE.
The scanf() family consists of scanf() and fscanf().
These functions depend only on the functions fgetc(), __FILE,
and __backspace(). See Reimplementing __backspace().
The standard input function of the form scanf(...) is
equivalent to:
fscanf(& __stdin, ...)
where __stdin has type __FILE.
If you define your own version of __FILE,
and your own fputc() and ferror() functions and
the __stdout object, you can use all of the printf() family, fwrite(), fputs(), puts() and the
C++ object std::cout unchanged from the library. Example 2.10 and Example 2.11 show you
how to do this. Consider modifying the system routines if you require
real file handling.
You are not required to reimplement every function shown in these examples. Only reimplement the functions that require reimplementation.
Example 2.10. Retargeting printf()
#include <stdio.h>
struct __FILE
{
int handle;
/* Whatever you require here. If the only file you are using is */
/* standard output using printf() for debugging, no file handling */
/* is required. */
};
/* FILE is typedefd in stdio.h. */
FILE __stdout;
int fputc(int ch, FILE *f)
{
/* Your implementation of fputc(). */
return ch;
}
int ferror(FILE *f)
{
/* Your implementation of ferror(). */
return 0;
}
void test(void)
{
printf(Hello world\n);
}
Be aware of endianness with fputc(). fputc() takes
an int parameter, but contains only a character.
Whether the character is in the top or the bottom byte of the integer
variable depends on the endianness. The following code sample avoids
problems with endianness:
extern void sendchar(char *ch);
int fputc(int ch, FILE *f)
{
/* example: write a character to an LCD */
char tempch = ch; // temp char avoids endianness issue
sendchar(&tempch);
return ch;
}
Example 2.11. Retargeting cout
File 1: Reimplement any functions that require reimplementation.
#include <stdio.h>
namespace std {
struct __FILE
{
int handle;
/* Whatever you require here. If the only file you are using is */
/* standard output using printf() for debugging, no file handling */
/* is required. */
};
FILE __stdout;
FILE __stdin;
FILE __stderr;
int fgetc(FILE *f)
{
/* Your implementation of fgetc(). */
return 0;
};
int fputc(int c, FILE *stream)
{
/* Your implementation of fputc(). */
}
int ferror(FILE *stream)
{
/* Your implementation of ferror(). */
}
long int ftell(FILE *stream)
{
/* Your implementation of ftell(). */
}
int fclose(FILE *f)
{
/* Your implementation of fclose(). */
return 0;
}
int fseek(FILE *f, long nPos, int nMode)
{
/* Your implementation of fseek(). */
return 0;
}
int fflush(FILE *f)
{
/* Your implementation of fflush(). */
return 0;
}
}
File 2: Print Hello world using your reimplemented functions.
#include <stdio.h>
#include <iostream>
using namespace std;
int main()
{
cout << Hello world\n;
return 0;
}
By default, fread() and fwrite() call
fast block input/output functions that are part of the ARM stream
implementation. If you define your own __FILE structure
instead of using the ARM stream implementation, fread() and fwrite() call fgetc() instead
of calling the block input/output functions.
The functions fread(), fgets(),
and gets() are implemented as a loop over fgetc() and ferror().
Each uses the FILE argument opaquely.
If you provide your own implementation of __FILE, __stdin (for gets()), fgetc(),
and ferror(), you can use these functions,
and the C++ object std::cin directly from the library.
The function __backspace() is used by
the scanf family of functions. It must never
be called directly, but reimplemented if you are retargeting the stdio arrangements
at the fgetc() level.
The semantics are:
int __backspace(FILE *stream);
__backspace(stream) must only be called
after reading a character from the stream. You must not call it
after a write, a seek, or immediately after opening the file, for
example. It returns to the stream the last character that was read
from the stream, so that the same character can be read from the
stream again by the next read operation. This means that a character
that was read from the stream by scanf but that
is not required (that is, it terminates the scanf operation)
is read correctly by the next function that reads from the stream.
__backspace is separate from ungetc().
This is to guarantee that a single character can be pushed back
after the scanf family of functions has finished.
The value returned by __backspace() is
either 0 (success) or EOF (failure).
It returns EOF only if used incorrectly, for
example, if no characters have been read from the stream. When used
correctly, __backspace() must always return 0,
because the scanf family of functions do not
check the error return.
The interaction between __backspace() and ungetc() is:
If you apply __backspace() to
a stream and then ungetc() a character into
the same stream, subsequent calls to fgetc() must
return first the character returned by ungetc(),
and then the character returned by __backspace().
If you ungetc() a character
back to a stream, then read it with fgetc(),
and then backspace it, the next character read by fgetc() must
be the same character that was returned to the stream. That is the __backspace() operation
must cancel the effect of the fgetc() operation.
However, another call to ungetc() after the
call to __backspace() is not required to succeed.
The situation where you ungetc() a
character into a stream and then __backspace() another
one immediately, with no intervening read, never arises. __backspace() must
only be called after fgetc(), so this sequence
of calls is illegal. If you are writing __backspace() implementations,
you can assume that the unget() of a character
into a stream followed immediately by a __backspace() with
no intervening read, never occurs.
__backspacewc() is the wide-character
equivalent of __backspace(). __backspacewc() behaves
in the same way as __backspace() except that
it pushes back the last wide character instead of a narrow character.
[1] The file structure.
[2] The standard
input object of type __FILE.
[3] The standard
output object of type __FILE.
[4] Outputs a character to a file.
[5] Returns the error status accumulated during file I/O.
[6] Gets a character from a file.
[7] Moves the file pointer to the previous character. See Reimplementing __backspace().