banner function
declare-output-type function
guard-name function
+ one-off-output function
output-module function
output-type-pathname function
\begin{describe}{fun}{guard-name @<filename> @> @<string>}
\end{describe}
+\begin{describe}{fun}
+ {one-off-output @<token> @<sequencer> @<item-name> @<function>}
+\end{describe}
+
%%%--------------------------------------------------------------------------
\section{Class output} \label{output.class}
/*----- Preliminary utilities ---------------------------------------------*/
+/* --- @SOD__HAVE_VARARGS_MACROS@ --- *
+ *
+ * Use: Defined if the compiler supports C99-style variadic macros.
+ *
+ * This is more complicated than just checking the value of
+ * @__STDC_VERSION__@ because GCC has traditionally claimed C89
+ * by default, but provides the functionality anyway unless it's
+ * been explicitly turned off.
+ */
+
+#if __STDC_VERSION__ >= 199901
+ /* The feature exists. All is well with the world. */
+
+# define SOD__HAVE_VARARGS_MACROS
+
+#elif __GNUC__ >= 3
+ /* We're using GCC, which is trying to deny it but we don't believe it.
+ * Unfortunately there's a fly in the ointment: if `-pedantic' -- or,
+ * worse, `-pedantic-errors' -- is set, then GCC will warn about these
+ * macros being defined, and there isn't a way to detect pedantry from the
+ * preprocessor.
+ *
+ * We must deploy bodges. There doesn't seem to be a good way to suppress
+ * particular warnings from the preprocessor: in particular, messing about
+ * with `pragma GCC diagnostic' doesn't help. So we're left with this
+ * hack: just declare all Sod-generated header files which try to do
+ * varargs macro things to be `system headers', which means that GCC's
+ * preprocessor will let them get away with all manner of nefarious stuff.
+ */
+
+# define SOD__HAVE_VARARGS_MACROS
+# define SOD__VARARGS_MACROS_PREAMBLE _Pragma("GCC system_header")
+
+#endif
+
+/* Make sure this gratuitous hack is understood, at least vacuously. */
+#ifndef SOD__VARARGS_MACROS_PREAMBLE
+# define SOD__VARARGS_MACROS_PREAMBLE
+#endif
+
+/* We're going to want to make use of this ourselves. */
+SOD__VARARGS_MACROS_PREAMBLE
+
/* --- @SOD__CAR@ --- *
*
* Arguments: @...@ = a nonempty list of arguments
* Returns: The first argument only.
*/
-#if __STDC_VERSION__ >= 199901
+#ifdef SOD__HAVE_VARARGS_MACROS
# define SOD__CAR(...) SOD__CARx(__VA_LIST__, _)
# define SOD__CARx(a, ...) a
#endif
;; We need each message's method entry type for this, so we need to dig it
;; out of the vtmsgs structure. Indeed, the vtmsgs for this class contains
;; entries for precisely the messages we want to make macros for.
+ (when (some #'varargs-message-p (sod-class-messages class))
+ (one-off-output 'varargs-macros sequencer :early-decls
+ (lambda (stream)
+ (format stream
+ "~%SOD__VARARGS_MACROS_PREAMBLE~%"))))
(when (sod-class-messages class)
(sequence-output (stream sequencer)
((class :message-macros)
(push name in-names)
(push name out-names)))))
(when varargsp
- (format stream "#if __STDC_VERSION__ >= 199901~%"))
+ (format stream "#ifdef SOD__HAVE_VARARGS_MACROS~%"))
(format stream "#define ~A(~{~A~^, ~}) ~
~A->_vt->~A.~A(~{~A~^, ~})~%"
(message-macro-name class entry)
(merge-pathnames (make-pathname :type type :case :common)
(module-name module)))
+(defvar *done-one-off-output* nil
+ "A list of tokens for things which should appear at most once in output.")
+
+(export 'one-off-output)
+(defun one-off-output (token sequencer item-name function)
+ "Arrange to output a thing at most once.
+
+ If there has been no previous call to `one-off-output' with the given
+ TOKEN during this output run, then arrange to call FUNCTION when the item
+ called ITEM-NAME is traversed. Otherwise do nothing."
+ (unless (member token *done-one-off-output*)
+ (push token *done-one-off-output*)
+ (add-sequencer-item-function sequencer item-name function)))
+
;;;--------------------------------------------------------------------------
;;; Main output interface.
"Write the MODULE to STREAM, giving the output machinery the REASON.
This is the top-level interface for producing output."
- (let ((sequencer (make-instance 'sequencer))
+ (let ((*done-one-off-output* nil)
+ (sequencer (make-instance 'sequencer))
(stream (if (typep stream 'position-aware-output-stream)
stream
(make-instance 'position-aware-output-stream
(:prologue
(:guard :start)
(:typedefs :start) :typedefs (:typedefs :end)
- (:includes :start) :includes (:includes :end)
+ (:includes :start) :includes :early-decls (:includes :end)
(:early-user :start) :early-user (:early-user :end)
(:classes :start) (:classes :end)
(:user :start) :user (:user :end)