2009-11-11 10 views
13

Creo que D's static if es una función de idioma interesante. Eso me lleva a preguntar: ¿hay otros ejemplos de lenguajes compilados en los que el compilador tenga una noción fuerte del código y haya facilidades de idiomas para acceder a ellos?¿Hay otros idiomas además de D con static si?

Por ejemplo, este código proporciona algo similar a repr desde 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); 
    } 
} 

creo que esto es lo máximo es debido a que permite un enfoque diferente y más general a lo sobrecarga hace, que es una especie de interior forma de hacer código más dinámico, en comparación con características como esta. Por ejemplo, el compilador sabe cuántos campos tiene mi clase, pero no hay forma de que mi código acceda a esa información en tiempo de compilación en la mayoría de los idiomas.

CAVEAT: Ese último párrafo tenía opiniones en él, pero solo quiero proporcionar algo de motivación y aclaración para mi pregunta, no suscitar controversia. Solo quiero saber si otros lenguajes compilados tienen tales características.

+1

Muchos compiladores de C y C++ eliminarán el código no utilizado si la expresión en 'if' se puede determinar en tiempo de compilación. Sin embargo, no estoy seguro si esto responde completamente su pregunta. –

+0

No: el ejemplo anterior no se compilaría porque el código en algunos bloques de la static no sería válido. P.ej. si T fuera type int, entonces value .__ repr __() sería un error de compilación. – Grumdrig

+1

Necesita hacer que "static if (is (typeof (value .__ repr__)))" para que se compile lo anterior. O "estático si (__ rasgos (compila, valor .__ repr__))". – Baxissimo

Respuesta

10

Cualquier lenguaje con macros reales tiene una forma de estática if. Por ejemplo, Lisp y Nemerle le permiten construir el código que una macro expande al usar construcciones de programación como 'if' y for-loops. Esas son esencialmente decisiones en tiempo de compilación y le permiten hacer algo similar a static si. En el caso de las macros de Nemerle, básicamente son complementos del compilador que se ejecutan en tiempo de compilación.

En C++ hay una biblioteca boost MPL que tiene un kind of static if que se puede utilizar para elegir entre dos tipos. Podría poner un código dentro de los dos tipos en un miembro run() y obtener algo similar, pero con una sintaxis muy engorrosa.

Por ejemplo, con MPL Boost se podría hacer algo como esto:

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

En D que serías:

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

Hermoso. Nemerle es nuevo para mí y muy interesante. Esperando explorarlo. Lisp macros que debería haber pensado. – Grumdrig

2

para "La conciencia del lenguaje del código" A, no hay mejor me Se ha visto que Lisp y su facilidad macro, específicamente, Common Lisp. Pero el comercio allí es que la mayoría de las veces no se conoce el tipo de un objeto en tiempo de compilación o macroexpansión. Para los literales, los tipos son conocidos, por lo que puede encontrar ejemplos de macros agresivas que prueban para ver si un objeto es literal y, de ser así, trátelo de una manera, tal vez según su tipo, y prepare la variable detectada. para la inspección de tipo de tiempo de ejecución.

Aquí hay un ejemplo que adapté de la biblioteca CLLIB (parte de la biblioteca CLOCC) hace varios años. El objetivo es proporcionar funciones que corten una cadena de prefijo de otra cadena con un prefijo coincidente. El prefijo puede conocerse en el momento de macroexpansión o puede que no. Si es así, podemos hacer una optimización: calcular la longitud del prefijo primero e incrustarlo como un literal, de modo que no se vuelva a calcular en cada llamada a la función generada. La macro es desalentadora al principio, pero el código generado real es pequeño.

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

Ver la forma

(if (stringp prefix) 

en el medio? Eso es inspeccionar el primer argumento en el tiempo de macroexpansión y, dependiendo de si el argumento es literal o simbólico, su tipo puede o no ser conocido.Si el tipo es un símbolo, suponemos que debemos esperar hasta el tiempo de ejecución para reconsiderarlo como una variable que apunta a algún otro valor.

Aquí está la expansión de la forma (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)))) 

Tenga en cuenta que la variable #:PREFIX-LENGTH-5343 está obligado a la longitud calculada de FOO, con destino a la variable #:PREFIX-5342 aquí.

Ahora mira la expansión de la forma (after-prefix "foo" bar), donde el prefijo es ahora una cadena 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))) 

Ahora no hay cómputo de la longitud de "foo"; está en línea como 3.

Puede parecer demasiado trabajo en este ejemplo, pero ser capaz de hacer tales cosas es un buen poder tener, como su pregunta opina.

Cuestiones relacionadas