根据不同的条件,将不同的代码片段写入编译后的 (.elc) 文件

Writing different pieces of code to the compiled (.elc) file, based on different conditions

我想有条件地将不同的代码片段写入我的 elisp 文件 (.elc) 的编译版本,我可以使用函数定义来做到这一点:

(defalias 'my-test
  (eval-when-compile
    (if nil 
      (lambda ()
        (message "True"))
      (lambda ()
        (message "False")))))

通过上面的代码,我可以根据不同的条件为my-test分配不同的匿名函数,结果将写入编译后的.elc文件。但是我想写的是函数调用文件范围。像这样:

(eval-when-compile
  (if nil
      (add-to-list 'auto-mode-alist '("\.gitconfig\'" . A-mode))
    (add-to-list 'auto-mode-alist '("\.gitconfig\'" . B-mode))))

;; or using `when' to either write a call or do nothing:
(eval-when-compile
  (when (not (eq (user-uid) 0))           ; if uid != 0
    (dangerous-call)))

代码在编译时被求值,求值结果只会在编译过程中可用,之后什么都不会进入.elc,因为我没有将它赋值给任何东西,但是怎么能我实际上将 if 控件的 byte-compiled 结果(这是一个 calladd-to-list 函数)写到编译 .elc 文件?在这种情况下,我希望 (add-to-list 'auto-mode-alist '("\.gitconfig\'" . B-mode)) 写入 .elc 文件。

我认为这个问题的答案是宏:如果你编写一个扩展为你想要的代码的宏,那么只需将宏的调用放在顶层,那么宏的扩展将结束于编译后的文件。

所以:

(defmacro foo ()
  (if <compile-time-condition>
      `(setq ...)
      `(setq ...)))

(foo)

警告:您只能根据您在编译时知道的事情进行条件化:您在 UID 上调度的示例可能无法达到您的预期。

tfb 的答案通常是更好的选择,但对于一次性情况,您可能更愿意这样做

(if (eval-when-compile (my-test))
    (add-to-list 'auto-mode-alist '("\.gitconfig\'" . A-mode))
  (add-to-list 'auto-mode-alist '("\.gitconfig\'" . B-mode)))

甚至可以重写为

(add-to-list 'auto-mode-alist 
             `("\.gitconfig\'"
             . ,(if (eval-when-compile (my-test)) 'A-mode 'B-mode)))

这依赖于编译器优化根据常数将(if <constant> a b)变成ab