.. 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`.