Существуют ли другие языки, кроме D, со статическим if?

Я думаю, что 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);
  }
}

Я думаю, что это круто, потому что он позволяет использовать другой и более общий подход к тому, что делает перегрузка, что является своего рода способом сделать код более динамичным по сравнению с такими функциями. Например, компилятор знает, сколько полей имеет мой класс, но у моего кода нет возможности получить доступ к этой информации во время компиляции на большинстве языков.

CAVEAT: В этом последнем абзаце были мнения, но я просто хочу дать некоторую мотивацию и разъяснения по моему вопросу, а не вызвать разногласия. Я просто хочу узнать, есть ли в других компилируемых языках такие возможности.


person Grumdrig    schedule 11.11.2009    source источник
comment
Многие компиляторы C и C ++ удаляют неиспользуемый код, если выражение в if может быть определено во время компиляции. Не уверен, что это полностью отвечает на ваш вопрос.   -  person Mark Ransom    schedule 11.11.2009
comment
Нет - приведенный выше пример не будет компилироваться, потому что код в некоторых блоках static if недействителен. Например. если бы T был типом int, тогда значение .__ repr __ () было бы ошибкой компиляции.   -  person Grumdrig    schedule 12.11.2009
comment
Вам нужно сделать это статическим if (is (typeof (value .__ repr__))) для компиляции вышеуказанного. Или статический if (traits (compiles, value .__ repr)).   -  person Baxissimo    schedule 12.11.2009


Ответы (3)


Любой язык с реальными макросами имеет форму статического if. Например, Lisp и Nemerle позволяют создавать код, расширяемый макросом для использования таких программных конструкций, как 'if' и для -петли. По сути, это решения, принимаемые во время компиляции, и они позволяют вам делать что-то похожее на статическое 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 */
}
person Baxissimo    schedule 11.11.2009
comment
Красивый. Nemerle для меня новинка и очень интересна. С нетерпением жду его изучения. Макросы Lisp, о которых я должен был подумать. - person Grumdrig; 12.11.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.

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

person seh    schedule 21.11.2009

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

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

Кроме того, можно подделать статический if в текущем C ++ с помощью хаков компилятора.

person Mechanical snail    schedule 22.08.2012