4

显然,外部可见的 API 是通过导出符号来发布的。但是......如果我有多个包(比如 A、B 和 C)并且 A 的导出符号并不都是外部 API 的一部分——B 和 C 需要其中的一些?(类似地,B 为 A 和 C 导出一些符号,为外部 API 导出一些符号;C 是“顶级”包,它所有导出的符号都是公共 API 的一部分;我想保持模块化并允许 A 隐藏其内部来自 B 和 C,所以我避免使用 '::')。

我现在的解决方案是从 C 重新导出所有应该公开的内容,并记录公共 API 仅包含 C 的导出符号,人们应该远离 A 和 B 的公共符号,因为错误和代码被破坏内部接口改变的未来。

有没有更好的办法?

更新:这是我对 Xach 回答的理解的实现:

首先,让我完成我的示例。我想导出符号symbol-a-1symbol-a-2a,符号symbol-b-1symbol-b-2b和符号api-symbol-1以及api-symbol-2c。只有从中导出的符号c是公共 API 的一部分。

首先, 的定义a

(defpackage #:a
  (:use #:cl))

请注意,没有任何导出的符号:-)

辅助宏(使用 Alexandria):

(defmacro privately-export (package-name &body symbols)
  `(eval-when (:compile-toplevel :load-toplevel :execute)
     (defun ,(alexandria:format-symbol *package*
                                       "IMPORT-FROM-~a"
                                       (symbol-name package-name)) ()
       (list :import-from
             ,package-name
             ,@(mapcar (lambda (to-intern)
                         `',(intern (symbol-name to-intern) package-name))
                       symbols)))))

使用宏“私下导出”:-):

(privately-export :a :symbol-a-1 :symbol-a-2)

现在定义b

(defpackage #:b
  (:use #:cl)
  #.(import-from-a))

... b'出口':

(privately-export :b :symbol-b-1 :symbol-b-2)

...c的定义:

(defpackage #:c
  (:use #:cl)
  #.(import-from-a)
  #.(import-from-b)
  (:export :api-symbol-1 :api-symbol-2)

这种方法的问题:

  • a不能使用符号 from b(在两者都被定义后没有importing 符号 from bfrom a);
  • 该语法package:symbol基本上不适用于“私人”导出的符号(它只是symbolpackage::symbol)。
4

3 回答 3

2

如果 A 和 B 主要用于 C 的实现,您可以让 C 的 defpackage 形式通过选择性地使用来驱动事物:import-from,因为您可以导入非外部的事物。然后您可以有选择地从那里重新导出。

于 2012-10-18T12:46:37.967 回答
1

您可以添加第三个包 D,它导出所有公共 API 符号,并将 A、B 和 C 包视为私有包。然后,您可以使用限定名称限定 API 包中的所有函数和变量定义,例如

(defun D:blah () ...)

便于直观地发现公共入口点的定义。

于 2012-10-18T09:06:43.133 回答
1

Probably, the easiest way is proposed by Hans.

You may also wan to take a look at Tim Bradshaw's Conduit packages

于 2012-10-18T12:18:04.223 回答