emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Why shouldn't we have a #if .... #else .... #endif construct in Emac


From: Alan Mackenzie
Subject: Re: Why shouldn't we have a #if .... #else .... #endif construct in Emacs Lisp?
Date: Sat, 2 Sep 2023 15:06:46 +0000

Hello, Stefan.

On Wed, Aug 30, 2023 at 20:17:48 +0200, Stefan Kangas wrote:
> Alan Mackenzie <acm@muc.de> writes:

> > I looked at APEL, and its code was near identical to what I (with help
> > from PK) wrote.  I'd have nothing against the name static-if instead.
> > As long as this doesn't raise copyright assignment problems, assuming
> > the authors of APEL haven't assigned copyright to the FSF.

> I think it's fine as far as copyright goes.  You came up with your
> version independently, before even looking at their code.  We even
> have a public record of that right here on this list.  IANAL, but the
> name `static-if' in itself is also not copyrightable, AFAIU.

OK, static-if it is.  I think the code and documentation are ready to be
committed.

For anybody who wants one last check, here's the intended patch:



diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi
index 3aee9dd80e4..41e5cb148e5 100644
--- a/doc/lispref/control.texi
+++ b/doc/lispref/control.texi
@@ -36,13 +36,14 @@ Control Structures
 structure constructs (@pxref{Macros}).
 
 @menu
-* Sequencing::             Evaluation in textual order.
-* Conditionals::           @code{if}, @code{cond}, @code{when}, @code{unless}.
-* Combining Conditions::   @code{and}, @code{or}, @code{not}, and friends.
+* Sequencing::                  Evaluation in textual order.
+* Conditionals::                @code{if}, @code{cond}, @code{when}, 
@code{unless}.
+* Combining Conditions::        @code{and}, @code{or}, @code{not}, and friends.
 * Pattern-Matching Conditional::  How to use @code{pcase} and friends.
-* Iteration::              @code{while} loops.
-* Generators::             Generic sequences and coroutines.
-* Nonlocal Exits::         Jumping out of a sequence.
+* Iteration::                   @code{while} loops.
+* Generators::                  Generic sequences and coroutines.
+* Nonlocal Exits::              Jumping out of a sequence.
+* Conditional Compilation::     A facility like C's #if.
 @end menu
 
 @node Sequencing
@@ -2467,3 +2468,48 @@ Cleanups
 @code{ftp-setup-buffer} returns but before the variable @code{process} is
 set, the process will not be killed.  There is no easy way to fix this bug,
 but at least it is very unlikely.
+
+@node Conditional Compilation
+@section Conditional Compilation
+
+  There will be times when you want certain code to be compiled only
+when a certain condition holds.  This is particularly the case when
+maintaining Emacs packages; to keep the package compatible with older
+versions of Emacs you may need to use a function or variable which has
+become obsolete in the current version of Emacs.
+
+  You could just use a conditional form to select the old or new form
+at run time, but this tends to output annoying warning messages about
+the obsolete function/variable.  For such situations, the macro
+@code{static-if} comes in handy.  It is inspired by the conditional
+compilation directives like @code{#if} in C like languages, and is
+patterned after the special form @code{if} (@pxref{Conditionals}).
+
+  To use this facility for an older version of Emacs, copy the source
+for @code{static-if} from the Emacs source file @file{lisp/subr.el}
+into your package.
+
+@defmac static-if condition then-form else-forms...
+Test @var{condition} at macro-expansion time.  If its value is
+non-@code{nil}, expand the macro to @var{then-form}, otherwise expand
+it to @var{else-forms} enclosed in a @code{progn}.  @var{else-forms}
+may be empty.
+
+Here is an example of its use from CC Mode, which prevents a
+@code{defadvice} form being compiled in newer versions of Emacs:
+@example
+@group
+(static-if (boundp 'comment-line-break-function)
+    (progn)
+  (defvar c-inside-line-break-advice nil)
+  (defadvice indent-new-comment-line (around c-line-break-advice
+                                             activate preactivate)
+    "Call `c-indent-new-comment-line' if in CC Mode."
+    (if (or c-inside-line-break-advice
+            (not c-buffer-is-cc-mode))
+        ad-do-it
+      (let ((c-inside-line-break-advice t))
+        (c-indent-new-comment-line (ad-get-arg 0))))))
+@end group
+@end example
+@end defmac
diff --git a/etc/NEWS b/etc/NEWS
index 5c11b6b9ac7..4e7e185c8bc 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -855,6 +855,10 @@ Use 'define-minor-mode' and 'define-globalized-minor-mode' 
instead.
 See the "(elisp) Porting Old Advice" node for help converting them
 to use 'advice-add' or 'define-advice' instead.
 
++++
+** There is now conditional compilation, based on the C language's #if.
+To use this, see the new macro 'static-if'.
+
 +++
 ** Desktop notifications are now supported on the Haiku operating system.
 The new function 'haiku-notifications-notify' provides a subset of the
diff --git a/lisp/subr.el b/lisp/subr.el
index 47fcbc2f317..1a7d8aadd46 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -277,6 +277,23 @@ pop
          (macroexp-let2 macroexp-copyable-p x getter
            `(prog1 ,x ,(funcall setter `(cdr ,x))))))))
 
+;; Note: `static-if' handles the version of `eval' without the &optional
+;; parameter LEXICAL so that it can be copied unchanged for use in older
+;; Emacsen.
+(defmacro static-if (condition then-form &rest else-forms)
+  "A conditional compilation macro analogous to C's #if.
+Evaluate CONDITION at macro-expansion time.  If it is non-nil,
+expand the macro to THEN-FORM.  Otherwise expand it to ELSE-FORMS
+enclosed in a `progn' form.  ELSE-FORMS may be empty."
+  (declare (indent 2)
+           (debug (sexp sexp &rest sexp)))
+  (if (condition-case err
+          (eval condition lexical-binding)
+        ((wrong-number-of-arguments void-variable) (eval condition))
+        ((debug error) (signal (car err) (cdr err))))
+      then-form
+    (cons 'progn else-forms)))
+
 (defmacro when (cond &rest body)
   "If COND yields non-nil, do BODY, else return nil.
 When COND yields non-nil, eval BODY forms sequentially and return


-- 
Alan Mackenzie (Nuremberg, Germany).



reply via email to

[Prev in Thread] Current Thread [Next in Thread]