Существуют ли другие языки помимо D со статическими, если?
Я думаю, что 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: Этот последний абзац содержал в нем свои мнения, но я просто хочу дать некоторые мотивы и разъяснения по моему вопросу, а не вызвать споры. Я просто хочу узнать, имеют ли какие-либо другие скомпилированные языки такие функции.
Ответы
Ответ 1
Любой язык с реальными макросами имеет форму static if. Например, Lisp и Nemerle позволяют создавать код, который макрос расширяет, используя конструкторы программирования, такие как 'if' и for-loop, Это, по сути, решения во время компиляции и позволяют делать что-то похожее на static if. В случае макросов Nemerle это в основном плагины для компилятора, которые выполняются во время компиляции.
В С++ там увеличить MPL библиотеку, которая имеет вид static, если, который можно использовать для выбора между двумя типами. Вы можете поместить некоторый код внутри двух типов в члене 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
Для "языковой осведомленности о коде" нет лучшего, чем я видел, чем 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.
В этом примере может показаться слишком много работы, но иметь возможность делать такие вещи - это хорошая сила, о чем говорит ваш вопрос.
Ответ 3
static_if
был предложен для следующей версии С++ (С++ 1y). Первоначально он был предложен для С++ 11, но, по-видимому, был задержан.
См. предложение здесь. Интересно, что одним из авторов является Уолтер Брайт, создатель D.
Кроме того, можно подделать static-if в текущем С++ с использованием компилятора hacks.