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
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*),
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
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
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
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)``.
+ 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:
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.
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
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.
+ 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
- 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):
-
- 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
.. 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 :