除了 D 语言之外还有其他带有静态 if 的语言吗?

发布于 2024-08-11 06:05:48 字数 636 浏览 13 评论 0原文

我认为 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);
  }
}

我认为这很酷,因为它允许使用一种不同的、更通用的方法来实现重载的功能,这是一种由内而外的方法与此类功能相比,代码更加动态。例如,编译器知道我的类有多少个字段,但在大多数语言中,我的代码无法在编译时访问该信息。

注意:最后一段有意见,但我只是想为我的问题提供一些动机和澄清,而不是引起争议。我只是想知道其他编译语言是否有这样的功能。

I think D's static if is an interesting language feature. That prompts my question: Are there are other examples of compiled languages in which the compiler has a strong notion of the code and there are languages facilities to access them?

For example, this code provides something similar to repr from Python:

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);
  }
}

I think this is cool is because it allows a different and more general approach to what overloading does, which is kind of an inside out way to make code more dynamic, compared to features like this. For example, the compiler knows how many fields my class has, but there's no way for my code to access that information at compile time in most languages.

CAVEAT: That last paragraph had opinions in it, but I just mean to provide some motivation and clarification for my question, not elicit controversy. I just want to find out if any other compiled languages have such features.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

無處可尋 2024-08-18 06:05:48

任何具有真正宏的语言都有静态 if 的形式。例如,Lisp 和 Nemerle 允许您使用“if”和 for 等编程结构构建宏扩展为的代码-循环。这些本质上是编译时决策,让您可以执行类似于 static if 的操作。就 Nemerle 而言,宏基本上是编译器的插件,在编译时执行。

在 C++ 中,有 boost MPL 库,它具有某种静态,如果可用于在两种类型之间进行选择。您可以将一些代码放入 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 */
}

Any language with real macros has a form of static if. For instance Lisp and Nemerle let you construct the code that a macro expands to using programming constructs like 'if' and for-loops. Those are essentially compile-time decisions and let you do something similar to static if. In the case of Nemerle macros are basically plug-ins to the compiler that are executed at compile-time.

In C++ there's boost MPL library which has a kind of static if that can be used to choose between two types. You could put some code inside the two types in a run() member and get something kinda similar, but with very cumbersome syntax.

For example with Boost MPL you could do something like this:

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();

In D that'd be:

static if(is(T == float)) {
     /* float code */
}
else {
     /* int code */
}
逆夏时光 2024-08-18 06:05:48

对于“语言对代码的感知”,我见过没有比 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 的 em>,在此处绑定到变量 #: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。

在这个例子中,这似乎工作量太大,但正如你的问题所表明的那样,能够做这样的事情是一种很好的能力。

For a "language's awareness of code", there's no better I've seen than Lisp and its macro facility -- specifically, Common Lisp. But the trade there is that most of the time an object's type is not known at compile time or macroexpansion time. For literals, the types are known, so you can find examples of aggressive macros that test to see if an object is a literal and, if so, treat it one way -- maybe based on its type -- and otherwise prepare the detected variable for run-time type inspection.

Here's an example I adapted from the CLLIB library (part of the CLOCC library) several years ago. The goal is to provide functions that will chop a prefix string off of some other string with a matching prefix. The prefix may be known at macroexpansion time or it may not. If it is, we can an optimization: compute the prefix's length first and embed it as a literal, so that it's not recomputed on each call to the generated function. The macro is daunting at first, but the actual generated code is small.

(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))

See the form

(if (stringp prefix)

in the middle? That's inspecting the first argument at macroexpansion time and, depending on whether the argument is a literal or symbol, its type may or may not be known. If the type is a symbol, we assume that we should wait until run time to reconsider it as a variable pointing to some other value.

Here's the expansion for the form (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))))

Note that the variable #:PREFIX-LENGTH-5343 is bound to the computed length of FOO, bound here to variable #:PREFIX-5342.

Now look at the expansion for the form (after-prefix "foo" bar), where the prefix is now a string literal:

(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)))

Now there's no computing the length of "foo"; it's inlined as 3.

It may seem like too much work in this example, but being able to do such things is a good power to have, as your question opines.

子栖 2024-08-18 06:05:48

static_if 已被提议用于下一版本的 C++ (C++1y)。它最初是针对 C++11 提出的,但显然被推迟了。

请参阅此处的提案。有趣的是,作者之一是 D 的创建者 Walter Bright。

此外,可以伪造静态-if 在当前的 C++ 中使用编译器 hack

static_if has been proposed for the next version of C++ (C++1y). It was originally proposed for C++11 but was apparently delayed.

See the proposal here. Interestingly, one of the authors is Walter Bright, the creator of D.

Also, it's possible to fake static-if in current C++ using compiler hacks.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文