check-message-type generic
check-method-type generic
make-sod-class function
+ make-sod-class-initfrag generic
make-sod-class-initializer generic
+ make-sod-class-tearfrag generic
make-sod-initializer-using-slot generic
make-sod-instance-initializer generic
make-sod-message generic
sod-class-class-initializers generic setf
sod-class-direct-superclasses generic
sod-class-ilayout generic
+ sod-class-initfrags generic setf
sod-class-initializer class
sod-class-instance-initializers generic setf
sod-class-messages generic setf
sod-class-precedence-list generic
sod-class-slots generic setf
sod-class-state generic
+ sod-class-tearfrags generic setf
sod-class-type generic
sod-class-vtables generic
sod-initializer class
basic-effective-method sod-class sod-class
sod-parser:make-scanner-stream
sod-token-scanner
+make-sod-class-initfrag
+ sod-class t t
make-sod-class-initializer
sod-class t t t t
+make-sod-class-tearfrag
+ sod-class t t
make-sod-initializer-using-slot
sod-class sod-slot t t t t
make-sod-instance-initializer
sod-class
sod-class-ilayout
sod-class
+sod-class-initfrags
+ sod-class
+(setf sod-class-initfrags)
+ t sod-class
sod-class-instance-initializers
sod-class
(setf sod-class-instance-initializers)
t sod-class
sod-class-state
sod-class
+sod-class-tearfrags
+ sod-class
+(setf sod-class-tearfrags)
+ t sod-class
sod-class-type
sod-class
sod-class-vtables
aggregating-message
sod::initialization-message
standard-message
+ sod::teardown-message
sod-message-kernel-function
aggregating-message
sod-message-method-class
necessary.
\end{enumerate}
The \descref{SOD_DECL}[macro]{mac} handles constructing instances with
-automatic storage duration (`on the stack'). Programmers can add support for
-other allocation strategies by using the \descref{SOD_INIT}[macro]{mac} and
-the \descref{sod_init}{fun} and \descref{sod_initv}{fun} functions, which
-package up imprinting and initialization.
+automatic storage duration (`on the stack'). Similarly, the
+\descref{SOD_MAKE}[macro]{mac} and the \descref{sod_make}{fun} and
+\descref{sod_makev}{fun} functions construct instances allocated from the
+standard @|malloc| heap. Programmers can add support for other allocation
+strategies by using the \descref{SOD_INIT}[macro]{mac} and the
+\descref{sod_init}{fun} and \descref{sod_initv}{fun} functions, which package
+up imprinting and initialization.
\subsubsection{Allocation}
Instances of most classes (specifically including those classes defined by
method combination which works like the standard combination, except that the
\emph{default behaviour}, if there is no overriding method, is to initialize
the instance's slots using the initializers defined in the class and its
-superclasses. This default behaviour may be invoked multiple times if some
-method calls on its @|next_method| more than once, unless some other method
-takes steps to prevent this.
+superclasses, and to invoke each superclass's initialization fragments. This
+default behaviour may be invoked multiple times if some method calls on its
+@|next_method| more than once, unless some other method takes steps to
+prevent this.
Slots are initialized in a well-defined order.
\begin{itemize}
their definitions appear.
\end{itemize}
-The recommended way to add new initialization behaviour is to define @|after|
-methods on the @|init| message. These will be run after the slot
+A class can define \emph{initialization fragments}: pieces of literal code to
+be executed to set up a new instance. Each superclass's initialization
+fragments are executed with @|me| bound to an instance pointer of the
+appropriate superclass type, immediately after that superclass's slots (if
+any) have been initialized; therefore, fragments defined by a more specific
+superclass are executed after fragments defined by a more specific
+superclass. A class may define more than one initialization fragment: the
+fragments are executed in the order in which they appear in the class
+definition. It is possible for an initialization fragment to use @|return|
+or @|goto| for special control-flow effects, but this is not likely to be a
+good idea.
+
+Note that an initialization fragment defined in a class is copied literally
+into each subclass's initialization method. This is fine for simple cases
+but wasteful if the initialization logic is complicated. More complex
+initialization behaviour should be added either by having an initialization
+fragments call functions (necessarily with external linkage), or by defining
+@|after| methods on the @|init| message. These will be run after the slot
initializers have been applied, in reverse precedence order.
Initialization is \emph{parametrized}, so the caller may select from a space
@|init| message accepts keyword arguments (\xref{sec:concepts.keywords})
which can be defined and used by methods defined on it.
-\subsubsection{Example}
-The following is a simple function, with syntactic-sugar macro, which
-allocate storage for an instance of a class, imprints and initializes it, and
-returns a pointer to the new instance.
-\begin{prog}
- void *make_instance(const SodClass *c, @|\dots|) \\
- \{ \\ \ind
- va_list ap;
- void *p = malloc(c@->cls.initsz); \\
- if (!p) return (0); \\
- va_start(ap, c); \\
- sod_initv(c, p, ap); \\
- va_end(ap); \\
- return (p); \- \\
- \}
- \\+
- \#define MAKE(cls, keys) (cls *)make_instance(cls\#\#__class, keys)
-\end{prog}
-
\subsection{Destruction}
\label{sec:concepts.lifecycle.death}
\item \emph{Deallocation} releases the memory used to store the instance so
that it can be reused.
\end{enumerate}
+Teardown alone, for objects which require special deallocation, or for which
+deallocation occurs automatically (e.g., instances with automatic storage
+duration, or instances whose storage will be garbage-collected), is performed
+using the \descref{sod_teardown}[function]{fun}. Destruction of instances
+allocated from the standard @|malloc| heap is done using the
+\descref{sod_destroy}[function]{fun}.
\subsubsection{Teardown}
-Details of teardown are class-specific, but typically it involves releasing
-resources held by the instance, and possibly unlinking it from some larger
-data structure which used to keep track of it.
+Details of initialization are necessarily class-specific, but typically it
+involves setting the instance's slots to appropriate values, and possibly
+linking it into some larger data structure to keep track of it.
+
+Teardown is performed by sending the instance the @|teardown| message,
+defined by the @|SodObject| class. The message returns an integer, used as a
+boolean flag. If the message returns zero, then the instance's storage
+should be deallocated. If the message returns nonzero, then it is safe for
+the caller to forget about instance, but should not deallocate its storage.
+This is \emph{not} an error return: if some teardown method fails then the
+program may be in an inconsistent state and should not continue.
-There is no provided protocol for teardown: classes whose instances require
-teardown behaviour must define and implement an appropriate protocol of their
-own. The following class may serve for simple cases.
+This simple protocol can be used, for example, to implement a reference
+counting system, as follows.
\begin{prog}
- [nick = disposable] \\
- class DisposableObject : SodObject \{ \\- \ind
- void release() \{ ; \} \\
- \quad /* Release resources held by the receiver. */ \- \\-
- \}
- \\+
- code c : user \{ \\- \ind
- /* If p is a a DisposableObject then release its resources. */ \\
- void maybe_dispose(void *p) \\
+ [nick = ref] \\
+ class ReferenceCountedObject \{ \\ \ind
+ unsigned nref = 1; \\-
+ void inc() \{ me@->ref.nref++; \} \\-
+ [role = around] \\
+ int obj.teardown() \\
\{ \\ \ind
- DisposableObject *d = SOD_CONVERT(DisposableObject, p); \\
- if (d) DisposableObject_release(d); \- \\
+ if (--\,--me@->ref.nref) return (1); \\
+ else return (CALL_NEXT_METHOD); \- \\
\} \- \\
\}
\end{prog}
+This message uses a nonstandard method combination which works like the
+standard combination, except that the \emph{default behaviour}, if there is
+no overriding method, is to execute the superclass's teardown fragments, and
+to return zero. This default behaviour may be invoked multiple times if some
+method calls on its @|next_method| more than once, unless some other method
+takes steps to prevent this.
+
+A class can define \emph{teardown fragments}: pieces of literal code to be
+executed to shut down an instance. Each superclass's teardown fragments are
+executed with @|me| bound to an instance pointer of the appropriate
+superclass type; fragments defined by a more specific superclass are executed
+before fragments defined by a more specific superclass. A class may define
+more than one teardown fragment: the fragments are executed in the order in
+which they appear in the class definition. It is possible for an
+initialization fragment to use @|return| or @|goto| for special control-flow
+effects, but this is not likely to be a good idea. Similarly, it's probably
+a better idea to use an @|around| method to influence the return value than
+to write an explicit @|return| statement in a teardown fragment.
+
\subsubsection{Deallocation}
The details of instance deallocation are obviously specific to the allocation
strategy used by the instance, and this is often orthogonal from the object's
require the proper base address of the instance's storage, which can be
determined using the \descref{SOD_INSTBASE}[macro]{mac}.
-\subsubsection{Example}
-The following is a counterpart to the @|new_instance| function
-(\xref{sec:concepts.lifecycle.birth}), which tears down and deallocates an
-instance allocated using @|malloc|.
-\begin{prog}
- void free_instance(void *p) \\
- \{ \\ \ind
- SodObject *obj = p; \\
- maybe_dispose(p); \\
- free(SOD_INSTBASE(obj)); \- \\
- \}
-\end{prog}
-
%%%--------------------------------------------------------------------------
\section{Metaclasses} \label{sec:concepts.metaclasses}
\&key \=:name :nick :location :pset \+ \\
:superclasses :link :metaclass \\
:slots :instance-initializers :class-initializers \\
- :messages :methods}
+ :initfrags :tearfrags :messages :methods}
\end{describe}
\begin{describe*}
\dhead{gf}{setf (sod-class-instance-initializers @<class>) @<list>}
\dhead{gf}{sod-class-class-initializers @<class> @> @<list>}
\dhead{gf}{setf (sod-class-class-initializers @<class>) @<list>}
+ \dhead{gf}{sod-class-initfrags @<class> @> @<list>}
+ \dhead{gf}{setf (sod-class-initfrags @<class>) @<list>}
+ \dhead{gf}{sod-class-tearfrags @<class> @> @<list>}
+ \dhead{gf}{setf (sod-class-tearfrags @<class>) @<list>}
\dhead{gf}{sod-class-messages @<class> @> @<list>}
\dhead{gf}{setf (sod-class-messages @<class>) @<list>}
\dhead{gf}{sod-class-methods @<class> @> @<list>}
\nlret @<init>}
\end{describe}
+\begin{describe*}
+ {\dhead{gf}{make-sod-class-initfrag @<class> @<frag> @<pset>
+ \&optional @<floc>}
+ \dhead{gf}{make-sod-class-tearfrag @<class> @<frag> @<pset>
+ \&optional @<floc>}}
+\end{describe*}
+
\begin{describe}{cls}{sod-message () \&key :name :location :class :type}
\end{describe}
\xref{sec:structures.layout.instance}.
\end{describe*}
+\begin{describe}[sod_teardown]{fun}{int sod_teardown(void *@<p>);}
+ Tears down an instance of a class, releasing any resources it holds.
+
+ This function is a very thin wrapper around sending the @|obj.teardown|
+ message. See the description of that message
+ (page~\pageref{msg:obj.teardown}) and \xref{sec:concepts.lifecycle.death}
+ for details.
+\end{describe}
+
\subsubsection{Automatic storage duration}
The following macro constructs an instance with automatic storage duration.
\descref{KWARGS}{mac} or \descref{NO_KWARGS}{mac}.
The instance has automatic storage duration: pointers to it will become
- invalid when control exits the scope of the declaration.
+ invalid when control exits the scope of the declaration. If necessary, the
+ instance should be torn down before this happens, using the
+ \descref{sod_teardown}[function]{fun}.
+\end{describe}
+
+\subsubsection{Dynamic allocation}
+The following macros and functions deal with objects allocated from the
+standard C heap. They don't work in freestanding implementations where
+@|malloc| and @|free| are not available.
+
+\begin{describe*}
+ {\dhead[SOD_MAKE]{mac}{@<cls> *SOD_MAKE(@<cls>, @<keywords>);}
+ \dhead[sod_make]{fun}{void *sod_make(const SodClass *@<cls>, \dots);}
+ \dhead[sod_makev]{fun}
+ {void *sod_makev(const SodClass *@<cls>, va_list @<ap>);}}
+ Constructs and returns a pointer to a new instance of @<cls>.
+
+ The direct class for the new instance is specified as a class name to
+ @|SOD_MAKE|, or a class object to the functions.
+
+ Keyword arguments for the initialization message may be provided. The
+ @|SOD_MAKE| macro expects a single preprocessor-time argument which is
+ a use of one of \descref{KWARGS}{mac} or \descref{NO_KWARGS}{mac}; the
+ @|sod_make| function expects the keywords as a variable-length argument
+ tail; and @|sod_makev| expects the keywords to be passed indirectly,
+ through the captured argument-tail cursor @<ap>.
+
+ Storage for the new instance will have been allocated using the standard
+ @|malloc| function. The easiest way to destroy the instance, when it is no
+ longer needed, is probably to call the
+ \descref{sod_destroy}[function]{fun}.
+
+ The return value is an instance pointer for the class @<cls>; the
+ @|SOD_MAKE| macro will have converted it to the correct type, so it should
+ probably be used where possible.
+\end{describe*}
+
+\begin{describe}[sod_destroy]{fun}{int sod_destroy(void *@<p>);}
+ Tears down and frees an instance allocated using @|malloc|.
+
+ The pointer @<p> should be an instance pointer, i.e., a pointer to any of
+ an instance's chains. The instance is torn down, by sending it the
+ \descref{obj.teardown}[message]{msg}. If the instance reports itself ready
+ for deallocation, then its storage is released using @|free|. The return
+ value is the value returned by the @|obj.teardown| message.
\end{describe}
%%%----- That's all, folks --------------------------------------------------
size_t _base; \\
struct SodObject__vtmsgs_obj \{ \\ \ind
void (*init)(SodObject *me, ...); \\
- void (*init__v)(SodObject *me, va_list); \- \\
+ void (*init__v)(SodObject *me, va_list); \\
+ int (*teardown)(SodObject *me); \- \\
\} obj; \- \\
\};
\end{nprog} \\
The instance and vtable layout of @|SodObject| is shown in
\xref{fig:structures.root.sodobject}.
- The following message is defined.
+ The following messages are defined.
\begin{describe}[obj.init]{msg}{void init(?);}
Initialize a newly allocated instance.
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: each slot is initialized using the most
- specific applicable initializer, if any. Slots without an initializer
- are left uninitialized.
+ 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 in reverse-precedence order of their defining
- classes; i.e., slots defined by a less specific superclass are
- initialized earlier than slots defined by a more specific superclass.
- Slots defined by the same class are initialized in the order in which
- they appear in the class definition.
+ 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.
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}
<class-item> ::= <slot-item>
\alt <initializer-item>
+\alt <fragment-item>
\alt <message-item>
\alt <method-item>
\end{grammar}
class's superclasses (including itself); the second must be the name of a
slot defined in that superclass.
+\subsubsection{Fragment items}
+\begin{grammar}
+<fragment-item> ::= <fragment-kind> "{" <c-fragment> "}"
+
+<fragment-kind> ::= "init" | "teardown"
+\end{grammar}
+
\subsubsection{Message items}
\begin{grammar}
<message-item> ::=
dist_man_MANS += keyword.3
pkginclude_HEADERS += sod.h
-libsod_la_SOURCES += sod.c
+libsod_la_SOURCES += sod.c sod-hosted.c
dist_man_MANS += sod.3
###--------------------------------------------------------------------------
--- /dev/null
+/* -*-c-*-
+ *
+ * Runtime support for SOD requiring hosted implementation
+ *
+ * (c) 2015 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the Sensible Object Design, an object system for C.
+ *
+ * SOD is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * SOD is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with SOD; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdlib.h>
+
+#include "sod.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @sod_make@, @sod_makev@ --- *
+ *
+ * Arguments: @const SodClass *cls@ = class object for new instance
+ * @va_list ap, ...@ = initialization keyword arguments
+ *
+ * Returns: Pointer to the newly-allocated initialized instance, or null.
+ *
+ * Use: Allocates storage for a new instance, initializes it, and
+ * returns a pointer to it. If allocation fails, a null pointer
+ * is returned instead.
+ *
+ * This function will allocate the storage using @malloc@, and
+ * then initialize it as for @sod_init@.
+ *
+ * It's usually convenient to use the macro @SOD_MAKE@ rather
+ * than calling @sod_make@ directly.
+ *
+ * (This function is not available in freestanding environments
+ * lacking @malloc@ and @free@.)
+ */
+
+void *sod_make(const SodClass *cls, ...)
+{
+ void *p;
+ va_list ap;
+
+ va_start(ap, cls);
+ p = sod_makev(cls, ap);
+ va_end(ap);
+ return (p);
+}
+
+void *sod_makev(const SodClass *cls, va_list ap)
+{
+ void *p;
+
+ if ((p = malloc(cls->cls.initsz)) == 0) return (0);
+ return (sod_initv(cls, p, ap));
+}
+
+/* --- @sod_destroy@ --- *
+ *
+ * Arguments: @void *p@ = pointer to an instance to be torn down, or null
+ *
+ * Returns: Zero if the object was freed; nonzero if it refused for some
+ * reason.
+ *
+ * Use: Invokes the instance's `teardown' method to release any held
+ * resources, and then calls @free@ to release the instance's
+ * storage. See @sod_teardown@ for details, especially
+ * regarding the return value's meaning.
+ *
+ * If @p@ is null, then this function does nothing except
+ * returns zero.
+ *
+ * (This function is not available in freestanding environments
+ * lacking @malloc@ and @free@.)
+ */
+
+int sod_destroy(void *p)
+{
+ int rc;
+
+ if (!p) return (0);
+ rc = sod_teardown(p);
+ if (!rc) free(p);
+ return (rc);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
\h'2n'struct SodObject__vtmsgs_obj {
\h'4n'void (*init)(SodObject *\fIme\fB, ...);
\h'4n'void (*init__v)(SodObject *\fIme\fB, va_list);
+\h'4n'int (*teardown)(SodObject *\fIme\fB);
\h'2n'} obj;
};
\h'2n'struct SodClass__vtmsgs_obj {
\h'4n'void (*init)(SodClass *\fIme\fB, ...);
\h'4n'void (*init__v)(SodClass *\fIme\fB, va_list);
+\h'4n'int (*teardown)(SodClass *\fIme\fB);
\h'2n'} obj;
};
function more than once.
.PP
This default behaviour is to initialize the instance's slots
-using the defined slot initializers:
-each slot is initialized
+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.
.PP
-Slots are initialized in reverse-precedence order
-of their defining classes;
-i.e., slots defined by a less specific superclass are initialized
-earlier than slots defined by a more specific superclass.
-Slots defined by the same class are initialized in the order in which
-they appear in the class definition.
+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.
.PP
There are no standard keyword arguments;
methods on subclasses are free to
methods.
This ensures that slots have been initialized as necessary
before the method executes.
+.PP
+The
+.B teardown
+message is used to tear down an instance which is no longer required.
+.PP
+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.
+.PP
+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.
+.PP
+It is usual to provide complex teardown behaviour as
+.B before
+methods.
+Logic to decide whether to allow deallocation
+is usually implemented as
+.B around
+methods.
.
.SS The SodClass class
The
return (p);
}
+/* --- @sod_teardown@ --- *
+ *
+ * Arguments: @void *p@ = pointer to an instance to be torn down
+ *
+ * Returns: Zero if the object is torn down; nonzero if it refused for
+ * some reason.
+ *
+ * Use: Invokes the instance's `teardown' method to release any held
+ * resources.
+ *
+ * If this function returns nonzero, then the object is still
+ * active, and may still hold important resources. This is not
+ * intended to be a failure condition: failures in teardown are
+ * usually unrecoverable (or very hard to recover from) and
+ * should probably cause the program to abort. A refusal, on
+ * the other hand, means that the object is still live and
+ * shouldn't be deallocated, but that this is a normal situation
+ * and the caller shouldn't worry about it.
+ */
+
+int sod_teardown(void *p)
+{
+ SodObject *obj;
+
+ obj = SOD_CONVERT(SodObject, p);
+ return (SodObject_teardown(obj));
+}
+
/*----- That's all, folks -------------------------------------------------*/
/* We're going to want to make use of this ourselves. */
SOD__VARARGS_MACROS_PREAMBLE
+/* --- @SOD__IGNORE@ --- *
+ *
+ * Arguments: @var@ = some variable name
+ *
+ * Use: Suppress any warning that @var@ isn't used.
+ */
+
+#define SOD__IGNORE(var) ((void)(var))
+
/* --- @SOD__CAR@ --- *
*
* Arguments: @...@ = a nonempty list of arguments
#define SOD_INIT(cls, p, keys) ((cls *)sod_init(cls##__class, (p), keys))
+/* --- @SOD_MAKE@ --- *
+ *
+ * Arguments: @cls@ = a class type name
+ * @keys@ = a @KWARGS(...)@ keyword argument sequence
+ *
+ * Use: Allocates (using @malloc@) eand initializes storage to be an
+ * instance of @cls@. Returns a null pointer if allocation
+ * fails. Use @sod_destroy@ to release the instance.
+ */
+
+#define SOD_MAKE(cls, keys) ((cls *)sod_make(cls##__class, keys))
+
/* --- @SOD_DECL@ --- *
*
* Arguments: @cls@ = a class type name
extern KWCALL void *sod_init(const SodClass */*cls*/, void */*p*/, ...);
extern void *sod_initv(const SodClass */*cls*/, void */*p*/, va_list /*ap*/);
+/* --- @sod_make@, @sod_makev@ --- *
+ *
+ * Arguments: @const SodClass *cls@ = class object for new instance
+ * @va_list ap, ...@ = initialization keyword arguments
+ *
+ * Returns: Pointer to the newly-allocated initialized instance, or null.
+ *
+ * Use: Allocates storage for a new instance, initializes it, and
+ * returns a pointer to it. If allocation fails, a null pointer
+ * is returned instead.
+ *
+ * This function will allocate the storage using @malloc@, and
+ * then initialize it as for @sod_init@.
+ *
+ * It's usually convenient to use the macro @SOD_MAKE@ rather
+ * than calling @sod_make@ directly.
+ *
+ * (This function is not available in freestanding environments
+ * lacking @malloc@ and @free@.)
+ */
+
+extern KWCALL void *sod_make(const SodClass */*cls*/, ...);
+extern void *sod_makev(const SodClass */*cls*/, va_list /*ap*/);
+
+/* --- @sod_teardown@ --- *
+ *
+ * Arguments: @void *p@ = pointer to an instance to be torn down
+ *
+ * Returns: Zero if the object is torn down; nonzero if it refused for
+ * some reason.
+ *
+ * Use: Invokes the instance's `teardown' method to release any held
+ * resources.
+ *
+ * If this function returns nonzero, then the object is still
+ * active, and may still hold important resources. This is not
+ * intended to be a failure condition: failures in teardown are
+ * usually unrecoverable (or very hard to recover from) and
+ * should probably cause the program to abort. A refusal, on
+ * the other hand, means that the object is still live and
+ * shouldn't be deallocated, but that this is a normal situation
+ * and the caller shouldn't worry about it.
+ */
+
+extern int sod_teardown(void */*p*/);
+
+/* --- @sod_destroy@ --- *
+ *
+ * Arguments: @void *p@ = pointer to an instance to be torn down, or null
+ *
+ * Returns: Zero if the object was freed; nonzero if it refused for some
+ * reason.
+ *
+ * Use: Invokes the instance's `teardown' method to release any held
+ * resources, and then calls @free@ to release the instance's
+ * storage. See @sod_teardown@ for details, especially
+ * regarding the return value's meaning.
+ *
+ * If @p@ is null, then this function does nothing except
+ * returns zero.
+ *
+ * (This function is not available in freestanding environments
+ * lacking @malloc@ and @free@.)
+ */
+
+extern int sod_destroy(void */*p*/);
+
/*----- That's all, folks -------------------------------------------------*/
#ifdef __cplusplus
:test (lambda (class item)
(and (typep item 'islots)
(eq (islots-class item) class)))))
+ (frags (sod-class-initfrags super))
(this-class-focussed-p nil)
(isl (format nil "me->~A" (sod-class-nickname super))))
slot-default)))
(emit-inst codegen initinst)))))
+ ;; Emit the class's initialization fragments.
+ (when frags
+ (let ((used-me-p this-class-focussed-p))
+ (focus-this-class)
+ (unless used-me-p
+ (deliver-call codegen :void "SOD__IGNORE" "me")))
+ (dolist (frag frags)
+ (codegen-push codegen)
+ (emit-inst codegen frag)
+ (emit-inst codegen (codegen-pop-block codegen))))
+
;; If we opened a block to initialize this class then close it
;; again.
(when this-class-focussed-p
(deliver-call codegen :void func-name "sod__obj")))
+;; Teardown.
+
+(defclass teardown-message (lifecycle-message)
+ ())
+
+(defclass teardown-effective-method (lifecycle-effective-method)
+ ())
+
+(defmethod sod-message-effective-method-class ((message teardown-message))
+ 'teardown-effective-method)
+
+(defmethod lifecycle-method-kernel
+ ((method teardown-effective-method) codegen target)
+ (let* ((class (effective-method-class method))
+ (obj-tag (ilayout-struct-tag class))
+ (func-type (c-type (fun void ("sod__obj" (* (struct obj-tag))))))
+ (func-name (format nil "~A__teardown" class)))
+ (codegen-push codegen)
+ (dolist (super (sod-class-precedence-list class))
+ (let ((frags (sod-class-tearfrags super)))
+ (when frags
+ (emit-banner codegen "Teardown for class `~A'." super)
+ (codegen-push codegen)
+ (declare-me codegen super)
+ (deliver-call codegen :void "SOD__IGNORE" "me")
+ (dolist (frag frags)
+ (codegen-push codegen)
+ (emit-inst codegen frag)
+ (emit-inst codegen (codegen-pop-block codegen)))
+ (emit-inst codegen (codegen-pop-block codegen)))))
+ (codegen-pop-function codegen func-name func-type
+ "Instance teardown function ~:_~
+ for class `~A'."
+ class)
+ (deliver-call codegen :void
+ (format nil "~A__teardown" class) "sod__obj")
+ (deliver-expr codegen target 0)))
+
;;;--------------------------------------------------------------------------
;;; Bootstrapping the class graph.
(c-type (fun void :keys))
(make-property-set
:message-class 'initialization-message))
+ (make-sod-message sod-object "teardown" (c-type (fun int))
+ (make-property-set :message-class 'teardown-message))
;; Sort out the recursion.
(setf (slot-value sod-class 'chain-link) sod-object)
(declare (ignore slot-names pset))
nil)
+;;;--------------------------------------------------------------------------
+;;; Initialization and teardown fragments.
+
+(defmethod make-sod-class-initfrag
+ ((class sod-class) frag pset &optional location)
+ (declare (ignore pset location))
+ (with-slots (initfrags) class
+ (setf initfrags (append initfrags (list frag)))))
+
+(defmethod make-sod-class-tearfrag
+ ((class sod-class) frag pset &optional location)
+ (declare (ignore pset location))
+ (with-slots (tearfrags) class
+ (setf tearfrags (append tearfrags (list frag)))))
+
;;;--------------------------------------------------------------------------
;;; Messages.
You are not expected to call this generic function directly; it's more
useful as a place to hang methods for custom initializer classes."))
+(export 'make-sod-class-initfrag)
+(defgeneric make-sod-class-initfrag (class frag pset &optional location)
+ (:documentation
+ "Attach an initialization fragment FRAG to the CLASS.
+
+ Currently, initialization fragments are just dumb objects held in a
+ list."))
+
+(export 'make-sod-class-tearfrag)
+(defgeneric make-sod-class-tearfrag (class frag pset &optional location)
+ (:documentation
+ "Attach a teardown fragment FRAG to the CLASS.
+
+ Currently, teardown fragments are just dumb objects held in a
+ list."))
+
;;;--------------------------------------------------------------------------
;;; Messages and methods.
sod-class-direct-superclasses sod-class-precedence-list
sod-class-chain-link sod-class-chain-head
sod-class-chain sod-class-chains
- sod-class-slots
+ sod-class-slots sod-class-initfrags sod-class-tearfrags
sod-class-instance-initializers sod-class-class-initializers
sod-class-messages sod-class-methods
sod-class-state
:accessor sod-class-instance-initializers)
(class-initializers :initarg :class-initializers :initform nil
:type list :accessor sod-class-class-initializers)
+ (initfrags :initarg :initfrags :initform nil
+ :type list :accessor sod-class-initfrags)
+ (tearfrags :initarg :tearfrags :initform nil
+ :type list :accessor sod-class-tearfrags)
(messages :initarg :messages :initform nil
:type list :accessor sod-class-messages)
(methods :initarg :methods :initform nil
(export 'class-item)
+(define-pluggable-parser class-item initfrags (scanner class pset)
+ ;; raw-class-item ::= frag-keyword `{' c-fragment `}'
+ ;; frag-keyword ::= `init' | `teardown'
+ (with-parser-context (token-scanner-context :scanner scanner)
+ (parse (seq ((make (or (seq ("init") #'make-sod-class-initfrag)
+ (seq ("teardown") #'make-sod-class-tearfrag)))
+ (frag (parse-delimited-fragment scanner #\{ #\})))
+ (funcall make class frag pset scanner)))))
+
(defun parse-class-body (scanner pset name supers)
;; class-body ::= `{' class-item* `}'
;;
;; | method-item
;; | slot-item
;; | initializer-item
+ ;; | initfrag-item
;;
;; Most of the above begin with declspecs and a declarator
;; (which might be dotted). So we parse that here and
.|
.I initializer-item
.|
+.I fragment-item
+.|
.I message-item
.|
.I method-item
::=
.I c-fragment
.br
+.I fragment-item
+::=
+.I fragment-kind
+.B {
+.I c-fragment
+.B }
+.br
+.I fragment-kind
+::=
+.B init
+|
+.B teardown
+.br
.I message-item
::=
.<