.. index::
pair: WriteF function; design
.. _design-writef:
The WriteF function
===================
.. mps:prefix:: design.mps.writef
Introduction
------------
:mps:tag:`intro` This document describes the :c:func:`WriteF()` function, which
allows formatted output in a manner similar to ANSI C ``printf``, but
allows the MPM to operate in a freestanding environment (see
design.mps.exec-env).
:mps:tag:`background` The documents design.mps.exec-env and design.mps.lib
describe the design of the library interface and the reason that it
exists.
Design
------
:mps:tag:`no-printf` There is no dependency on :c:func:`printf()`. The MPM only
depends on :c:func:`fputc()` and :c:func:`fputs()`, via the Library Interface
(design.mps.lib). This makes it much easier to deploy the MPS in a
freestanding environment. This is achieved by implementing our own
internal output routines in mpm.c.
:mps:tag:`writef` Our output requirements are few, so the code is short. The
only output function which should be used in the rest of the MPM is
:c:func:`WriteF()`.
.. c:function:: Res WriteF(mps_lib_FILE *stream, Count depth, ...)
If ``depth`` is greater than zero, then the first format character,
and each format character after a newline, is preceded by ``depth``
spaces.
:c:func:`WriteF()` expects a format string followed by zero or more items to
insert into the output, followed by another format string, more items,
and so on, and finally a :c:macro:`NULL` format string. For example::
res = WriteF(stream, depth,
"Hello: $A\n", address,
"Spong: $U ($S)\n", number, string,
NULL);
if (res != ResOK) return res;
This makes :c:func:`Describe()` methods much easier to write. For example, :c:func:`BufferDescribe()` contains the following code::
res = WriteF(stream, depth,
"Buffer $P ($U) {\n",
(WriteFP)buffer, (WriteFU)buffer->serial,
" class $P (\"$S\")\n",
(WriteFP)buffer->class, buffer->class->name,
" Arena $P\n", (WriteFP)buffer->arena,
" Pool $P\n", (WriteFP)buffer->pool,
" ", buffer->isMutator ? "Mutator" : "Internal", " Buffer\n",
" mode $C$C$C$C (TRANSITION, LOGGED, FLIPPED, ATTACHED)\n",
(WriteFC)((buffer->mode & BufferModeTRANSITION) ? 't' : '_'),
(WriteFC)((buffer->mode & BufferModeLOGGED) ? 'l' : '_'),
(WriteFC)((buffer->mode & BufferModeFLIPPED) ? 'f' : '_'),
(WriteFC)((buffer->mode & BufferModeATTACHED) ? 'a' : '_'),
" fillSize $UKb\n", (WriteFU)(buffer->fillSize / 1024),
" emptySize $UKb\n", (WriteFU)(buffer->emptySize / 1024),
" alignment $W\n", (WriteFW)buffer->alignment,
" base $A\n", buffer->base,
" initAtFlip $A\n", buffer->initAtFlip,
" init $A\n", buffer->ap_s.init,
" alloc $A\n", buffer->ap_s.alloc,
" limit $A\n", buffer->ap_s.limit,
" poolLimit $A\n", buffer->poolLimit,
NULL);
if (res != ResOK) return res;
:mps:tag:`types` For each format ``$X`` that :c:func:`WriteF()` supports, there is a
type defined in impl.h.mpmtypes :c:func:`WriteFX()` which is the promoted
version of that type. These are provided both to ensure promotion and
to avoid any confusion about what type should be used in a cast. It is
easy to check the casts against the formats to ensure that they
correspond.
:mps:tag:`types.future` It is possibly that this type set or similar may be
used in future in some generalisation of varargs in the MPS.
:mps:tag:`formats` The formats supported are as follows.
======= =========== ================== ======================================
Code Name Type Example rendering
======= =========== ================== ======================================
``$A`` address :c:type:`Addr` ``000000019EF60010``
``$P`` pointer ``void *`` ``000000019EF60100``
``$F`` function ``void *(*)()`` ``0001D69E01000000`` (see `.f`_)
``$S`` string ``char *`` ``hello``
``$C`` character ``char`` ``x``
``$W`` word :c:type:`ULongest` ``0000000000109AE0``
``$U`` decimal :c:type:`ULongest` ``42``
``$B`` binary :c:type:`ULongest` ``00000000000000001011011110010001``
``$$`` dollar -- ``$``
======= =========== ================== ======================================
Note that ``WriteFC`` is an ``int``, because that is the default
promotion of a ``char`` (see :mps:ref:`.types`).
:mps:tag:`snazzy` We should resist the temptation to make :c:func:`WriteF()` an
incredible snazzy output engine. We only need it for :c:func:`Describe()`
methods. At the moment it's a simple bit of code -- let's keep it that
way.
_`.f`: The ``F`` code is used for function pointers. ISO C forbids casting
function pointers to other types, so the bytes of their representation are
written sequentially, and may have a different endianness to other pointers.
Could be smarter, or even look up function names, but see :mps:ref:`.snazzy`.