【问题标题】:cl-who passing stream in funcallscl-who 在函数调用中传递流
【发布时间】:2018-12-20 20:57:15
【问题描述】:

我正在使用 cl-who(通过 hunchentoot),到目前为止完全成功,但是有一件事我无法弄清楚,而且我的解决方法很丑陋,所以我希望有一个简单的解决方法。我的 hunchentoot 简单处理程序调用看起来像这样的函数:

(defun foo ()
 (with-html-output-to-string
   (*standard-output* nil :prologue t)
   (:html
    (:body (htm :br :hr "foo" :hr ...etc...))))

一切都很好。

但是,当我想从 foo 中调用辅助函数以执行...任何我想做的子工作时,我无法弄清楚如何使 CL-WHO 的 HTM 上下文通过调用。例如,这很好用:

(defun foo ()
  (with-html-output-to-string
   (*standard-output* nil :prologue t)
   (:html
    (:body (htm :br :hr "foo" :hr (bar)))))

(defun bar ()
   (format t "This will show up in the html stream"))

但这不起作用:

(defun bar ()
  (with-html-output-to-string
   (*standard-output* nil :prologue t)
   (htm "This will NOT show up in the html stream")))

(我已经尝试了各种操作,但无济于事。)

我确定我做错了一些简单的事情;必须在任何 subfn 中恢复为 t 格式是非常丑陋的,尤其是。 bcs 我不能使用 cl-who 方便的 html 宏。

【问题讨论】:

  • 为什么不只是(defun bar() "This will NOT show up in the html stream")?你没有做任何需要htm的事情。
  • 摆脱:prologue t。这应该只在顶级 HTML 文档中使用,而不是在其他标签内。

标签: lisp common-lisp hunchentoot cl-who


【解决方案1】:

CL-WHO 基于生成 write 语句的宏,所有以关键字开头的表单以及参数值都会自动打印。其他形式仅被评估(例如,用于副作用),并且不会自动打印。这就是 CL-WHO 引入 strfmteschtm 宏的原因,它们会强制打印它们的参数(以不同的方式)。

您的代码:

(defun bar ()
  (with-html-output-to-string
   (*standard-output* nil :prologue t)
   (htm "This will NOT show up in the html stream")))

返回值是一个字符串,因为您使用的是with-html-output-to-string*standard-output* 临时绑定到一个流,与外部bar 不同,只是为了构建一个返回给调用者的字符串,这里是foo。 不打印字符串(仅打印内容位置为常量字符串的表单)。

您可以使用str 强制写入返回的生成 HTML,但恕我直言,最好的选择是直接写入与调用者相同的输出流,而不是构建中间字符串。

直接写入流

基本上,使用with-html-output:

  • 我不喜欢使用*standard-output*,而是只用于html的流。这可以防止其他库将任何不需要的内容写入 HTML 页面。您也可以将流向下传递给每个辅助函数,但在这些情况下最好使用特殊变量。

  • 让我们使用简单的宏来简化语法并强制执行我们自己的约定。

以下定义了一个包并配置 CL-WHO 以发出 HTML5 代码。这必须在宏扩展之前完成,因为在宏扩展期间会使用正在设置的特殊变量:

(defpackage :web (:use :cl :cl-who))
(in-package :web)

;; Evaluate before CL-WHO macro are expanded
(eval-when (:compile-toplevel :load-toplevel :execute)
  (setf (html-mode) :html5))

定义一个我们可以控制的流,默认绑定到 *standard-output* 我们打开流时绑定的任何东西(而不是在我们定义变量时):

(defvar *html-output* (make-synonym-stream '*standard-output*)
  "Use a dedicated stream variable for html")

另外,定义一个通用的缩进级别:

(defvar *indent* 2
  "Default indentation")

有两个宏,一个用于嵌入在辅助函数中的sn-ps,写入我们的流中,一个用于顶级html页面,返回一个字符串。

(defmacro with-html (&body body)
  "Establish an HTML context (intended for auxiliary functions)."
  `(with-html-output (*html-output* nil :indent *indent*)
     ,@body))

(defmacro with-html-page (&body body)
  "Return an HTML string (intended for top-level pages)."
  `(with-html-output-to-string (*html-output* nil :prologue t :indent *indent*)
     ,@body))

示例用法:

(defun my-section (title)
  (with-html
    (:h1 (esc title))
    (:p "lorem ipsum")))

(defun my-page ()
  (with-html-page
    (my-section "title")))

调用(my-page) 返回:

"<!DOCTYPE html>

<h1>title
</h1>
<p>lorem ipsum
</p>"

另见鲜为人知的https://github.com/ruricolist/spinneret

【讨论】:

  • 啊。好的,所以我并不像我想的那样感到困惑;您确实必须在 auxfn 中以不同的方式进行操作。谢谢!
【解决方案2】:

我不清楚你想做什么。如果你想把一个网页“分块”组成,通过函数来​​生成页面的各个部分,你可以在调用其中一个函数时使用str,例如:

(defun f1 ()
  (with-html-output-to-string (*output-string*)
    (:p "some text")))

(defun f2 ()
  (with-html-output-to-string (*output-string*)
       (:body (:p "some other text") (str (f1)))))

(f2)
"<body><p>some other text</p><p>some text</p></body>"

来自manual

类似于(str form) 的表单将替换为(let ((result form)) (when result (princ result s)))

如果不使用str,则结果不包含在html输出中:

(defun f3 ()
  (with-html-output-to-string (*output-string*)
       (:body (:p "some other text") (f1))))

(f3)
"<body><p>some other text</p></body>"

【讨论】:

  • 但它也说将逐字打印一个字符串,所以htm应该是一样的。
  • @Barmar,是的,但是对于返回字符串的表单,该字符串不会显示在流中。我以为常量字符串只是一个例子。
猜你喜欢
  • 1970-01-01
  • 2011-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-07
  • 1970-01-01
  • 1970-01-01
  • 2018-06-03
相关资源
最近更新 更多