代码之家  ›  专栏  ›  技术社区  ›  Grumdrig

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

  •  13
  • Grumdrig  · 技术社区  · 15 年前

    我想D static if 是一个有趣的语言特性。这就提示了我的问题:是否还有其他编译语言的例子,在这些例子中,编译器对代码有很强的概念,并且有访问它们的语言工具?

    例如,此代码提供类似于 repr 从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);
      }
    }
    

    我认为这很酷,因为它允许一种不同的、更通用的方法来处理重载的工作,这是一种由内到外的方法,与类似的特性相比,它可以使代码更加动态。例如,编译器知道我的类有多少个字段,但我的代码无法在大多数语言的编译时访问这些信息。

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

    3 回复  |  直到 12 年前
        1
  •  10
  •   Baxissimo    15 年前

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

    在C++中有 boost MPL 图书馆有一个 kind of static if 可用于在两种类型之间进行选择。您可以在run()成员的两个类型中放入一些代码,得到类似的东西,但语法非常繁琐。

    例如,使用BoostMPL,您可以执行如下操作:

    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 */
    }
    
        2
  •  2
  •   seh Alexei    15 年前

    对于“语言对代码的认识”,我所看到的最好的就是Lisp及其宏工具——特别是公共Lisp。但是这里的交易是,在大多数情况下,对象的类型在编译时或宏扩展时都是未知的。对于文本,类型是已知的,因此您可以找到攻击性宏的示例,这些宏测试对象是否是文本,如果是,则以一种方式处理对象(可能基于其类型),否则为运行时类型检查准备检测到的变量。

    以下是我改编自 克利布 图书馆 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。

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

        3
  •  2
  •   Community CDub    8 年前

    static_if 已经提出了下一个版本的C++(C++1Y)。它最初是为C++ 11提出的,但显然是延迟的。

    见提案 here . 有趣的是,其中一位作者是D.

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