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