Есть ли другие языки, кроме D со статическим if? - PullRequest
13 голосов
/ 11 ноября 2009

Я думаю, что D's 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 ]

10 голосов
/ 12 ноября 2009

Любой язык с реальными макросами имеет статическую форму if. Например, Lisp и Nemerle позволяют вам создать код, который макрос расширяет до использования таких конструкций программирования, как 'if' и for-loop. Это, по сути, решения во время компиляции, которые позволяют вам делать что-то похожее на static if. В случае с Nemerle макросы - это в основном плагины для компилятора, которые выполняются во время компиляции.

В C ++ есть библиотека boost MPL , которая имеет статический тип 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 */
}
2 голосов
/ 23 августа 2012

static_if было предложено для следующей версии C ++ (C ++ 1y). Первоначально он был предложен для C ++ 11, но, видимо, был отложен.

См. Предложение здесь . Интересно, что одним из авторов является Вальтер Брайт, создатель D.

Также, возможно подделать static-if в текущем C ++, используя хаки компилятора .

2 голосов
/ 22 ноября 2009

Что касается "языковой осведомленности о коде", я не видел ничего лучше, чем 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.

В этом примере может показаться слишком большой работой, но способность делать такие вещи - хорошая сила, как показывает ваш вопрос.

...