我认为D static if
是一个有趣的语言特征。这提示了我的问题:是否还有编译语言的其他示例,其中编译器具有强大的代码概念,并且有语言工具可以访问它们?
例如,此代码提供类似于Python的repr
:
char[] repr(T)(T value) {
static if (is(typeof(value.__repr__))) { // class T provides a "repr()" method
return value.__repr__();
} else static if (is(T:string)) {
return `"` ~ value ~ `"`;
// ...other cases...
} else {
return toString(value);
}
}
我认为这很酷,因为它允许一种不同的,更通用的方法来实现重载,与这样的功能相比,这是一种让代码更具动态性的内部方式。例如,编译器知道我的类有多少字段,但是我的代码无法在编译时以大多数语言访问该信息。
CAVEAT:最后一段中有意见,但我只是想为我的问题提供一些动力和澄清,而不是引起争议。我只是想知道是否有其他编译语言有这样的功能。
答案 0 :(得分:10)
任何具有真实宏的语言都具有静态if的形式。例如,Lisp和Nemerle允许您使用编程结构(如'if'和for-loops)构建宏扩展的代码。这些本质上是编译时的决定,让你做类似于static的事情。在Nemerle的情况下,宏基本上是编译时在编译时执行的插件。
在C ++中有boost MPL库,其kind of static if可用于在两种类型之间进行选择。你可以将一些代码放在run()成员中的两种类型中,并获得类似的东西,但语法非常繁琐。
例如,使用Boost MPL,您可以执行以下操作:
struct float_impl {
static void run() { /* float case code */ }
}
struct int_impl {
static void run() { /* int case code */ }
}
typedef typename if_<
is_same<T, float>
, float_impl
, int_impl
>::type impl_t;
impl_t::run();
在D中,是:
static if(is(T == float)) {
/* float code */
}
else {
/* int code */
}
答案 1 :(得分:2)
对于“语言的代码意识”,我看到的不是Lisp及其宏设施 - 特别是Common Lisp。但是,在大多数情况下,交易是在编译时或宏扩展时不知道对象的类型。对于文字,类型是已知的,因此您可以找到攻击性宏的示例,以测试对象是否为文字,如果是,则以一种方式对待它 - 可能基于其类型 - 并以其他方式准备检测到的变量用于运行时类型检查。
这是几年前我从 CLLIB 库(CLOCC库的一部分)改编的一个例子。目标是提供一些函数,这些函数将使用匹配的前缀从一些其他字符串中删除前缀字符串。前缀可以在宏扩展时知道,也可以不知道。如果是,我们可以进行优化:首先计算前缀的长度并将其作为文字嵌入,这样在每次调用生成的函数时都不会重新计算它。该宏最初令人生畏,但实际生成的代码很小。
(defmacro after-prefix-core (comparison-op prefix string &optional length)
"Similar to cllib:string-beg-with-cs."
(flet ((chop (prefix prefix-length string string-length)
`(when (and (>= ,string-length ,prefix-length)
(,comparison-op ,prefix ,string :end2 ,prefix-length))
(subseq ,string ,prefix-length ,string-length))))
(let* ((gstring (gensym "STRING-"))
(gstring-length (gensym "STRING-LENGTH-")))
`(let* ((,gstring ,string)
(,gstring-length ,(or length `(length ,gstring))))
,(if (stringp prefix)
;; Constant -- length known at expansion time.
(let ((prefix-length (length prefix)))
(chop prefix prefix-length gstring gstring-length))
;; Other form -- length not known at expansion time.
(let ((gprefix (gensym "PREFIX-"))
(gprefix-length (gensym "PREFIX-LENGTH-")))
`(let* ((,gprefix ,prefix)
(,gprefix-length (length ,gprefix)))
,(chop gprefix gprefix-length gstring gstring-length))))))))
(defmacro after-prefix (prefix string &optional length)
"Similar to cllib:string-beg-with."
`(after-prefix-core string-equal ,prefix ,string ,length))
(defmacro after-prefix-cs (prefix string &optional length)
"Similar to cllib:string-beg-with-cs."
`(after-prefix-core string= ,prefix ,string ,length))
见表格
(if (stringp prefix)
在中间?那是在宏扩展时检查第一个参数,并且根据参数是文字还是符号,其类型可能是也可能不知道。如果类型是符号,我们假设我们应该等到运行时间重新考虑它作为指向其他值的变量。
以下是表单(after-prefix foo bar)
的扩展:
(LET* ((#:STRING-5340 BAR) (#:STRING-LENGTH-5341 (LENGTH #:STRING-5340)))
(LET* ((#:PREFIX-5342 FOO) (#:PREFIX-LENGTH-5343 (LENGTH #:PREFIX-5342)))
(WHEN
(AND (>= #:STRING-LENGTH-5341 #:PREFIX-LENGTH-5343)
(STRING-EQUAL #:PREFIX-5342 #:STRING-5340 :END2 #:PREFIX-LENGTH-5343))
(SUBSEQ #:STRING-5340 #:PREFIX-LENGTH-5343 #:STRING-LENGTH-5341))))
请注意,变量#:PREFIX-LENGTH-5343
绑定到FOO
的计算长度,此处绑定到变量#:PREFIX-5342
。
现在看一下(after-prefix "foo" bar)
形式的扩展,其中前缀现在是一个字符串文字:
(LET* ((#:STRING-5463 BAR) (#:STRING-LENGTH-5464 (LENGTH #:STRING-5463)))
(WHEN (AND (>= #:STRING-LENGTH-5464 3) (STRING-EQUAL "foo" #:STRING-5463 :END2 3))
(SUBSEQ #:STRING-5463 3 #:STRING-LENGTH-5464)))
现在没有计算“foo”的长度;它被列为3。
在这个例子中看起来似乎太多了,但是正如你的问题所说的那样,能够做这些事情是一种很好的力量。
答案 2 :(得分:2)
static_if
已被提议用于下一版本的C ++(C ++ 1y)。它最初是为C ++ 11提出的,但显然是推迟了。
查看提案here。有趣的是,其中一位作者是D的创作者Walter Bright。
另外,it's possible to fake static-if in current C++ using compiler hacks。