X-Git-Url: https://git.llucax.com/z.facultad/75.00/informe.git/blobdiff_plain/b0956c037ae52da777b06705157486067755ec2c..176db493a0519114c728648cbe94582a1a476078:/source/d.rst?ds=sidebyside diff --git a/source/d.rst b/source/d.rst index f916af0..bab59d5 100644 --- a/source/d.rst +++ b/source/d.rst @@ -31,19 +31,19 @@ Java_ o incluso lenguajes dinámicos como Perl_. El origen del lenguaje está plasmado en su sitio web, en donde se cita: - It seems to me that most of the "new" programming languages fall - into one of two categories: Those from academia with radical new - paradigms and those from large corporations with a focus on RAD and - the web. Maybe it's time for a new language born out of practical - experience implementing compilers. + It seems to me that most of the "new" programming languages fall into + one of two categories: Those from academia with radical new paradigms + and those from large corporations with a focus on RAD and the web. Maybe + it's time for a new language born out of practical experience + implementing compilers. Esto podría traducirse como: - Parece que la mayoría de los lenguajes de programación "nuevos" caen - en 2 categorías: aquellos académicos con nuevos paradigmas radicales y - aquellos de grandes corporaciones con el foco en el desarrollo rápido - y web. Tal vez es hora de que nazca un nuevo lenguaje de la experiencia - práctica implementando compiladores. + Parece que la mayoría de los lenguajes de programación "nuevos" caen en + 2 categorías: aquellos académicos con nuevos paradigmas radicales + y aquellos de grandes corporaciones con el foco en el desarrollo rápido + y web. Tal vez es hora de que nazca un nuevo lenguaje de la experiencia + práctica implementando compiladores. La versión 1.0 fue más bien una etiqueta arbitraria que un indicador real de estar ante una versión estable y completa. Luego de liberarse se @@ -52,11 +52,11 @@ el desarrollo en paralelo de la versión 2.0 al introducirse el concepto de inmutabilidad y funciones *puras* [#dpure]_ (a mediados de 2007). .. [#dpure] Por funciones *puras* en D_ se entiende que no tienen efectos - colaterales. Es decir, una función pura siempre que se llame - con la misma entrada producirá el mismo resultado. Esto es - análogo a como funcionan los lenguajes funcionales en general, - abríendo la puerta a la programación de estilo funcional en - D_. + colaterales. Es decir, una función pura siempre que se llame con la + misma entrada producirá el mismo resultado. Esto es análogo a como + funcionan los lenguajes funcionales en general, abríendo la puerta a la + programación de estilo funcional en + D_. A partir de este momento la versión 1.0 quedó *teóricamente* congelada, introduciendo solo cambios que arreglen errores (*bug fixes*), @@ -79,10 +79,10 @@ lenguaje. En este contexto nacen primero Mango_ y luego Ares_. Mango_ fue creada por Kris Macleod Bell a principios de 2004 como una biblioteca que provee servicios básicos de entrada/salida (o *I/O* de *input/output* en inglés) -de alto rendimiento. Siendo estos servicios algo básico lo más natural +de alto rendimiento. Siendo estos servicios algo básico lo más natural hubiera sido que se encuentren en la biblioteca estándar de D_ pero por las dificultades para contribuir a ésta, se desarrolla como una biblioteca -separada. A mediados de 2004 Sean Kelly crea Ares_ , con las mismas +separada. A mediados de 2004 Sean Kelly crea Ares_ , con las mismas motivaciones pero con la intención de crear una biblioteca base (conocida en inglés como *runtime*) que incluye los servicios básicos que necesita el lenguaje (información de tipos, manejo de excepciones e hilos, creación @@ -125,7 +125,7 @@ Descripción general D_ es un lenguaje de programación con sintaxis tipo C, multi-paradigma, compilado, con *tipado* fuerte y estático, buenas capacidades tanto de -programación de bajo nivel (*system programming*) como de alto nivel. Es +programación de bajo nivel (*system programming*) como de alto nivel. Es compatible de forma binaria con C (se puede enlazar código objeto C con código objeto D). Con estas características, D_ logra llenar un vacío importante que hay entre lo lenguajes de alto bajo nivel y los de alto @@ -173,6 +173,7 @@ A continuación se enumeran las principales características de D_, agrupadas por unidades funcional o paradigmas que soporta: + .. _ref_d_generic: Programación genérica y meta-programación @@ -197,230 +198,231 @@ D_ provee las siguientes herramientas para realizar programación genérica y meta-programación: ``if`` estático (``static if``): - puede verse como similar a la directiva del preprocesador de C/C++ - ``#if``, pero a diferencia de esto, en D_ el ``static if`` tiene acceso - a todos los símbolos del compilador (constantes, tipos, variables, etc). + puede verse como similar a la directiva del preprocesador de C/C++ + ``#if``, pero a diferencia de esto, en D_ el ``static if`` tiene acceso + a todos los símbolos del compilador (constantes, tipos, variables, etc). - Ejemplo:: + Ejemplo:: - static if ((void*).sizeof == 4) - pragma(msg, "32 bits"); + static if ((void*).sizeof == 4) + pragma(msg, "32 bits"); - Más información en http://www.digitalmars.com/d/1.0/version.html#staticif + Más información en http://www.digitalmars.com/d/1.0/version.html#staticif Inferencia de tipos básica implícita y explícita (mediante ``typeof``): - si no se especifica un tipo al declarar una variable, se infiere del tipo - de su inicializador. + si no se especifica un tipo al declarar una variable, se infiere del + tipo de su inicializador. - Ejemplo:: + Ejemplo:: - static i = 5; // i es int - const d = 6.0; // d es double - auto s = "hola"; // s es string (que es un alias de char[]) + static i = 5; // i es int + const d = 6.0; // d es double + auto s = "hola"; // s es string (que es un alias de char[]) - Más información en - http://www.digitalmars.com/d/1.0/declaration.html#AutoDeclaration + Más información en + http://www.digitalmars.com/d/1.0/declaration.html#AutoDeclaration - Mediante el uso de ``typeof`` se puede solicitar el tipo de una expresión - arbitraria. + Mediante el uso de ``typeof`` se puede solicitar el tipo de una + expresión arbitraria. - Ejemplo:: + Ejemplo:: - typeof(5 + 6.0) d; // d es double + typeof(5 + 6.0) d; // d es double - Más información en http://www.digitalmars.com/d/1.0/declaration.html#typeof + Más información en http://www.digitalmars.com/d/1.0/declaration.html#typeof Iteración sobre colecciones (``foreach``): - cualquier tipo de colección (arreglos estáticos y dinámicos, arreglos - asociativos, clases, estructuras o delegados) puede ser iterada mediante - la sentencia ``foreach``. + cualquier tipo de colección (arreglos estáticos y dinámicos, arreglos + asociativos, clases, estructuras o delegados) puede ser iterada mediante + la sentencia ``foreach``. - Ejemplo:: + Ejemplo:: - int[] a = [ 1, 2, 3 ]; - int total = 0; - foreach (i; a) - total += i; + int[] a = [ 1, 2, 3 ]; + int total = 0; + foreach (i; a) + total += i; *Templates*: - clases y funciones pueden ser parametrizadas. Esto permite desarrollar - algoritmos genéricos sin importar el tipo de los datos de entrada, - siempre y cuando todos los tipos tengan una *interfaz* común. Esto - también es conocido como *polimorfismo en tiempo de compilación*, y es la - forma más básica de programación genérica. + clases y funciones pueden ser parametrizadas. Esto permite desarrollar + algoritmos genéricos sin importar el tipo de los datos de entrada, + siempre y cuando todos los tipos tengan una *interfaz* común. Esto + también es conocido como *polimorfismo en tiempo de compilación*, y es + la forma más básica de programación genérica. + + Ejemplo:: + + T sumar(T)(T x, T y) { return x + y; } + auto i = sumar!(int)(5, 6); // i == 11 + auto f = sumar!(float)(5, 6); // j == 11.0f + + Además se pueden definir bloques de declaraciones parametrizados (esto + no es posible en C++), permitiendo instanciar dicho bloque con + parámetros particulares. Esto sirve como un mecanismo para la + reutilización de código, ya que puede incluirse un mismo bloque en + distintos lugares (por ejemplo clases). Un bloque parametrizado puede + verse como una especie de módulo. + + Ejemplo:: + + template bloque(T, U) { + T x; + U foo(T y); + } - Ejemplo:: + bloque!(int, float).x = 5; + float f = bloque!(int, float).foo(7); - T sumar(T)(T x, T y) { return x + y; } - auto i = sumar!(int)(5, 6); // i == 11 - auto f = sumar!(float)(5, 6); // j == 11.0f + La utilidad más prominente de los bloques parametrizados se da al + acompañarse de *mixins*. - Además se pueden definir bloques de declaraciones parametrizados (esto no - es posible en C++), permitiendo instanciar dicho bloque con parámetros - particulares. Esto sirve como un mecanismo para la reutilización de - código, ya que puede incluirse un mismo bloque en distintos lugares (por - ejemplo clases). Un bloque parametrizado puede verse como una especie de - módulo. +Instanciación implícita de funciones parametrizadas: + el lenguaje es capaz de deducir los parámetros siempre que no hayan + ambigüedades - Ejemplo:: + Ejemplo:: - template bloque(T, U) { - T x; - U foo(T y); - } + auto i = sumar(5, 6); // i == 11 + auto f = sumar(5.0f, 6.0f); // f == 11.0f - bloque!(int, float).x = 5; - float f = bloque!(int, float).foo(7); +Especialización explícita y parcial de *templates*: + la especialización de *templates* consiste, al igual que en C++, en + proveer una implementación especializada para un tipo de dato (o valor) + de los parámetros. Especialización parcial se refiere a la capacidad + de especializar un parámetro a través de un subtipo. Por ejemplo, se + puede especializar un *template* para cualquier tipo de puntero, o para + cualquier tipo de arreglo dinámico, sin necesidad de especificar el tipo + al que apunta dicho puntero o el tipo almacenado por el arreglo. - La utilidad más prominente de los bloques parametrizados se da al - acompañarse de *mixins*. + Ejemplo de especialización:: -Instanciación implícita de funciones parametrizadas: - el lenguaje es capaz de deducir los parámetros siempre que no hayan - ambigüedades + T sumar(T: int)(T x, T y) { return x + y + 1; } + auto i = sumar(5, 6); // i == 12 + auto f = sumar(5.0f, 6.0f) // f == 11.0f - Ejemplo:: + Ejemplo de especialización parcial:: - auto i = sumar(5, 6); // i == 11 - auto f = sumar(5.0f, 6.0f); // f == 11.0f + T sumar(T: T*)(T x, T y) { return *x + *y; } + int x = 5, y = 6; + auto i = sumar(&x, &y); // i == 11 + float v = 5.0f, w = 6.0f; + auto f = sumar(&v, &w); // f == 11.0f -Especialización explícita y parcial de *templates*: - la especialización de *templates* consiste, al igual que en C++, en - proveer una implementación especializada para un tipo de dato (o valor) - de los parámetros. Especialización parcial se refiere a la capacidad de - especializar un parámetro a través de un subtipo. Por ejemplo, se puede - especializar un *template* para cualquier tipo de puntero, o para - cualquier tipo de arreglo dinámico, sin necesidad de especificar el tipo - al que apunta dicho puntero o el tipo almacenado por el arreglo. +Tipos, valores (incluyendo *strings*) y *templates* como parámetros: + esto es otro bloque de construcción importantísimo para la programación + genérica en D, ya que combinando *templates* que toman *strings* como + parámetro en combinación con *string mixins* pueden hacerse toda clase + de meta-programas. + + Ejemplo:: + + template hash(string s, uint so_far=0) { + static if (s.length == 0) + const hash = sofar; + else + const hash = hash!(s[1 .. length], sofar * 11 + s[0]); + } + string s = hash!("hola"); // calculado en tiempo de compilación - Ejemplo de especialización:: +Cantidad de parámetros variables para *templates*: + Esto permite implementar tuplas u otros algoritmos que inherentemente + deben tomar parámetros variables en tiempo de compilación. - T sumar(T: int)(T x, T y) { return x + y + 1; } - auto i = sumar(5, 6); // i == 12 - auto f = sumar(5.0f, 6.0f) // f == 11.0f + Ejemplo:: - Ejemplo de especialización parcial:: + double sumar(T...)(T t) { + double res = 0.0; + foreach (x; t) + res += x; + return res; + } + double d = sumar(1, 2.0, 3.0f, 4l); // d == 10.0 - T sumar(T: T*)(T x, T y) { return *x + *y; } - int x = 5, y = 6; - auto i = sumar(&x, &y); // i == 11 - float v = 5.0f, w = 6.0f; - auto f = sumar(&v, &w); // f == 11.0f +*CTFE* (*compile-time function execution*): + si una función cumple ciertas reglas básicas (como por ejemplo no tener + efectos colaterales) puede ser ejecutada en tiempo de compilación en vez + de tiempo de ejecución. Esto permite hacer algunos cálculos que no + cambian de ejecución en ejecución al momento de compilar, mejorando la + performance o permitiendo formas avanzadas de metaprogramación. Esta + característica se vuelve particularmente útil al combinarse con *string + mixins*. + + Ejemplo:: + + int factorial(int n) { + if (n == 1) + return 1; + else + return n * factorial(n - 1); + } + static int x = factorial(5); // calculado en tiempo de compilación + int x = factorial(5); // calculado en tiempo de ejecución -Tipos, valores (incluyendo *strings*) y *templates* como parámetros: - esto es otro bloque de construcción importantísimo para la programación - genérica en D, ya que combinando *templates* que toman *strings* como - parámetro en combinación con *string mixins* pueden hacerse toda clase de - meta-programas. + Esta característica es vital para evitar la duplicación de código. - Ejemplo:: +*Mixins*, incluyendo *string mixins*: + la palabra *mixin* tiene significados distintos en varios lenguajes de + programación. En D_ *mixin* significa tomar una secuencia arbitraria de + declaraciones e insertarla en el contexto (*scope*) actual. Esto puede + realizarse a nivel global, en clases, estructuras o funciones. Esto + sirve como un mecanismo para evitar duplicación de código que puede ser + introducida por la falta de herencia múltiple. - template hash(string s, uint so_far=0) { - static if (s.length == 0) - const hash = sofar; - else - const hash = hash!(s[1 .. length], sofar * 11 + s[0]); - } - string s = hash!("hola"); // calculado en tiempo de compilación + Ejemplo:: -Cantidad de parámetros variables para *templates*: - Esto permite implementar tuplas u otros algoritmos que inherentemente - deben tomar parámetros variables en tiempo de compilación. + class A { + mixin bloque!(int, float); + } + A a = new A; + a.x = 5; + float f = a.foo(a.x); - Ejemplo:: + class B { + mixin bloque!(long, double); + } + B b = new B; + b.x = 5l; + double d = a.foo(a.x); - double sumar(T...)(T t) { - double res = 0.0; - foreach (x; t) - res += x; - return res; - } - double d = sumar(1, 2.0, 3.0f, 4l); // d == 10.0 + *String mixin* se refiere a la capacidad de *incrustar* un *string* que + contenga un fragmento de código en un programa como si este fragmento + hubiera sido escrito en el código fuente directamente por el + programador. Esto permite hacer manipulaciones arbitrariamente + complejas en combinación con funciones ejecutadas en tiempo de + compilación. -*CTFE* (*compile-time function execution*): - si una función cumple ciertas reglas básicas (como por ejemplo no tener - efectos colaterales) puede ser ejecutada en tiempo de compilación en vez - de tiempo de ejecución. Esto permite hacer algunos cálculos que no - cambian de ejecución en ejecución al momento de compilar, mejorando la - performance o permitiendo formas avanzadas de metaprogramación. Esta - característica se vuelve particularmente útil al combinarse con *string - mixins*. - - Ejemplo:: - - int factorial(int n) { - if (n == 1) - return 1; - else - return n * factorial(n - 1); - } - static int x = factorial(5); // calculado en tiempo de compilación - int x = factorial(5); // calculado en tiempo de ejecución - - Esta característica es vital para evitar la duplicación de código. + Ejemplo:: -*Mixins*, incluyendo *string mixins*: - la palabra *mixin* tiene significados distintos en varios lenguajes de - programación. En D_ *mixin* significa tomar una secuencia arbitraria de - declaraciones e insertarla en el contexto (*scope*) actual. Esto puede - realizarse a nivel global, en clases, estructuras o funciones. Esto sirve - como un mecanismo para evitar duplicación de código que puede ser - introducida por la falta de herencia múltiple. - - Ejemplo:: - - class A { - mixin bloque!(int, float); - } - A a = new A; - a.x = 5; - float f = a.foo(a.x); - - class B { - mixin bloque!(long, double); - } - B b = new B; - b.x = 5l; - double d = a.foo(a.x); - - *String mixin* se refiere a la capacidad de *incrustar* un *string* que - contenga un fragmento de código en un programa como si este fragmento - hubiera sido escrito en el código fuente directamente por el programador. - Esto permite hacer manipulaciones arbitrariamente complejas en - combinación con funciones ejecutadas en tiempo de compilación. - - Ejemplo:: - - string generar_sumar(string var_x, string var_y) { - return "return " ~ var_x ~ " + " ~ var_y ~ ";"; - } - - int sumar(int a, int b) { - mixin(generar_sumar!("a", b")); - } - - Más información en http://www.digitalmars.com/d/1.0/mixin.html + string generar_sumar(string var_x, string var_y) { + return "return " ~ var_x ~ " + " ~ var_y ~ ";"; + } -Expresiones ``is``: - las *expresiones ``is``* permiten la compilación condicional basada en - las características de un tipo. Esto se realiza en favor a una técnica - utilizada en C++ de realizar *pattern matching* sobre los parámetros de - las plantillas. + int sumar(int a, int b) { + mixin(generar_sumar!("a", b")); + } - Ejemplo:: + Más información en http://www.digitalmars.com/d/1.0/mixin.html - T foo(T)(T x) { - static if (is(T == class)) - return new T; - else - return T.init; - } +Expresiones ``is``: + las *expresiones ``is``* permiten la compilación condicional basada en + las características de un tipo. Esto se realiza en favor a una técnica + utilizada en C++ de realizar *pattern matching* sobre los parámetros de + las plantillas. + + Ejemplo:: + + T foo(T)(T x) { + static if (is(T == class)) + return new T; + else + return T.init; + } - Esto provee además una forma simple de reflexión en tiempo de - compilación. + Esto provee además una forma simple de reflexión en tiempo de + compilación. - Más información en - http://www.digitalmars.com/d/1.0/expression.html#IsExpression + Más información en + http://www.digitalmars.com/d/1.0/expression.html#IsExpression @@ -436,90 +438,90 @@ probablemente el lenguaje de bajo nivel más popular, seguido por C++. D_ presenta muchas características de bajo nivel: Compila a código de máquina nativo: - no es interpretado ni necesita una máquina virtual como otros lenguajes - de más alto nivel como Java_, `C#`_, Python_, etc. + no es interpretado ni necesita una máquina virtual como otros lenguajes + de más alto nivel como Java_, `C#`_, Python_, etc. Provee acceso a *assembly*: - por lo tanto, acceso directo al *hardware* y la posibilidad de utilizar - cualquier característica de éste que no esté disponible en el lenguaje. + por lo tanto, acceso directo al *hardware* y la posibilidad de utilizar + cualquier característica de éste que no esté disponible en el lenguaje. - Una ventaja sobre C y C++ es que el lenguaje *assembly* utilizado dentro - de D_ está especificado, por lo que se puede mantener la portabilidad - entre compiladores incluso cuando se utiliza *assembly* (mientras que no - se cambie de arquitectura, por supuesto). + Una ventaja sobre C y C++ es que el lenguaje *assembly* utilizado dentro + de D_ está especificado, por lo que se puede mantener la portabilidad + entre compiladores incluso cuando se utiliza *assembly* (mientras que no + se cambie de arquitectura, por supuesto). ``goto``: - al igual que C y C++, D_ provee la flexibilidad del uso de ``goto``. + al igual que C y C++, D_ provee la flexibilidad del uso de ``goto``. -Compatibilidad con C - soporta todos los tipos de C y es ABI [#abi]_ compatible con éste. Esto - permite enlazar archivos objeto estándar de C y D_ en un mismo programa. - Además permite interoperar con C a través de ``extern (C)``. +Compatibilidad con C: + soporta todos los tipos de C y es ABI [#abi]_ compatible con éste. Esto + permite enlazar archivos objeto estándar de C y D_ en un mismo programa. + Además permite interoperar con C a través de ``extern (C)``. - Ejemplo:: + Ejemplo:: - extern (C) printf(const char* format, ...); - printf("3 + 5 == %d\n", 3 + 5); // llama al printf de C + extern (C) printf(const char* format, ...); + printf("3 + 5 == %d\n", 3 + 5); // llama al printf de C Manejo de memoria explícito: - permite alocar estructuras en el *stack* o en el *heap*, haciendo uso de - los servicios del sistema operativo o la biblioteca estándar de C. + permite alocar estructuras en el *stack* o en el *heap*, haciendo uso de + los servicios del sistema operativo o la biblioteca estándar de C. Objetos y arreglos *livianos*: - por objetos *livianos* se entiende no-polimórficos. Es decir, un - agrupamiento de variables análogo al ``struct`` de C, sin tabla - virtual ni otro tipo de *overhead*. Los arreglos *livianos* son - arreglos estáticos como en C, cuyo tamaño es fijo, también sin ningún - tipo de *overhead* como C. Además puede alocarse un arreglo dinámicamente - usando ``malloc()`` y utilizar el operador ``[]`` para accederlo. - - Esto también permite interoperar con C, ya que pueden definirse - ``structs`` y arreglos que pueden ser intercambiados con dicho lenguaje - sin problemas. - - Ejemplo:: - - struct timeval { - time_t tv_sec; - suseconds_t tv_usec; - } - extern (C) { - void* malloc(size_t); - size_t strlen(const char *); - int gettimeofday(timeval *, void *); - } - char* s = cast(char*) malloc(2); - s[0] = 'C'; - s[1] = '\0'; - size_t l = strlen(s); // l == 1 - timeval tv; - gettimeofday(&tv, null); + por objetos *livianos* se entiende no-polimórficos. Es decir, un + agrupamiento de variables análogo al ``struct`` de C, sin tabla virtual + ni otro tipo de *overhead*. Los arreglos *livianos* son arreglos + estáticos como en C, cuyo tamaño es fijo, también sin ningún tipo de + *overhead* como C. Además puede alocarse un arreglo dinámicamente usando + ``malloc()`` y utilizar el operador ``[]`` para accederlo. + + Esto también permite interoperar con C, ya que pueden definirse + ``structs`` y arreglos que pueden ser intercambiados con dicho lenguaje + sin problemas. + + Ejemplo:: + + struct timeval { + time_t tv_sec; + suseconds_t tv_usec; + } + extern (C) { + void* malloc(size_t); + size_t strlen(const char *); + int gettimeofday(timeval *, void *); + } + char* s = cast(char*) malloc(2); + s[0] = 'C'; + s[1] = '\0'; + size_t l = strlen(s); // l == 1 + timeval tv; + gettimeofday(&tv, null); Rendimiento: - la :ref:`ref_d_generic` permite realizar muchas optimizaciones ya que - se resuelve en tiempo de compilación y por lo tanto aumentando la - *performance* en la ejecución. + la :ref:`ref_d_generic` permite realizar muchas optimizaciones ya que se + resuelve en tiempo de compilación y por lo tanto aumentando la + *performance* en la ejecución. Número de punto flotante de 80 bits: - El tipo ``real`` de D_ tiene precisión de 80 bits si la plataforma lo - soporta (por ejemplo en i386). + El tipo ``real`` de D_ tiene precisión de 80 bits si la plataforma lo + soporta (por ejemplo en i386). Control de alineación de miembros de una estructura: - Mediante ``align`` se puede especificar la alineación a tener en una - estructura. + Mediante ``align`` se puede especificar la alineación a tener en una + estructura. - Ejemplo:: + Ejemplo:: - align (1) - struct paquete_de_red { - char tipo; - short valor; - } - // paquete_de_red.sizeof == 3 + align (1) + struct paquete_de_red { + char tipo; + short valor; + } + // paquete_de_red.sizeof == 3 .. [#abi] Interfaz de Aplicación Binaria (del inglés *Application Binary - Interface*). + Interface*). .. _ref_d_high_level: @@ -538,162 +540,162 @@ la productividad de los programadores. D_ adopta herramientas de muchos lenguajes de alto nivel, como Java_ y Python_, por ejemplo: Manejo automático de memoria: - al igual que C/C++ y prácticamente cualquier lenguaje imperativo maneja - automáticamente el *stack*, pero a diferencia de la mayoría de los - lenguajes de bajo nivel, D_ permite manejar el *heap* de manera - automática también a través de un *recolección de basura*. + al igual que C/C++ y prácticamente cualquier lenguaje imperativo maneja + automáticamente el *stack*, pero a diferencia de la mayoría de los + lenguajes de bajo nivel, D_ permite manejar el *heap* de manera + automática también a través de un *recolección de basura*. Sistema de paquetes y módulos (similar a Java_ o Python_): - un módulo es una unidad que agrupa clases, funciones y cualquier otra - construcción de lenguaje. Un paquete es una agrupación de módulos. D_ - asocia un módulo a un archivo fuente (y un archivo objeto cuando éste es - compilado) y un paquete a un directorio. A diferencia de C/C++ no - necesita de un preprocesador para incluir declaraciones de otros - *módulos* (en C/C++ no existe el concepto de módulo, solo de unidades de - compilación). + un módulo es una unidad que agrupa clases, funciones y cualquier otra + construcción de lenguaje. Un paquete es una agrupación de módulos. D_ + asocia un módulo a un archivo fuente (y un archivo objeto cuando éste es + compilado) y un paquete a un directorio. A diferencia de C/C++ no + necesita de un preprocesador para incluir declaraciones de otros + *módulos* (en C/C++ no existe el concepto de módulo, solo de unidades de + compilación). - Ejemplo: + Ejemplo: - ``a.d``:: + ``a.d``:: - module a; - void f() {} + module a; + void f() {} - ``b.d``:: + ``b.d``:: - module b; - void f() {} + module b; + void f() {} - ``c.d``:: + ``c.d``:: - module c; - import a; - import b: f; - a.f(); - b.f(); - f(); // ejecuta b.f() + module c; + import a; + import b: f; + a.f(); + b.f(); + f(); // ejecuta b.f() Funciones y delegados: - las funciones pueden ser sobrecargadas (funciones con el mismo nombre - pero distinta cantidad o tipo de parámetros), pueden especificarse - argumentos de entrada, salida o entrada/salida, argumentos por omisión - o argumentos evaluados de forma perezosa (*lazy*). Además pueden tener - una cantidad de argumentos variables pero manteniendo información de - tipos (más seguro que C/C++). - - Los *delegados* son punteros a función con un contexto asociado. Este - contexto puede ser un objeto (en cuyo caso la función es un método) o un - *stack frame* (en cuyo caso la función es una función anidada). - - Además de esto los delegados son ciudadanos de primera clase - [#1stclasscity]_, disponiendo de forma literal (delegado anónimo), lo que - permite construcciones de alto nivel muy conveniente. Los argumentos - evaluados de forma perezosa no son más que un delegado que se ejecuta - solo cuando es necesario. - - Ejemplo:: - - bool buscar(T[] arreglo, T item, bool delegate(T x, T y) igual) { - foreach (t, arreglo) - if (igual(t, elemento)) - return true; - return false; - } - struct Persona { - string nombre; - } - Persona[] personas; - // llenas personas - Persona p; - p.nombre = "Carlos"; - bool encontrado = buscar(personas, p, - (Persona x, Persona y) { - return x.nombre == y.nombre; - } - ); + las funciones pueden ser sobrecargadas (funciones con el mismo nombre + pero distinta cantidad o tipo de parámetros), pueden especificarse + argumentos de entrada, salida o entrada/salida, argumentos por omisión + o argumentos evaluados de forma perezosa (*lazy*). Además pueden tener + una cantidad de argumentos variables pero manteniendo información de + tipos (más seguro que C/C++). + + Los *delegados* son punteros a función con un contexto asociado. Este + contexto puede ser un objeto (en cuyo caso la función es un método) o un + *stack frame* (en cuyo caso la función es una función anidada). + + Además de esto los delegados son ciudadanos de primera clase + [#1stclasscity]_, disponiendo de forma literal (delegado anónimo), lo + que permite construcciones de alto nivel muy conveniente. Los argumentos + evaluados de forma perezosa no son más que un delegado que se ejecuta + solo cuando es necesario. + + Ejemplo:: + + bool buscar(T[] arreglo, T item, bool delegate(T x, T y) igual) { + foreach (t, arreglo) + if (igual(t, elemento)) + return true; + return false; + } + struct Persona { + string nombre; + } + Persona[] personas; + // llenas personas + Persona p; + p.nombre = "Carlos"; + bool encontrado = buscar(personas, p, + (Persona x, Persona y) { + return x.nombre == y.nombre; + } + ); Arreglos *dinámicos* y arreglos asociativos: - los arreglos *dinámicos* son arreglos de longitud variable manejados - automáticamente por el lenguaje (análogos al ``std::vector`` de C++). - Soportan concatenación (a través del operador ``~``), rebanado - o *slicing* (a través del operador ``[x..y]``) y chequeo de límites - (*bound checking*). + los arreglos *dinámicos* son arreglos de longitud variable manejados + automáticamente por el lenguaje (análogos al ``std::vector`` de C++). + Soportan concatenación (a través del operador ``~``), rebanado + o *slicing* (a través del operador ``[x..y]``) y chequeo de límites + (*bound checking*). - Los arreglos asociativos (también conocidos como *hashes* o diccionarios) - también son provistos por el lenguaje. + Los arreglos asociativos (también conocidos como *hashes* + o diccionarios) también son provistos por el lenguaje. - Ambos son ciudadanos de primera clase, disponiendo de forma literal. + Ambos son ciudadanos de primera clase, disponiendo de forma literal. - Ejemplo:: + Ejemplo:: - int[] primos = [ 2, 3, 5, 7, 11, 13, 17, 19 ]; - primos ~= [ 23, 29 ]; - auto menores_que_10 = primos[0..4]; // [ 2, 3, 5, 7 ] - int[string] agenda; - agenda["Pepe"] = 5555_1234; + int[] primos = [ 2, 3, 5, 7, 11, 13, 17, 19 ]; + primos ~= [ 23, 29 ]; + auto menores_que_10 = primos[0..4]; // [ 2, 3, 5, 7 ] + int[string] agenda; + agenda["Pepe"] = 5555_1234; *Strings*: - al igual que los delegados y arreglos dinámicos y asociativos, los - *strings* son ciudadanos de primera clase, teniendo forma literal - y siendo codificados en UTF-8/16/32. Son un caso particular de arreglo - dinámico y es posible utilizarlos en sentencias ``switch``/``case``. + al igual que los delegados y arreglos dinámicos y asociativos, los + *strings* son ciudadanos de primera clase, teniendo forma literal + y siendo codificados en UTF-8/16/32. Son un caso particular de arreglo + dinámico y es posible utilizarlos en sentencias ``switch``/``case``. - Ejemplo:: + Ejemplo:: - string s = "árbol"; + string s = "árbol"; - switch (s) { - case "árbol": - s = "tree"; - default: - s = ""; - } + switch (s) { + case "árbol": + s = "tree"; + default: + s = ""; + } ``typedef`` y ``alias``: - el primero define un nuevo tipo basado en otro. A diferencia de C/C++ el - tipo original no puede ser implícitamente convertido al tipo nuevo - (excepto valores literales), pero la conversión es válida en el otro - sentido (similar a los ``enum`` en C++). Por el contrario, ``alias`` es - análogo al ``typedef`` de C/C++ y simplemente es una forma de referirse - al mismo tipo con un nombre distinto. - - Ejemplo:: - - typedef int tipo; - int foo(tipo x) {} - tipo t = 10; - int i = 10; - foo(t); - foo(i); // error, no compila - alias tipo un_alias; - un_alias a = t; - foo(a); + el primero define un nuevo tipo basado en otro. A diferencia de C/C++ el + tipo original no puede ser implícitamente convertido al tipo nuevo + (excepto valores literales), pero la conversión es válida en el otro + sentido (similar a los ``enum`` en C++). Por el contrario, ``alias`` es + análogo al ``typedef`` de C/C++ y simplemente es una forma de referirse + al mismo tipo con un nombre distinto. + + Ejemplo:: + + typedef int tipo; + int foo(tipo x) {} + tipo t = 10; + int i = 10; + foo(t); + foo(i); // error, no compila + alias tipo un_alias; + un_alias a = t; + foo(a); Documentación embebida: - D_ provee un sistema de documentación embebida, análogo a lo que provee - Java_ o Python_ en menor medida. Hay comentarios especiales del código - que pueden ser utilizados para documentarlo de forma tal que luego el - compilador pueda extraer esa información para generar un documento. + D_ provee un sistema de documentación embebida, análogo a lo que provee + Java_ o Python_ en menor medida. Hay comentarios especiales del código + que pueden ser utilizados para documentarlo de forma tal que luego el + compilador pueda extraer esa información para generar un documento. - Más información en http://www.digitalmars.com/d/1.0/ddoc.html + Más información en http://www.digitalmars.com/d/1.0/ddoc.html Números complejos: - D_ soporta números complejos como ciudadanos de primera clase. Soporta - forma literal de números imaginarios y complejos. + D_ soporta números complejos como ciudadanos de primera clase. Soporta + forma literal de números imaginarios y complejos. - Ejemplo:: + Ejemplo:: - ifloat im = 5.0i; - float re = 1.0; - cfloat c = re + im; // c == 1.0 + 5.0i + ifloat im = 5.0i; + float re = 1.0; + cfloat c = re + im; // c == 1.0 + 5.0i .. [#1stclasscity] Por ciudadano de primera clase se entiende que se - trata de un tipo soportado por completo por el lenguaje, disponiendo de - expresiones literales anónimas, pudiendo ser almacenados en variables, - estructuras de datos, teniendo una identidad intrínseca, más allá - de un nombre dado, etc. En realidad los arreglos asociativos no pueden - ser expresados como literales anónimos pero sí tienen una sintaxis - especial soportada directamente por el lenguaje. + trata de un tipo soportado por completo por el lenguaje, disponiendo de + expresiones literales anónimas, pudiendo ser almacenados en variables, + estructuras de datos, teniendo una identidad intrínseca, más allá + de un nombre dado, etc. En realidad los arreglos asociativos no pueden + ser expresados como literales anónimos pero sí tienen una sintaxis + especial soportada directamente por el lenguaje. @@ -706,127 +708,128 @@ herramientas para soportar este paradigma de forma confiable. Entre las características más salientes se encuentran: Objetos *pesados*: - objetos polimórficos como los de cualquier lenguaje con orientación real - a objetos. Estos objetos poseen una tabla virtual para *dispatch* - dinámico, todos los métodos son virtuales a menos que se indique lo - contrario y tienen semántica de referencia [#drefsem]_. Estos objetos - tienen un *overhead* comparados a los objetos *livianos* pero aseguran - una semántica segura para trabajar con orientación a objetos, evitando - problemas con los que se enfrenta C++ (como *slicing* [#dslicing]_) - debido a que permite semántica por valor [#dvalsem]_. - - D_ además soporta tipos de retorno covariantes para funciones virtuales. - Esto significa que una función sobreescrita por una clase derivada puede - retornar un tipo que sea derivado del tipo retornado por la función - original sobreescrita. - - Ejemplo:: - - class A { } - class B : A { } - - class Foo { - A test() { return null; } - } + objetos polimórficos como los de cualquier lenguaje con orientación real + a objetos. Estos objetos poseen una tabla virtual para *dispatch* + dinámico, todos los métodos son virtuales a menos que se indique lo + contrario y tienen semántica de referencia [#drefsem]_. Estos objetos + tienen un *overhead* comparados a los objetos *livianos* pero aseguran + una semántica segura para trabajar con orientación a objetos, evitando + problemas con los que se enfrenta C++ (como *slicing* [#dslicing]_) + debido a que permite semántica por valor [#dvalsem]_. + + D_ además soporta tipos de retorno covariantes para funciones virtuales. + Esto significa que una función sobreescrita por una clase derivada puede + retornar un tipo que sea derivado del tipo retornado por la función + original sobreescrita. + + Ejemplo:: + + class A { } + class B : A { } + + class Foo { + A test() { return null; } + } - class Bar : Foo { - B test() { return null; } // sobreescribe y es covariante con Foo.test() - } + class Bar : Foo { + B test() { return null; } // sobreescribe y es covariante con Foo.test() + } - Más información en http://www.digitalmars.com/d/1.0/function.html + Más información en http://www.digitalmars.com/d/1.0/function.html Interfaces: - D_ no soporta herencia múltiple pero sí interfaces. Una interfaz es - básicamente una tabla virtual, una definición de métodos virtuales que - debe proveer una clase. Las interfaces no proveen una implementación de - dichos métodos, ni pueden tener atributos. Esto simplifica mucho el - lenguaje y no se pierde flexibilidad porque puede conseguirse el mismo - efecto de tener herencia múltiple a través de interfaces y *mixins* para - proveer una implementación o atributos en común a varias clases que - implementan la misma interfaz. + D_ no soporta herencia múltiple pero sí interfaces. Una interfaz es + básicamente una tabla virtual, una definición de métodos virtuales que + debe proveer una clase. Las interfaces no proveen una implementación de + dichos métodos, ni pueden tener atributos. Esto simplifica mucho el + lenguaje y no se pierde flexibilidad porque puede conseguirse el mismo + efecto de tener herencia múltiple a través de interfaces y *mixins* para + proveer una implementación o atributos en común a varias clases que + implementan la misma interfaz. Sobrecarga de operadores: - la sobrecarga de operadores permite que un objeto tenga una sintaxis - similar a un tipo de dato nativo. Esto es muy importante además para la - programación genérica. + la sobrecarga de operadores permite que un objeto tenga una sintaxis + similar a un tipo de dato nativo. Esto es muy importante además para la + programación genérica. Clases anidadas: - al igual que C (con respecto a ``struct``) y C++, pueden anidarse clases - dentro de clases. D_ sin embargo provee la posibilidad de acceder - a atributos de la instancia exterior desde la anidada. - - Ejemplo:: - - class Exterior { - int m; - class Anidada { - int foo() { - return m; // ok, puede acceder a un miembro de Exterior - } + al igual que C (con respecto a ``struct``) y C++, pueden anidarse clases + dentro de clases. D_ sin embargo provee la posibilidad de acceder + a atributos de la instancia exterior desde la anidada. + + Ejemplo:: + + class Exterior { + int m; + class Anidada { + int foo() { + return m; // ok, puede acceder a un miembro de Exterior + } + } } - } - - Esto tiene un pequeño *overhead* ya que la clase ``Anidada`` debe guardar - un puntero a la clase ``Exterior``. Si no se necesita este comportamiento - es posible evitar este *overhead* utilizando ``static``, en cuyo caso - solo puede acceder a atributos estáticos de la clase ``Exterior``. - - Ejemplo:: - - class Exterior { - int m; - static int n; - static class Anidada { - int foo() { - //return m; // error, miembro de Exterior - return n; // ok, miembro estático de Exterior - } + + Esto tiene un pequeño *overhead* ya que la clase ``Anidada`` debe + guardar un puntero a la clase ``Exterior``. Si no se necesita este + comportamiento es posible evitar este *overhead* utilizando ``static``, + en cuyo caso solo puede acceder a atributos estáticos de la clase + ``Exterior``. + + Ejemplo:: + + class Exterior { + int m; + static int n; + static class Anidada { + int foo() { + //return m; // error, miembro de Exterior + return n; // ok, miembro estático de Exterior + } + } } - } Propiedades (*properties*): - en D_ se refiere a funciones miembro que pueden ser tratadas - sintácticamente como campos de esa clase/estructura. + en D_ se refiere a funciones miembro que pueden ser tratadas + sintácticamente como campos de esa clase/estructura. - Ejemplo:: + Ejemplo:: - class Foo { - int data() { return _data; } // propiedad de lectura - int data(int value) { return _data = value; } // de escritura - private int _data; - } - Foo f = new Foo; - f.data = 1; // llama a f.data(1) - int i = f.data; // llama a f.data() + class Foo { + int data() { return _data; } // propiedad de lectura + int data(int value) { return _data = value; } // de escritura + private int _data; + } + Foo f = new Foo; + f.data = 1; // llama a f.data(1) + int i = f.data; // llama a f.data() - Además tipos nativos, clases, estructuras y expresiones tienen - *properties* predefinidos, por ejemplo: + Además tipos nativos, clases, estructuras y expresiones tienen + *properties* predefinidos, por ejemplo: - ``sizeof``: - tamaño ocupado en memoria (ejemplo: ``int.sizeof`` -> 4). + ``sizeof``: + tamaño ocupado en memoria (ejemplo: ``int.sizeof`` -> 4). - ``init``: - valor de inicialización por omisión (ejemplo: ``float.init`` -> *NaN* - [#dnan]_). + ``init``: + valor de inicialización por omisión (ejemplo: ``float.init`` -> *NaN* + [#dnan]_). - ``stringof``: - representación textual del tipo (ejemplo: ``(1+2).stringof`` -> ``"1 - + 2"``). + ``stringof``: + representación textual del tipo (ejemplo: ``(1+2).stringof`` -> ``"1 + + 2"``). - ``mangleof``: - representación textual del tipo *mutilado* [#dmangle]_. + ``mangleof``: + representación textual del tipo *mutilado* [#dmangle]_. - ``alignof`` - alineación de una estructura o tipo. + ``alignof`` + alineación de una estructura o tipo. - Estos son solo los *properties* predefinidos para todos los tipos, pero - hay una cantidad considerable de *properties* extra para cada tipo. + Estos son solo los *properties* predefinidos para todos los tipos, pero + hay una cantidad considerable de *properties* extra para cada tipo. - Más información sobre *properties* de clases en - http://www.digitalmars.com/d/1.0/property.html#classproperties - y sobre *properties* predefinidos en - http://www.digitalmars.com/d/1.0/property.html + Más información sobre *properties* de clases en + http://www.digitalmars.com/d/1.0/property.html#classproperties y sobre + *properties* predefinidos en + http://www.digitalmars.com/d/1.0/property.html .. [#drefsem] Semántica de referencia significa que el tipo es tratado como @@ -859,187 +862,189 @@ evitar que ciertas fallas puedan existir directamente). D_ presta particular atención a esto y provee las siguientes herramientas: Excepciones: - D_ soporta excepciones de manera similar a Java_: provee ``try``, - ``catch`` y ``finally``. Esto permite que los errores difícilmente pasen - silenciosamente sin ser detectados. + D_ soporta excepciones de manera similar a Java_: provee ``try``, + ``catch`` y ``finally``. Esto permite que los errores difícilmente pasen + silenciosamente sin ser detectados. ``assert``: - es una condición que debe cumplirse siempre en un programa, como un - chequeo de integridad. Esto es muy utilizado en C/C++, donde ``assert()`` - es una *macro* que solo se compila cuando la *macro* ``NDEBUG`` no está - definida. Esto permite eliminar los chequeos de integridad del programa, - que pueden ser costosos, para versiones que se suponen estables. + es una condición que debe cumplirse siempre en un programa, como un + chequeo de integridad. Esto es muy utilizado en C/C++, donde + ``assert()`` es una *macro* que solo se compila cuando la *macro* + ``NDEBUG`` no está definida. Esto permite eliminar los chequeos de + integridad del programa, que pueden ser costosos, para versiones que se + suponen estables. - D_ lleva este concepto más allá y hace al ``assert`` parte del lenguaje. - Si una verificación no se cumple, lanza una excepción. El ``assert`` no - es compilado cuando se utiliza una opción del compilador. + D_ lleva este concepto más allá y hace al ``assert`` parte del lenguaje. + Si una verificación no se cumple, lanza una excepción. El ``assert`` no + es compilado cuando se utiliza una opción del compilador. - Ejemplo:: + Ejemplo:: - File f = open("archivo"); - assert (f.ok()); + File f = open("archivo"); + assert (f.ok()); Diseño por contrato: - el diseño por contrato es un concepto introducido por el lenguaje Eiffel_ - a mediados/finales de los '80. Se trata de incorporar en el lenguaje las - herramientas para poder aplicar verificaciones formales a las interfaces - de los programas. - - D_ implementa las siguientes formas de diseño por contrato (todas se - ejecutan siempre y cuando no se compile en modo *release*, de manera de - no sacrificar *performance* cuando es necesario): + el diseño por contrato es un concepto introducido por el lenguaje + Eiffel_ a mediados/finales de los '80. Se trata de incorporar en el + lenguaje las herramientas para poder aplicar verificaciones formales + a las interfaces de los programas. + + D_ implementa las siguientes formas de diseño por contrato (todas se + ejecutan siempre y cuando no se compile en modo *release*, de manera de + no sacrificar *performance* cuando es necesario): + + Pre y post condiciones: + Ejemplo:: + + double raiz_cuadrada(double x) + in { // pre-condiciones + assert (x >= 0.0); + } + out (resultado) { // post-condiciones + assert (resultado >= 0.0); + if (x < 1.0) + assert (resultado < x); + else if (x > 1.0) + assert (resultado > x); + else + assert (resultado == 1); + } + body { + // implementación + } + + Invariantes de representación: + La invariante de representación es un método de una clase + o estructura que es verificada cuando se completa su construcción, + antes de la destrucción, antes y después de ejecutar cualquier + función miembro pública y cuando se lo requiere de forma explícita + utilizando ``assert``. + + Ejemplo:: + + class Fecha { + int dia; + int hora; + invariant() { + assert(1 <= dia && dia <= 31); + assert(0 <= hora && hora < 24); + } + } + + Más información en http://www.digitalmars.com/d/1.0/dbc.html - ure y post condiciones: - Ejemplo:: +Pruebas unitarias: + es posible incluir pequeñas pruebas unitarias en el lenguaje. Éstas son + ejecutadas (cuando no se compila en modo *release*) al comenzar el + programa, antes de que la función ``main()``. - double raiz_cuadrada(double x) - in { // pre-condiciones - assert (x >= 0.0); - } - out (resultado) { // post-condiciones - assert (resultado >= 0.0); - if (x < 1.0) - assert (resultado < x); - else if (x > 1.0) - assert (resultado > x); - else - assert (resultado == 1); - } - body { - // implementación - } + Ejemplo:: - Invariantes de representación: - La invariante de representación es un método de una clase o estructura - que es verificada cuando se completa su construcción, antes de la - destrucción, antes y después de ejecutar cualquier función miembro - pública y cuando se lo requiere de forma explícita utilizando - ``assert``. - - Ejemplo:: - - class Fecha { - int dia; - int hora; - invariant() { - assert(1 <= dia && dia <= 31); - assert(0 <= hora && hora < 24); - } + unittest { + Fecha fecha; + fecha.dia = 5; + assert (fecha.dia == 5); + assert (fecha); } - Más información en http://www.digitalmars.com/d/1.0/dbc.html +Orden de construcción estática: + a diferencia de C++, D_ garantiza el orden de inicialización de los + módulos. Si bien en C++ no hay módulos si no unidades de compilación, es + posible que se ejecute código antes del ``main()`` en C++, si hay, por + ejemplo, instancias globales con un constructor definido. C++ no + garantiza un orden de inicialización, lo que trae muchos problemas. En + D_ se define el orden de inicialización y es el mismo orden en que el + usuario importa los módulos. -Pruebas unitarias: - es posible incluir pequeñas pruebas unitarias en el lenguaje. Éstas son - ejecutadas (cuando no se compila en modo *release*) al comenzar el - programa, antes de que la función ``main()``. +Inicialización garantizada: + todas las variables son inicializadas por el lenguaje (a menos que el + usuario pida explícitamente que no lo sean). Siempre que sea posible se + elijen valores de inicialización que permitan saber al programador que + la variable no fue inicializada explícitamente, de manera de poder + detectar errores de manera temprana. - Ejemplo:: + Ejemplo:: - unittest { - Fecha fecha; - fecha.dia = 5; - assert (fecha.dia == 5); - assert (fecha); - } + double d; // inicializado a NaN + int x; // inicializado a 0 + Fecha f; // inicializado a null + byte[5] a; // inicializados todos los valores a 0 + long l = void; // NO inicializado (explícitamente) -Orden de construcción estática: - a diferencia de C++, D_ garantiza el orden de inicialización de los - módulos. Si bien en C++ no hay módulos si no unidades de compilación, es - posible que se ejecute código antes del ``main()`` en C++, si hay, por - ejemplo, instancias globales con un constructor definido. C++ no - garantiza un orden de inicialización, lo que trae muchos problemas. En D_ - se define el orden de inicialización y es el mismo orden en que el - usuario importa los módulos. +*RAII* (*Resource Adquisition Is Initialization*): + es una técnica muy utilizada en C++ que consiste en reservar recursos + por medio de la construcción de un objeto y liberarlos cuando se libera + éste. Al llamarse al destructor de manera automática cuando se sale del + *scope*, se asegura que el recurso será liberado también. -Inicialización garantizada: - todas las variables son inicializadas por el lenguaje (a menos que el - usuario pida explícitamente que no lo sean). Siempre que sea posible se - elijen valores de inicialización que permitan saber al programador que la - variable no fue inicializada explícitamente, de manera de poder detectar - errores de manera temprana. + Esta técnica es la base para desarrollar código seguro en cuanto + a excepciones (*exception-safe*) [SUTT99]_. - Ejemplo:: + En D_ no es tan común utilizar *RAII* dada la existencia del recolector + de basura (en la mayoría de los casos el recurso a administrar es + sencillamente memoria). Sin embargo en los casos en donde es necesario, + puede utilizarse *RAII* mediante la utilización de la palabra reservada + ``scope``, que limita la vida de un objeto un bloque de código. - double d; // inicializado a NaN - int x; // inicializado a 0 - Fecha f; // inicializado a null - byte[5] a; // inicializados todos los valores a 0 - long l = void; // NO inicializado (explícitamente) + Ejemplo:: -*RAII* (*Resource Adquisition Is Initialization*): - es una técnica muy utilizada en C++ que consiste en reservar - recursos por medio de la construcción de un objeto y liberarlos cuando - se libera éste. Al llamarse al destructor de manera automática cuando - se sale del *scope*, se asegura que el recurso será liberado también. - - Esta técnica es la base para desarrollar código seguro en cuanto a - excepciones (*exception-safe*) [SUTT99]_. - - En D_ no es tan común utilizar *RAII* dada la existencia del recolector - de basura (en la mayoría de los casos el recurso a administrar es - sencillamente memoria). Sin embargo en los casos en donde es necesario, - puede utilizarse *RAII* mediante la utilización de la palabra reservada - ``scope``, que limita la vida de un objeto un bloque de código. - - Ejemplo:: - - class Archivo { - this() { /* adquiere recurso */ } - ~this() { /* libera recurso */ } - } - void f() { - scope Archivo archivo = new Archivo; - // uso de archivo - } // en este punto se llama al destructor de archivo + class Archivo { + this() { /* adquiere recurso */ } + ~this() { /* libera recurso */ } + } + void f() { + scope Archivo archivo = new Archivo; + // uso de archivo + } // en este punto se llama al destructor de archivo Guardias de bloque (*scope guards*): - además de poder limitar la vida de una instancia a un *scope*, es posible - especificar un bloque de código arbitrario a ejecutar al abandonar un - *scope*, ya sea cuando se sale del *scope* normalmente o por una falla. - - Ejemplo:: - - int f(Lock lock) { - lock.lock(); - scope (exit) - lock.unlock(); // ejecutado siempre que salga de f() - auto trans = new Transaccion; - scope (success) - trans.commit(); // ejecutado si sale con "return" - scope (failure) - trans.rollback(); // ejecutado si sale por una excepción - if (condicion) - throw Exception("error"); // ejecuta lock.unlock() y trans.rollback() - else if (otra_condicion) - return 5; // ejecuta lock.unlock() y trans.commit() - return 0; // ejecuta lock.unlock() y trans.commit() - } - - Esta es una nueva forma de poder escribir código *exception-safe*, aunque - el programador debe tener un poco más de cuidado de especificar las - acciones a ejecutar al finalizar el *scope*. + además de poder limitar la vida de una instancia a un *scope*, es + posible especificar un bloque de código arbitrario a ejecutar al + abandonar un *scope*, ya sea cuando se sale del *scope* normalmente + o por una falla. + + Ejemplo:: + + int f(Lock lock) { + lock.lock(); + scope (exit) + lock.unlock(); // ejecutado siempre que salga de f() + auto trans = new Transaccion; + scope (success) + trans.commit(); // ejecutado si sale con "return" + scope (failure) + trans.rollback(); // ejecutado si sale por una excepción + if (condicion) + throw Exception("error"); // ejecuta lock.unlock() y trans.rollback() + else if (otra_condicion) + return 5; // ejecuta lock.unlock() y trans.commit() + return 0; // ejecuta lock.unlock() y trans.commit() + } + + Esta es una nueva forma de poder escribir código *exception-safe*, + aunque el programador debe tener un poco más de cuidado de especificar + las acciones a ejecutar al finalizar el *scope*. Primitivas de sincronización de hilos: - la programación multi-hilo está directamente soportada por el lenguaje, - y se provee una primitiva de sincronización al igual que Java_. La - palabra reservada ``synchronized`` puede aparecer como modificador de - métodos (en cuyo caso se utiliza un *lock* por clase para sincronizar) - o como una sentencia, en cuyo caso se crea un *lock* global por cada - bloque ``synchronized`` a menos que se especifique sobre qué objeto - realizar la sincronización. Por ejemplo:: - - class Foo { - synchronized void bar() { /* cuerpo */ } - } - - Es equivalente a:: - - class Foo { - void bar() { - synchronized (this) { /* cuerpo */ } + la programación multi-hilo está directamente soportada por el lenguaje, + y se provee una primitiva de sincronización al igual que Java_. La + palabra reservada ``synchronized`` puede aparecer como modificador de + métodos (en cuyo caso se utiliza un *lock* por clase para sincronizar) + o como una sentencia, en cuyo caso se crea un *lock* global por cada + bloque ``synchronized`` a menos que se especifique sobre qué objeto + realizar la sincronización. Por ejemplo:: + + class Foo { + synchronized void bar() { /* cuerpo */ } + } + + Es equivalente a:: + + class Foo { + void bar() { + synchronized (this) { /* cuerpo */ } + } } - } Compiladores @@ -1099,4 +1104,4 @@ se terminó desarrollando el trabajo utilizando LDC_. .. include:: links.rst -.. vim: set ts=2 sts=2 sw=2 et tw=75 : +.. vim: set ts=3 sts=3 sw=3 et tw=78 :