Pergunta

Eu acho que D'S static if é um recurso de linguagem interessante. Isso solicita minha pergunta: existem outros exemplos de idiomas compilados nos quais o compilador tem uma forte noção do código e existem instalações de idiomas para acessá -las?

Por exemplo, este código fornece algo semelhante a repr De 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);
  }
}

Eu acho que isso é legal é porque permite uma abordagem diferente e mais geral para o que a sobrecarga faz, o que é uma maneira de dentro fora de tornar o código mais dinâmico, em comparação com recursos como esse. Por exemplo, o compilador sabe quantos campos minha classe possui, mas não há como meu código acessar essas informações no tempo de compilação na maioria dos idiomas.

Advertência: Esse último parágrafo tinha opiniões, mas eu pretendo fornecer alguma motivação e esclarecimento para minha pergunta, não provocar controvérsia. Eu só quero descobrir se outros idiomas compilados têm esses recursos.

Foi útil?

Solução

Qualquer idioma com macros reais tem uma forma de estática se. Por exemplo lisp e Nemerle Deixe você construir o código que uma macro expande para usar construções de programação como 'se' e loops. Essas são essencialmente decisões de tempo de compilação e permitem que você faça algo semelhante à estática se. No caso de macros Nemerle, são basicamente plug-ins para o compilador executado em tempo de compilação.

Em C ++, há Boost mpl biblioteca que tem um tipo de estática se Isso pode ser usado para escolher entre dois tipos. Você pode colocar algum código dentro dos dois tipos em um membro run () e obter algo meio semelhante, mas com sintaxe muito pesada.

Por exemplo, com o Boost MPL, você pode fazer algo assim:

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

Em D que seria:

static if(is(T == float)) {
     /* float code */
}
else {
     /* int code */
}

Outras dicas

Para uma "consciência do código de idioma", não há melhor que eu vi do que o LISP e sua instalação de macro - especificamente, comuns. Mas o comércio é que na maioria das vezes o tipo de objeto não é conhecido no tempo de compilação ou no tempo de macroexpansion. Para os literais, os tipos são conhecidos, para que você possa encontrar exemplos de macros agressivas que testem para ver se um objeto é literal e, se assim for, trate -o de uma maneira - talvez com base em seu tipo - e prepare a variável detectada para inspeção do tipo de tempo de execução.

Aqui está um exemplo que eu adaptei do Cllib biblioteca (parte do Clocc biblioteca) há vários anos. O objetivo é fornecer funções que cortarão uma string de prefixo de outra string com um prefixo correspondente. O prefixo pode ser conhecido no tempo de macroexpansion ou pode não. Se for, podemos uma otimização: calcular o comprimento do prefixo primeiro e incorporá -lo como literal, para que ele não seja recalculado em cada chamada para a função gerada. A macro é assustadora no início, mas o código gerado real é pequeno.

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

Veja o formulário

(if (stringp prefix)

no meio? Isso está inspecionando o primeiro argumento no tempo de macroexpansion e, dependendo se o argumento é um literal ou símbolo, seu tipo pode ou não ser conhecido. Se o tipo é um símbolo, nós presumir que devemos esperar até o tempo de execução para reconsiderá -lo como uma variável apontando para algum outro valor.

Aqui está a expansão para o formulário (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))))

Observe que a variável #:PREFIX-LENGTH-5343 está ligado ao comprimento calculado do FOO, vinculado aqui à variável #:PREFIX-5342.

Agora olhe para a expansão do formulário (after-prefix "foo" bar), onde o prefixo agora é um 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)))

Agora não há como calcular o comprimento de "Foo"; está inlinado como 3.

Pode parecer muito trabalho neste exemplo, mas ser capaz de fazer essas coisas é um bom poder de ter, como sua pergunta opina.

static_if foi proposto para a próxima versão do C ++ (C ++ 1Y). Foi proposto originalmente para C ++ 11, mas aparentemente foi atrasado.

Veja a proposta aqui. Curiosamente, um dos autores é Walter Bright, o criador de D.

Também, É possível falsificar estático-se no C ++ atual usando hackers do compilador.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top