The concrete types described in \xref{sec:structures.common} and
\ref{sec:structures.root} are declared by the header file @|<sod/sod.h>|.
-The definitions described in sections \ref{sec:structures.layout} are defined
-in the header file generated by the containing module.
+The definitions described in \xref{sec:structures.layout} are defined in the
+header file generated by the containing module.
%%%--------------------------------------------------------------------------
\section{Common instance structure} \label{sec:structures.common}
instances have the basic structure of a @|struct sod_instance|.
\begin{describe}[struct sod_instance]{type}
- {struct sod_instance \{ \\ \ind
- const struct sod_vtable *_vt; \- \\
+ {struct sod_instance \{ \\ \ind
+ const struct sod_vtable *_vt; \-\\
\};}
The basic structure of all instances. Members are as follows.
\end{describe}
\begin{describe}[struct sod_vtable]{type}
- {struct sod_vtable \{ \\ \ind
- const SodClass *_class; \\
- size_t _base; \- \\
+ {struct sod_vtable \{ \\ \ind
+ const SodClass *_class; \\
+ size_t _base; \-\\
\};}
A vtable contains static metadata needed for efficient conversions and
\begin{figure}[tbp]
\begin{tabular}{p{10pt}p{10pt}}
- \begin{prog}
- struct SodObject__ilayout \{ \\ \ind
- union \{ \\ \ind
- struct SodObject__ichain_obj \{ \\ \ind
- const struct SodObject__vt_obj *_vt; \- \\
- \}; \- \\
- \} obj; \- \\
+ \begin{nprog}
+ struct SodObject__ilayout \{ \\ \ind
+ union \{ \\ \ind
+ struct SodObject__ichain_obj \{ \\ \ind
+ const struct SodObject__vt_obj *_vt; \-\\
+ \} obj; \-\\
+ \} obj; \-\\
\};
- \end{prog}
+ \end{nprog}
&
- \begin{prog}
- struct SodObject__vt_obj \{ \\ \ind
- const SodClass *_class; \\
- size_t _base; \- \\
+ \begin{nprog}
+ struct SodObject__vt_obj \{ \\ \ind
+ const SodClass *_class; \\
+ size_t _base; \\
+ struct SodObject__vtmsgs_obj \{ \\ \ind
+ void (*init)(SodObject *me, ...); \\
+ void (*init__v)(SodObject *me, va_list); \\
+ int (*teardown)(SodObject *me); \-\\
+ \} obj; \-\\
\};
- \end{prog} \\
+ \end{nprog} \\
\end{tabular}
\caption{Instance and vtable layout of @|SodObject|}
\label{fig:structures.root.sodobject}
\end{figure}
\begin{describe}[SodObject]{cls}
- {[metaclass = SodClass, lisp_metaclass = sod_class] \\
- class SodObject \{ \}}
+ {[nick = obj, metaclass = SodClass,
+ lisp_metaclass = sod_class] \\
+ class SodObject \{ \\ \ind
+ void init(?);
+ \}}
- The @|SodObject| class defines no slots or messages. Because @|SodObject|
- has no direct superclasses, there is only one chain, and no inherited
- slots or messages, so the single chain contains only a vtable pointer.
+ The @|SodObject| class defines no slots. Because @|SodObject| has no
+ direct superclasses, there is only one chain, and no inherited slots or
+ messages, so the single chain contains only a vtable pointer.
- Since there are no messages, and @|SodClass| also has only one chain, the
- vtable contains only the standard class pointer and offset-to-base members.
- In a direct instance of @|SodObject| (why would you want one?) the class
- pointer contains the address of @|SodObject__class| and the offset is zero.
+ Since @|SodClass| also has only one chain, the vtable contains only the
+ standard class pointer and offset-to-base members. In a direct instance of
+ @|SodObject| (why would you want one?) the class pointer contains the
+ address of @|SodObject__class| and the offset is zero.
The instance and vtable layout of @|SodObject| is shown in
\xref{fig:structures.root.sodobject}.
+
+ The following messages are defined.
+
+ \begin{describe}[obj.init]{msg}{void init(?);}
+ Initialize a newly allocated instance.
+
+ This message uses a custom method combination which works like the
+ standard method combination except that default behaviour specific to the
+ receiver's direct class is invoked if no primary or around method
+ overrides. This default behaviour may be invoked multiple times if some
+ method calls on its @|next_method| function more than once.
+
+ This default behaviour is to initialize the instance's slots using the
+ defined slot initializers, and execute the initialization fragments.
+ Each slot is initialized using the most specific applicable initializer,
+ if any. Slots without an initializer are left uninitialized.
+
+ Slots are initialized and initialization fragments executed together, a
+ superclass at a time: first, the superclass's slots are initialized (if
+ any); then the superclass's initialization fragments (if any) are
+ executed, starting with the least specific superclass first. Slots and
+ initialization fragments defined by the same class are processed in the
+ order in which they appear in the class definition.
+
+ There are no standard keyword arguments; methods on subclasses are free
+ to introduce their own in the usual way.
+
+ It is usual to provide complex initialization behaviour as @|after|
+ methods. This ensures that slots have been initialized as necessary
+ before the method executes.
+
+ For more details on instance construction, see
+ \xref{sec:concepts.lifecycle.birth}.
+ \end{describe}
+
+ \begin{describe}[obj.teardown]{msg}{int teardown();}
+ Teardown an instance which is no longer required.
+
+ The message returns an integer flag. A zero value means that the
+ instance is safe to deallocate. A nonzero value means that the instance
+ should not be deallocated, and that it is safe for the caller to simply
+ forget about it. This simple protocol may be used, for example, to
+ implement a reference-counting system.
+
+ This message uses a custom method combination which works like the
+ standard method combination except that default behaviour is invoked if
+ no primary or around method overrides.
+
+ This default behaviour is to execute each superclass's teardown
+ fragments, most specific first, and then return zero to indicate that the
+ object is ready for deallocation. Teardown fragments defined by the same
+ class are processed in the order in which they appear in the class
+ definition.
+
+ It is usual to provide complex teardown behaviour as @|before| methods.
+ Logic to decide whether to allow deallocation is usually implemented as
+ @|around| methods.
+ \end{describe}
\end{describe}
\subsection{The SodClass class} \label{sec:structures.root.sodclass}
\begin{describe}[SodClass]{cls}
- {class SodClass : SodObject \{ \\ \ind
- const char *name; \\
- const char *nick; \\
- size_t initsz; \\
- void *(*imprint)(void *@<p>); \\
- void *(*init)(void *@<p>); \\
- size_t n_supers; \\
- const SodClass *const *supers; \\
- size_t n_cpl; \\
- const SodClass *const *cpl; \\
- const SodClass *link; \\
- const SodClass *head; \\
- size_t level; \\
- size_t n_chains; \\
- const struct sod_chain *chains; \\
- size_t off_islots; \\
- size_t islotsz; \- \\
+ {[nick = cls, link = SodObject] \\
+ class SodClass : SodObject \{ \\ \ind
+ const char *name; \\
+ const char *nick; \\
+ size_t initsz; \\
+ size_t align; \\
+ void *(*imprint)(void *@<p>); \\
+ size_t n_supers; \\
+ const SodClass *const *supers; \\
+ size_t n_cpl; \\
+ const SodClass *const *cpl; \\
+ const SodClass *link; \\
+ const SodClass *head; \\
+ size_t level; \\
+ size_t n_chains; \\
+ const struct sod_chain *chains; \\
+ size_t off_islots; \\
+ size_t islotsz; \-\\
\}}
- The @|SodClass| class defines no messages, but there are a number of slots.
- Its only direct superclass is @|SodObject| and so (like its superclass) its
- vtable is trivial.
+ The @|SodClass| class defines no additional messages , but there are a
+ number of slots. Its only direct superclass is @|SodObject| and so (like
+ its superclass) its vtable is simple.
The slots defined are as follows.
\begin{description} \let\makelabel\code
\item[initsz] The size in bytes required to store an instance of the class.
+ \item[align] A sufficient alignment for the class's instance storage.
+
\item[imprint] A pointer to a function: given a pointer @<p> to at least
@<initsz> bytes of appropriately aligned memory, `imprint' this memory it
so that it becomes a minimally functional instance of the class: all of
the vtable and class pointers are properly initialized, but the slots are
left untouched. The function returns its argument @<p>.
- \item[init] A pointer to a function: given a pointer @<p> to at least
- @<initsz> bytes of appropriately aligned memory, initialize an instance
- of the class in it: all of the vtable and class pointers are initialized,
- as are slots for which initializers are defined. Other slots are left
- untouched. The function returns its argument @<p>.
-
\item[n_supers] The number of direct superclasses. (This is zero exactly
in the case of @|SodObject|.)
\item[cpl] A pointer to an array of pointers to class objects listing all
of the class's superclasses, from most- to least-specific, starting with
- the class itself, so $c@->@|cls|.@|cpl|[0] = c$ for all class objects
+ the class itself, so $@|$c$@->cls.cpl[0]| = c$ for all class objects
$c$.
\item[link] If the class is a chain head, then this is a null pointer;
might or might not be a direct superclass).
\item[head] A pointer to the least-specific class in this class's chain; so
- $c@->@|cls|.@|head|@->@|cls|.@|link|$ is always null, and either
- $c@->@|cls|.@|link|$ is null (in which case $c@->@|cls|.@|head| = c$) or
- $c@->@|cls|.@|head| = c@->@|cls|.@|link|@->@|cls|.@|head|$.
+ @|$c$@->cls.head@->cls.link| is always null, and either @|$c$@->cls.link|
+ is null (in which case $@|$c$@->cls.head| = c$) or $@|$c$@->cls.head| =
+ @|$c$@->cls.link@->cls.head|$.
\item[level] The number of less specific superclasses in this class's
- chain. If $c@->@|cls|.@|link|$ is null then $c@->@|cls|.@|level|$ is
- zero; otherwise $c@->@|cls|.@|level| =
- c@->@|cls|.@|link|@->@|cls|.@|level| + 1$.
+ chain. If @|$c$@->cls.link| is null then @|$c$@->cls.level| is zero;
+ otherwise $@|$c$@->cls.level| = @|$c$@->cls.link@->cls.level| + 1$.
\item[n_chains] The number of chains formed by the class's superclasses.
\item[chains] A pointer to an array of @|struct sod_chain| structures (see
below) describing the class's superclass chains, in decreasing order of
specificity of their most specific classes. It is always the case that
- $c@->@|cls|.@|chains|[0].@|classes|[c@->@|cls|.@|level|] = c$.
+ $@|$c$@->cls.chains[0].classes[$c$@->cls.level]| = c$.
\item[off_islots] The offset of the class's @|islots| structure relative to
its containing @|ichain| structure. The class doesn't define any slots
\end{describe}
\begin{describe}[struct sod_chain]{type}
- {struct sod_chain \{ \\ \ind
- size_t n_classes; \\
- const SodClass *const *classes; \\
- size_t off_ichain; \\
- const struct sod_vtable *vt; \\
- size_t ichainsz; \- \\
+ {struct sod_chain \{ \\ \ind
+ size_t n_classes; \\
+ const SodClass *const *classes; \\
+ size_t off_ichain; \\
+ const struct sod_vtable *vt; \\
+ size_t ichainsz; \-\\
\};}
The @|struct sod_chain| structure describes an individual chain of
\item[classes] A pointer to an array of class pointers listing the classes
in the chain from least- to most-specific. So
- $@<classes>[i]@->@|cls|.@|head| = @<classes>[0]$ for all $0 \le i <
- @<n_classes>$, $@<classes>[0]@->@|cls|.@|link|$ is always null, and
- $@<classes>[i]@->@|cls|.@|link| = @<classes>[i - 1]$ if $1 \le i <
+ $@|@<classes>[$i$]@->cls.head| = @|@<classes>[0]|$ for all $0 \le i <
+ @<n_classes>$, @|@<classes>[0]@->cls.link| is always null, and
+ $@|@<classes>[$i$]@->cls.link| = @|@<classes>[$i - 1$]|$ if $1 \le i <
@<n_classes>$.
\item[off_ichain] The size of the @|ichain| structure for this chain.
type @|struct $C$__ilayout|.
\begin{prog}
- struct $C$__ilayout \{ \\ \ind
- union $C$__ichainu_$h$ \{ \\ \ind
- struct $C$__ichain_$h$ \{ \\ \ind
- const struct $C$__vt_$h$ *_vt; \\
- struct $H$__islots $h$; \\
- \quad$\vdots$ \\
- struct $C$__islots \{ \\ \ind
- @<type>_1 @<slot>_1; \\
- \quad$\vdots$ \\
- @<type>_n @<slot>_n; \- \\
- \} $c$; \- \\
- \} $c$; \\
- struct $H$__ichain_$h$ $h$; \\
- \quad$\vdots$ \- \\
- \} $h$; \\
- union $B$__ichainu_$i$ $i$; \\
- \quad$\vdots$ \- \\
- \};
- \\[\bigskipamount]
+ struct $C$__ilayout \{ \\ \ind
+ union $C$__ichainu_$h$ \{ \\ \ind
+ struct $C$__ichain_$h$ \{ \\ \ind
+ const struct $C$__vt_$h$ *_vt; \\
+ struct $H$__islots $h$; \\
+ \quad$\vdots$ \\
+ struct $C$__islots \{ \\ \ind
+ @<type>_1 @<slot>_1; \\
+ \quad$\vdots$ \\
+ @<type>_n @<slot>_n; \-\\
+ \} $c$; \-\\
+ \} $c$; \\
+ struct $H$__ichain_$h$ $h$; \\
+ \quad$\vdots$ \-\\
+ \} $h$; \\
+ union $B$__ichainu_$i$ $i$; \\
+ \quad$\vdots$ \-\\
+ \}; \\+
+
typedef struct $C$__ichain_$h$ $C$;
\end{prog}
naming their least specific classes; the least specific class in a chain is
called the \emph{chain head}. Suppose that the chain head of the chain
containing $C$ itself is named $H$ (though keep in mind that it's possible
-that .$H$ is in fact $C$ itself.)
+that $H$ is in fact $C$ itself.)
\subsubsection{The ilayout structure}
The @|ilayout| structure contains one member for each of $C$'s superclass
struct $A$__ichain_$h$ $a$;
\end{prog}
for each of $C$'s superclasses $A$ in the same chain in some (unimportant)
-order.
+order. The (somewhat obtuse) purpose of this union is to engage the `common
+initial sequence' rule of \cite[6.5.2.3]{FIXME:C99}.
\subsubsection{The ichain structure}
-The
-@|ichain|
-structure contains (in order), a pointer
+The @|ichain| structure contains (in order), a pointer
\begin{prog}
const struct $C$__vt_$h$ *_vt;
\end{prog}
A vtable for a class $C$ with chain head $H$ has the following general
structure.
\begin{prog}
- union $C$__vtu_$h$ \{ \\ \ind
- struct $C$__vt_$h$ \{ \\ \ind
- const $P$ *_class; \\
- size_t _base; \\
- \quad$\vdots$ \\
- const $Q$ *_cls_$j$; \\
- \quad$\vdots$ \\
- ptrdiff_t _off_$i$; \\
- \quad$\vdots$ \\
- struct $C$__vtmsgs_$a$ \{ \\ \ind
- @<type> (*@<msg>)($C$ *, $\dots$); \\
- \quad$\vdots$ \- \\
- \} $a$; \\
- \quad$\vdots$ \- \\
- \} $c$; \- \\
- \};
- \\[\bigskipamount]
+ union $C$__vtu_$h$ \{ \\ \ind
+ struct $C$__vt_$h$ \{ \\ \ind
+ const $P$ *_class; \\
+ size_t _base; \\
+ \quad$\vdots$ \\
+ const $Q$ *_cls_$j$; \\
+ \quad$\vdots$ \\
+ ptrdiff_t _off_$i$; \\
+ \quad$\vdots$ \\
+ struct $C$__vtmsgs_$a$ \{ \\ \ind
+ @<type> (*@<msg>)($C$ *, $\dots$); \\
+ \quad$\vdots$ \-\\
+ \} $a$; \\
+ \quad$\vdots$ \-\\
+ \} $c$; \-\\
+ \}; \\+
+
extern const union $C$__vtu_$h$ $C$__vtable_$h$;
\end{prog}
+In the following, let $M$ be the metaclass of $C$.
+
\subsubsection{The vtu union}
The outer layer is a @|union $C$__vtu_$h$| containing a member
\begin{prog}
for each of $C$'s superclasses $A$ in the same chain, with $C$ itself listed
first.
-This is mostly an irrelevant detail,
-whose purpose is to defend against malicious compilers:
-pointers are always to one of the inner
-@|vt|
-structures.
-It's important only because it's the outer
-@|vtu|
-union which is exported by name.
-Specifically, for each chain of
-$C$'s
-superclasses
-there is an external object
+This is mostly an irrelevant detail, whose purpose is to defend against
+malicious compilers: pointers are always to one of the inner @|vt|
+structures. It's important only because it's the outer @|vtu| union which is
+exported by name. Specifically, for each chain of $C$'s superclasses there is
+an external object
\begin{prog}
const union $A$__vtu_$i$ $C$__vtable_$i$;
\end{prog}
\begin{prog}
@<type>_0 $m$(@<type>_1 @<arg>_1, $\ldots$, @<type>_n @<arg>_n, \dots);
\end{prog}
+or a standard message which takes keyword arguments, defined as
+\begin{prog}
+ @<type>_0 $m$(\=@<type>_1 @<arg>_1,
+ $\ldots$,
+ @<type>_n @<arg>_n? \+\\
+ @<type>_{n+1} @<kw>_{n+1} @[= @<dflt>_{n+1}@],
+ $\ldots$,
+ @<type>_m @<kw>_m @[= @<dflt>_m@]);
+\end{prog}
two entry points are defined: the usual `main' entry point which accepts a
variable number of arguments, and a `valist' entry point which accepts an
argument of type @|va_list| in place of the variable portion of the argument
-list.
+list or keywords.
\begin{prog}
@<type>_0 $m$($C$ *me, @<type>_1 @<arg>_1, $\ldots$,
- @<type>_n @<arg>_n, \dots); \\
+ @<type>_n @<arg>_n, \dots); \\
@<type>_0 $m$__v($C$ *me, @<type>_1 @<arg>_1, $\ldots$,
@<type>_n @<arg>_n, va_list sod__ap);
\end{prog}
which makes sending the message $m$ to an instance of (any subclass of) $C$
somewhat less ugly.
-If $m$ takes a variable number of arguments, the macro is more complicated
-and is only available in compilers advertising C99 support, but the effect is
-the same. For each variable-argument message, there is also an additional
-macro for calling the `valist' entry point.
+If $m$ takes a variable number of arguments, or keyword arguments, the macro
+is more complicated and is only available in compilers advertising C99
+support, but the effect is the same. For each variable-argument message,
+there is also an additional macro for calling the `valist' entry point.
\begin{prog}
\#define $C$_$m$__v(@<me>, $\ldots$, @<sod__ap>)
@<me>@->_vt@->$c$.$m$__v(@<me>, $\ldots$, @<sod__ap>)
Finally, the class object is defined as
\begin{prog}
- extern const struct $R$__ilayout $C$__classobj; \\
+ extern const struct $R$__ilayout $C$__classobj; \\
\#define $C$__class (\&$C$__classobj.$j$.$r$)
\end{prog}
The exported symbol @|$C$__classobj| contains the entire class instance.