D , también conocido como dlang , es un lenguaje de programación de sistemas multiparadigma creado por Walter Bright en Digital Mars y lanzado en 2001. Andrei Alexandrescu se unió al esfuerzo de diseño y desarrollo en 2007. Aunque se originó como una reingeniería de C++ , D es ahora un lenguaje muy diferente. A medida que se ha desarrollado, se ha inspirado en otros lenguajes de programación de alto nivel . En particular, ha sido influenciado por Java , Python , Ruby , C# y Eiffel .
La referencia del lenguaje D lo describe de la siguiente manera:
D es un lenguaje de programación de sistemas de propósito general con una sintaxis similar a C que se compila en código nativo. Tiene tipado estático y admite la gestión de memoria tanto automática (recolección de basura) como manual. Los programas D están estructurados como módulos que se pueden compilar por separado y vincular con bibliotecas externas para crear bibliotecas nativas o ejecutables. [11]
En general, D no es compatible con el código fuente de C y C++. Sin embargo, cualquier código que sea legal tanto en C como en D debería comportarse de la misma manera.
Al igual que C++, D tiene cierres , funciones anónimas , ejecución de funciones en tiempo de compilación , rangos, conceptos de iteración de contenedores integrados e inferencia de tipos . Las sintaxis de declaración, instrucción y expresión de D también coinciden estrechamente con las de C++.
A diferencia de C++, D también implementa diseño por contrato , módulos , recolección de basura , matrices de primera clase , segmentación de matrices , funciones anidadas y evaluación diferida . D utiliza herencia simple al estilo Java con interfaces y mixins en lugar de herencia múltiple al estilo C++ .
D es un lenguaje de programación de sistemas. Al igual que C++, y a diferencia de los lenguajes de aplicación como Java y C# , D admite la programación de bajo nivel , incluido el ensamblador en línea . El ensamblador en línea permite a los programadores ingresar código ensamblador específico de la máquina dentro del código D estándar. Los programadores de sistemas utilizan este método para acceder a las características de bajo nivel del procesador que se necesitan para ejecutar programas que interactúan directamente con el hardware subyacente , como sistemas operativos y controladores de dispositivos . La programación de bajo nivel también se utiliza para escribir código de mayor rendimiento que el que produciría un compilador .
D admite la sobrecarga de funciones y la sobrecarga de operadores . Los símbolos ( funciones , variables , clases ) se pueden declarar en cualquier orden; no se necesitan declaraciones anticipadas .
En D, las cadenas de caracteres de texto son matrices de caracteres, y las matrices en D tienen límites controlados. [12] D tiene tipos de primera clase para números complejos e imaginarios. [13]
D admite cinco paradigmas de programación principales :
La programación imperativa en D es casi idéntica a la de C. Las funciones, datos, instrucciones, declaraciones y expresiones funcionan igual que en C, y se puede acceder directamente a la biblioteca de ejecución de C. Por otro lado, a diferencia de C, la foreach
construcción de bucle de D permite realizar bucles sobre una colección. D también permite funciones anidadas , que son funciones que se declaran dentro de otra función y que pueden acceder a las variables locales de la función que las contiene .
importar std . stdio ; void main () { int multiplicador = 10 ; int escalado ( int x ) { return x * multiplicador ; } foreach ( i ; 0 .. 10 ) { writefln ( "Hola, mundo %d! scaled = %d" , i , scaled ( i )); } }
La programación orientada a objetos en D se basa en una única jerarquía de herencia , en la que todas las clases se derivan de la clase Object. D no admite la herencia múltiple; en su lugar, utiliza interfaces de estilo Java , que son comparables a las clases abstractas puras de C++, y mixins , que separan la funcionalidad común de la jerarquía de herencia. D también permite la definición de métodos estáticos y finales (no virtuales) en interfaces.
Las interfaces y la herencia en D admiten tipos covariantes para los tipos de retorno de métodos anulados.
D admite el reenvío de tipos, así como el envío dinámico personalizado opcional .
Las clases (e interfaces) en D pueden contener invariantes que se verifican automáticamente antes y después de la entrada a métodos públicos, de acuerdo con la metodología de diseño por contrato .
Muchos aspectos de las clases (y estructuras) se pueden introspeccionar automáticamente en tiempo de compilación (una forma de programación reflexiva (reflexión) usando type traits
) y en tiempo de ejecución (RTTI / TypeInfo
), para facilitar la generación de código genérico o automática de código (generalmente usando técnicas de tiempo de compilación).
D admite características de programación funcional como literales de función , cierres , objetos recursivamente inmutables y el uso de funciones de orden superior . Existen dos sintaxis para funciones anónimas, que incluyen una forma de múltiples declaraciones y una notación de expresión única "abreviada": [14]
int function ( int ) g ; g = ( x ) { return x * x ; }; // expresión larga g = ( x ) => x * x ; // expresión abreviada
Hay dos tipos integrados para los literales de función, function
, que es simplemente un puntero a una función asignada a la pila, y delegate
, que también incluye un puntero al marco de pila relevante , el "entorno" circundante, que contiene las variables locales actuales. La inferencia de tipos se puede utilizar con una función anónima, en cuyo caso el compilador crea un delegate
a menos que pueda probar que un puntero de entorno no es necesario. Del mismo modo, para implementar un cierre, el compilador coloca las variables locales incluidas en el montón solo si es necesario (por ejemplo, si otra función devuelve un cierre y sale del ámbito de esa función). Al utilizar la inferencia de tipos, el compilador también agregará atributos como pure
y nothrow
al tipo de una función, si puede probar que se aplican.
Otras características funcionales, como currificación y funciones comunes de orden superior como map , filter y reduce, están disponibles a través de los módulos de la biblioteca estándar std.functional
y std.algorithm
.
importar std.stdio , std.algoritmo , std.rango ; vacío principal () { int [] a1 = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ]; int [] a2 = [ 6 , 7 , 8 , 9 ]; // debe ser inmutable para permitir el acceso desde dentro de una función pura inmutable pivot = 5 ; int mySum ( int a , int b ) pure nothrow /* función pura */ { if ( b <= pivot ) // referencia al ámbito envolvente return a + b ; de lo contrario return a ; } // pasar un delegado (cierre) auto result = reduce ! mySum ( chain ( a1 , a2 )); writeln ( "Resultado: " , result ); // Resultado: 15 // pasar un literal delegado result = reduce !(( a , b ) => ( b <= pivot ) ? a + b : a )( chain ( a1 , a2 )); writeln ( "Resultado: " , resultado ); // Resultado: 15 }
Como alternativa, las composiciones de funciones anteriores se pueden expresar utilizando la sintaxis de llamada de función uniforme (UFCS) para una lectura de izquierda a derecha más natural:
auto resultado = a1 . chain ( a2 ). reduce ! mySum (); writeln ( "Resultado: " , resultado ); resultado = a1 . cadena ( a2 ). reduce !(( a , b ) => ( b <= pivote ) ? a + b : a )(); writeln ( "Resultado: " , resultado );
Los conceptos de programación paralela se implementan en la biblioteca y no requieren soporte adicional del compilador. Sin embargo, el sistema de tipos D y el compilador garantizan que el uso compartido de datos se pueda detectar y gestionar de forma transparente.
importar std . stdio : writeln ; importar std . range : iota ; importar std . parallelism : paralelo ; void main () { foreach ( i ; iota ( 11 ). parallel ) { // El cuerpo del bucle foreach se ejecuta en paralelo para cada i writeln ( "processing " , i ); } }
iota(11).parallel
es equivalente a std.parallelism.parallel(iota(11))
utilizar UFCS.
El mismo módulo también admite taskPool
operaciones que pueden utilizarse para la creación dinámica de tareas paralelas, así como operaciones de estilo map-filter-reduce y fold en rangos (y matrices), lo que resulta útil cuando se combina con operaciones funcionales. std.algorithm.map
devuelve un rango evaluado de forma diferida en lugar de una matriz. De esta manera, cada tarea de trabajo calcula los elementos en paralelo de forma automática.
importar std . stdio : writeln ; importar std . algoritmo : mapa ; importar std . rango : iota ; importar std . paralelismo : taskPool ; /* En Intel i7-3930X y gdc 9.3.0: * 5140 ms usando std.algorithm.reduce * 888 ms usando std.parallelism.taskPool.reduce * * En AMD Threadripper 2950X y gdc 9.3.0: * 2864 ms usando std.algorithm.reduce * 95 ms usando std.parallelism.taskPool.reduce */ void main () { auto nums = iota ( 1.0 , 1_000_000_000.0 ); auto x = taskPool . reduce ! "a + b" ( 0.0 , map ! "1.0 / (a * a)" ( nums ) ); writeln ( "Suma: " , x ); }
La concurrencia está completamente implementada en la biblioteca y no requiere soporte del compilador. Son posibles implementaciones y metodologías alternativas para escribir código concurrente. El uso del sistema de tipado D ayuda a garantizar la seguridad de la memoria.
importar std . stdio , std . concurrency , std . variant ; vacío foo () { bool cont = verdadero ; mientras ( cont ) { recibir ( // Los delegados se utilizan para hacer coincidir el tipo de mensaje. ( int msg ) => writeln ( "int received: " , msg ), ( Tid sender ) { cont = false ; sender . send (- 1 ); }, ( Variante v ) => writeln ( "huh?" ) // La variante coincide con cualquier tipo ); } } void main () { auto tid = spawn (& foo ); // genera un nuevo hilo que ejecuta foo() foreach ( i ; 0 .. 10 ) tid.send ( i ); // envía algunos números enteros tid . send ( 1.0f ); // envía un float tid . send ( "hello" ); // envía una cadena tid . send ( thisTid ); // envía una estructura (Tid) recibir (( int x ) => writeln ( "El hilo principal recibió el mensaje: " , x )); }
La metaprogramación se apoya en plantillas, ejecución de funciones en tiempo de compilación, tuplas y combinaciones de cadenas. Los siguientes ejemplos demuestran algunas de las características de tiempo de compilación de D.
Las plantillas en D se pueden escribir en un estilo más imperativo en comparación con el estilo funcional de C++ para plantillas. Esta es una función regular que calcula el factorial de un número:
factorial ulong ( ulong n ) { si ( n < 2 ) devuelve 1 ; de lo contrario, devuelve n * factorial ( n - 1 ); }
Aquí, static if
se demuestra el uso de la construcción condicional de tiempo de compilación de D, para construir una plantilla que realiza el mismo cálculo utilizando un código similar al de la función anterior:
plantilla Factorial ( ulong n ) { estático si ( n < 2 ) enumeración Factorial = 1 ; de lo contrario enumeración Factorial = n * Factorial !( n - 1 ); }
En los dos ejemplos siguientes, se utilizan la plantilla y la función definidas anteriormente para calcular factoriales. No es necesario especificar explícitamente los tipos de constantes, ya que el compilador infiere sus tipos a partir de los lados derechos de las asignaciones:
enumeración fact_7 = Factorial !( 7 );
Este es un ejemplo de ejecución de función en tiempo de compilación (CTFE). Las funciones comunes se pueden utilizar en expresiones constantes en tiempo de compilación siempre que cumplan con ciertos criterios:
enumeración fact_9 = factorial ( 9 );
La std.string.format
función realiza printf
un formateo de datos similar (también en tiempo de compilación, a través de CTFE), y el pragma "msg" muestra el resultado en tiempo de compilación:
importar std . string : formato ; pragma ( msg , formato ( "7! = %s" , hecho_7 )); pragma ( msg , formato ( "9! = %s" , hecho_9 ));
Los mixins de cadenas, combinados con la ejecución de funciones en tiempo de compilación, permiten la generación de código D mediante operaciones de cadenas en tiempo de compilación. Esto se puede utilizar para analizar lenguajes específicos del dominio , que se compilarán como parte del programa:
import FooToD ; // módulo hipotético que contiene una función que analiza el código fuente de Foo // y devuelve el código D equivalente void main () { mixin ( fooToD ( import ( "example.foo" ))); }
La memoria se suele gestionar con recolección de basura , pero es posible que objetos específicos se finalicen inmediatamente cuando quedan fuera del alcance. Esto es lo que utilizan la mayoría de los programas y bibliotecas escritos en D.
En caso de que se necesite más control sobre el diseño de la memoria y un mejor rendimiento, es posible una gestión explícita de la memoria utilizando el operador sobrecargado new
, llamando directamente a malloc y free de C , o implementando esquemas de asignador personalizados (es decir, en pila con respaldo, asignación de estilo RAII, conteo de referencias, conteo de referencias compartidas). La recolección de basura se puede controlar: los programadores pueden agregar y excluir rangos de memoria para que no sean observados por el recolector, pueden deshabilitar y habilitar el recolector y forzar un ciclo de recolección generacional o completo. [15] El manual proporciona muchos ejemplos de cómo implementar diferentes esquemas de gestión de memoria altamente optimizados para cuando la recolección de basura es inadecuada en un programa. [16]
En las funciones, struct
las instancias se asignan de forma predeterminada en la pila, mientras que class
las instancias se asignan de forma predeterminada en el montón (con solo referencia a la instancia de clase que está en la pila). Sin embargo, esto se puede cambiar para las clases, por ejemplo, utilizando la plantilla de la biblioteca estándar std.typecons.scoped
o utilizando new
estructuras for y asignando a un puntero en lugar de una variable basada en valores. [17]
En las funciones, se asignan matrices estáticas (de tamaño conocido) en la pila. Para matrices dinámicas, se puede utilizar la core.stdc.stdlib.alloca
función (similar a alloca
C) para asignar memoria en la pila. El puntero devuelto se puede utilizar (reconvertir) en una matriz dinámica (tipificada) mediante una porción (sin embargo, se debe evitar cambiar el tamaño de la matriz, incluida la adición de datos; y por razones obvias, no se deben devolver desde la función). [17]
Una scope
palabra clave se puede utilizar para anotar partes del código, pero también variables y clases/estructuras, para indicar que deben destruirse (se debe llamar al destructor) inmediatamente al salir del ámbito. La memoria que se desasigne también depende de la implementación y de las diferencias entre clases y estructuras. [18]
std.experimental.allocator
Contiene plantillas de asignadores modulares y componibles para crear asignadores personalizados de alto rendimiento para casos de uso especiales. [19]
SafeD [20]
es el nombre que se le da al subconjunto de D que se puede garantizar que es seguro para la memoria . Las funciones marcadas @safe
se verifican en tiempo de compilación para garantizar que no utilicen ninguna característica, como aritmética de punteros y conversiones no verificadas, que podrían provocar la corrupción de la memoria. Cualquier otra función llamada también debe marcarse como @safe
o @trusted
. Las funciones se pueden marcar @trusted
para los casos en los que el compilador no puede distinguir entre el uso seguro de una característica que está deshabilitada en SafeD y un posible caso de corrupción de la memoria. [21]
Inicialmente bajo los nombres de DIP1000 [22] y DIP25 [23] (ahora parte de la especificación del lenguaje [24] ), D proporciona protección contra ciertas construcciones mal formadas que involucran la duración de los datos.
Los mecanismos actuales en funcionamiento tratan principalmente con parámetros de función y memoria de pila; sin embargo, es una ambición declarada de los líderes del lenguaje de programación proporcionar un tratamiento más exhaustivo de los tiempos de vida dentro del lenguaje de programación D [25] (influenciado por las ideas del lenguaje de programación Rust ).
Dentro del código @safe, se verifica la duración de una asignación que involucra un tipo de referencia para garantizar que la duración del cesionario sea más larga que la del asignado.
Por ejemplo:
@safe void test () { int tmp = 0 ; // #1 int * rad ; // #2 rad = & tmp ; // Si se invierte el orden de las declaraciones de #1 y #2, esto falla. { int bad = 45 ; // La duración de vida de "bad" solo se extiende al ámbito en el que está definido. * rad = bad ; // Esto es válido. rad = & bad ; // La duración de vida de rad es más larga que bad, por lo tanto, esto no es válido. } }
Cuando se aplica a parámetros de función que son de tipo puntero o referencias, las palabras clave return y scope restringen la duración y el uso de ese parámetro.
El estándar del lenguaje dicta el siguiente comportamiento: [26]
A continuación se ofrece un ejemplo anotado.
@seguro :int * gp ; void thorin ( ámbito int *); void gloin ( int *); int * balin ( return ámbito int * p , ámbito int * q , int * r ) { gp = p ; // Error, p escapa a la variable global gp. gp = q ; // Error, q escapa a la variable global gp. gp = r ; // OK. thorin ( p ); // OK, p no escapa a thorin(). thorin ( q ); // OK. thorin ( r ); // OK. gloino ( p ); // Error, p escapa de gloin(). gloino ( q ); // Error, q escapa de gloin(). gloino ( r ); // Está bien que r escape de gloin(). devolver p ; // OK. devolver q ; // Error, no se puede devolver 'scope' q. devolver r ; // OK. }
Se admite la interfaz binaria de aplicación (ABI) de C , así como todos los tipos fundamentales y derivados de C, lo que permite el acceso directo al código y las bibliotecas de C existentes. Los enlaces de D están disponibles para muchas bibliotecas de C populares. Además, la biblioteca estándar de C es parte del D estándar.
En Microsoft Windows, D puede acceder al código del Modelo de objetos componentes (COM).
Siempre que se tenga en cuenta la gestión de memoria de forma adecuada, se pueden mezclar muchos otros lenguajes con D en un único binario. Por ejemplo, el compilador GDC permite vincular y entremezclar códigos de C, C++ y otros lenguajes compatibles, como Objective-C. El código D (funciones) también se puede marcar como que utiliza ABIs de C, C++ y Pascal y, por lo tanto, se puede pasar a las bibliotecas escritas en estos lenguajes como callbacks . De manera similar, se pueden intercambiar datos entre los códigos escritos en estos lenguajes de ambas formas. Esto suele restringir el uso a tipos primitivos, punteros, algunas formas de matrices, uniones , estructuras y solo algunos tipos de punteros de función.
Dado que muchos otros lenguajes de programación suelen proporcionar la API de C para escribir extensiones o ejecutar el intérprete de los lenguajes, D también puede interactuar directamente con estos lenguajes, utilizando enlaces estándar de C (con un archivo de interfaz de D delgado). Por ejemplo, existen enlaces bidireccionales para lenguajes como Python , [27] Lua [28] [29] y otros lenguajes, que a menudo utilizan métodos de generación de código en tiempo de compilación y reflexión de tipos en tiempo de compilación.
Para el código D marcado como extern(C++)
, se especifican las siguientes características:
Los espacios de nombres de C++ se utilizan a través de la sintaxis extern(C++, namespace)
donde espacio de nombres es el nombre del espacio de nombres de C++.
El lado C++
importar < imprimir > ; clase Base { público : virtual void print3i ( int a , int b , int c ) = 0 ; }; clase Derivada : pública Base { pública : int campo ; Derivada ( int campo ) : campo ( campo ) {} void print3i ( int a , int b , int c ) { std :: println ( "a = {}" , a ); std :: println ( "b = {}" , b ); std :: println ( "c = {}" , c ); } int mul ( int factor ); }; int Derivado::mul ( int factor ) { campo de retorno * factor ; } Derivado * createInstance ( int i ) { return new Derivado ( i ); } void deleteInstance ( Derivado *& d ) { eliminar d ; d = 0 ; }
El lado D
extern ( C ++) { clase abstracta Base { void print3i ( int a , int b , int c ); } clase Derivada : Base { int campo ; @disable this (); anular void print3i ( int a , int b , int c ); final int mul ( int factor ); } Derivado createInstance ( int i ); void deleteInstance ( ref Derivado d ); } vacío principal () { importar std . stdio ; auto d1 = crearInstancia ( 5 ) ; writeln ( d1.campo ) ; writeln ( d1.mul ( 4 ) ) ; Base b1 = d1 ; b1 . print3i ( 1 , 2 , 3 ); deleteInstance ( d1 ); assert ( d1 es nulo ); auto d2 = createInstance ( 42 ) ; writeln ( d2.campo ) ; deleteInstance ( d2 ); assert ( d2 es nulo ); }
El lenguaje de programación D tiene un subconjunto oficial conocido como " Better C ". [30] Este subconjunto prohíbe el acceso a las características de D que requieren el uso de bibliotecas de tiempo de ejecución distintas a las de C.
Habilitado a través de los indicadores del compilador "-betterC" en DMD y LDC, y "-fno-druntime" en GDC, Better C solo puede llamar al código D compilado bajo el mismo indicador (y código vinculado que no sea D), pero el código compilado sin la opción Better C puede llamar al código compilado con ella: esto, sin embargo, conducirá a comportamientos ligeramente diferentes debido a las diferencias en cómo C y D manejan las afirmaciones.
core.thread
)core.sync
Walter Bright comenzó a trabajar en un nuevo lenguaje en 1999. D se lanzó por primera vez en diciembre de 2001 [1] y alcanzó la versión 1.0 en enero de 2007. [31] La primera versión del lenguaje (D1) se concentró en los paradigmas imperativo, orientado a objetos y metaprogramación, [32] similar a C++.
Algunos miembros de la comunidad D no estaban satisfechos con Phobos, el entorno de ejecución y la biblioteca estándar oficiales de D , y crearon un entorno de ejecución y una biblioteca estándar alternativos llamados Tango. El primer anuncio público de Tango se produjo a los pocos días del lanzamiento de D 1.0. [33] Tango adoptó un estilo de programación diferente, adoptando la programación orientada a objetos y una alta modularidad. Al ser un proyecto dirigido por la comunidad, Tango estaba más abierto a las contribuciones, lo que le permitió progresar más rápido que la biblioteca estándar oficial. En ese momento, Tango y Phobos eran incompatibles debido a las diferentes API de soporte del entorno de ejecución (el recolector de basura, el soporte de subprocesos, etc.). Esto hizo imposible utilizar ambas bibliotecas en el mismo proyecto. La existencia de dos bibliotecas, ambas ampliamente utilizadas, ha dado lugar a una disputa significativa debido a que algunos paquetes utilizan Phobos y otros utilizan Tango. [34]
En junio de 2007, se lanzó la primera versión de D2. [35] El comienzo del desarrollo de D2 marcó la estabilización de D1. La primera versión del lenguaje se colocó en mantenimiento, y solo recibió correcciones y correcciones de errores de implementación. D2 introdujo cambios importantes en el lenguaje, comenzando con su primer sistema const experimental . Más tarde, D2 agregó muchas otras características del lenguaje, como cierres , pureza y soporte para los paradigmas de programación funcional y concurrente. D2 también resolvió problemas de la biblioteca estándar al separar el entorno de ejecución de la biblioteca estándar. La finalización de un puerto de D2 Tango se anunció en febrero de 2012. [36]
El lanzamiento del libro de Andrei Alexandrescu , El lenguaje de programación D , el 12 de junio de 2010, marcó la estabilización de D2, al que hoy en día se le conoce comúnmente simplemente como "D".
En enero de 2011, el desarrollo de D pasó de un sistema de seguimiento de errores y envío de parches a GitHub . Esto ha llevado a un aumento significativo de las contribuciones al compilador, al entorno de ejecución y a la biblioteca estándar. [37]
En diciembre de 2011, Andrei Alexandrescu anunció que D1, la primera versión del lenguaje, se discontinuaría el 31 de diciembre de 2012. [38] La versión final de D1, D v1.076, fue el 31 de diciembre de 2012. [39]
El código para el compilador oficial de D, el compilador Digital Mars D de Walter Bright, fue publicado originalmente bajo una licencia personalizada , que calificaba como fuente disponible pero no se ajustaba a la Definición de Código Abierto . [40] En 2014, el front-end del compilador fue re-licenciado como código abierto bajo la Licencia de Software Boost . [3] Este código re-licenciado excluía el back-end, que había sido desarrollado parcialmente en Symantec . El 7 de abril de 2017, todo el compilador se puso a disposición bajo la licencia Boost después de que Symantec diera permiso para re-licenciar también el back-end. [4] [41] [42] [43] El 21 de junio de 2017, el lenguaje D fue aceptado para su inclusión en GCC. [44]
La mayoría de las implementaciones actuales de D se compilan directamente en código de máquina .
Compiladores listos para producción:
Compiladores de juguetes y de prueba de concepto:
Usando los compiladores y cadenas de herramientas anteriores, es posible compilar programas D para apuntar a muchas arquitecturas diferentes, incluyendo IA-32 , amd64 , AArch64 , PowerPC , MIPS64 , DEC Alpha , Motorola m68k , SPARC , s390 , WebAssembly . Los principales sistemas operativos soportados son Windows y Linux , pero varios compiladores también soportan Mac OS X , FreeBSD , NetBSD , AIX , Solaris/OpenSolaris y Android , ya sea como host o destino, o ambos. El destino WebAssembly (soportado a través de LDC y LLVM) puede operar en cualquier entorno WebAssembly, como el navegador web moderno ( Google Chrome , Mozilla Firefox , Microsoft Edge , Apple Safari ) o máquinas virtuales Wasm dedicadas.
Los editores y entornos de desarrollo integrados (IDE) que admiten el resaltado de sintaxis y la finalización parcial de código para el lenguaje incluyen SlickEdit , Emacs , vim , SciTE , Smultron , Zeus, [56] y Geany , entre otros. [57]
Existen IDE de código abierto para Windows , algunos escritos en D, como Poseidon, [70] D-IDE, [71] y Entice Designer. [72]
Las aplicaciones D pueden depurarse utilizando cualquier depurador de C/C++, como GNU Debugger (GDB) o WinDbg , aunque el soporte para varias características específicas del lenguaje D es extremadamente limitado. En Windows, los programas D pueden depurarse utilizando Ddbg, o herramientas de depuración de Microsoft (WinDBG y Visual Studio), después de haber convertido la información de depuración utilizando cv2pdb. El depurador ZeroBUGS Archivado el 23 de diciembre de 2017 en Wayback Machine para Linux tiene soporte experimental para el lenguaje D. Ddbg puede utilizarse con varios IDE o desde la línea de comandos; ZeroBUGS tiene su propia interfaz gráfica de usuario (GUI).
DustMite es una herramienta para minimizar el código fuente D, útil para encontrar problemas con el compilador o las pruebas. [73]
dub es un popular administrador de paquetes y compilaciones para aplicaciones y bibliotecas D, y a menudo está integrado en el soporte IDE. [74]
Este programa de ejemplo imprime sus argumentos de línea de comandos. La main
función es el punto de entrada de un programa en D y args
es una matriz de cadenas que representan los argumentos de la línea de comandos. A string
en D es una matriz de caracteres, representada por immutable(char)[]
.
importar std . stdio : writefln ; void principal ( cadena [] argumentos ) { foreach ( i , arg ; argumentos ) writefln ( "args[%d] = '%s'" , i , arg ); }
La foreach
instrucción puede iterar sobre cualquier colección. En este caso, produce una secuencia de índices ( i
) y valores ( arg
) a partir de la matriz args
. Los tipos del índice i
y del valor arg
se infieren a partir del tipo de la matriz args
.
A continuación se muestran varias capacidades de D y ventajas y desventajas del diseño de D en un programa corto. Itera sobre las líneas de un archivo de texto llamado words.txt
, que contiene una palabra diferente en cada línea, e imprime todas las palabras que son anagramas de otras palabras.
importar std.stdio , std.algoritmo , std.rango , std.cadena ; vacío principal () { dstring [] [ dstring ] firma2palabras ; foreach ( dchar [] w ; líneas ( Archivo ( "words.txt" ))) { w = w .chomp ( ). toLower (); firma inmutable = w . dup . sort (). release (). idup ; firma2palabras [ firma ] ~= w . idup ; } foreach ( palabras ; firma2palabras ) { si ( palabras . longitud > 1 ) writeln ( palabras . join ( " " )); }}
signature2words
es una matriz asociativa incorporada que asigna claves dstring (32 bits/char) a matrices de dstrings. Es similar a defaultdict(list)
en Python .lines(File())
produce líneas de forma diferida, con la nueva línea. Luego se debe copiar con idup
para obtener una cadena que se utilizará para los valores de la matriz asociativa (la idup
propiedad de las matrices devuelve un duplicado inmutable de la matriz, lo cual es necesario ya que el dstring
tipo es en realidad immutable(dchar)[]
). Las matrices asociativas integradas requieren claves inmutables.~=
operador agrega una nueva dstring a los valores de la matriz dinámica asociada.toLower
, join
y chomp
son funciones de cadena que D permite usar con una sintaxis de método. El nombre de tales funciones suele ser similar a los métodos de cadena de Python. toLower
Convierte una cadena a minúsculas, join(" ")
une una matriz de cadenas en una sola cadena usando un solo espacio como separador y chomp
elimina una nueva línea del final de la cadena si hay una presente. w.dup.sort().release().idup
Es más legible, pero equivalente a, release(sort(w.dup)).idup
por ejemplo. Esta característica se llama UFCS (Uniform Function Call Syntax) y permite extender cualquier tipo de paquete integrado o de terceros con una funcionalidad similar a un método. El estilo de escribir código como este a menudo se conoce como pipeline (especialmente cuando los objetos utilizados se calculan de forma diferida, por ejemplo, iteradores/rangos) o interfaz Fluent .sort
es una función de std.algorithm que ordena la matriz en su lugar, creando una firma única para las palabras que son anagramas entre sí. El release()
método en el valor de retorno de sort()
es útil para mantener el código como una sola expresión.foreach
itera sobre los valores de la matriz asociativa, es capaz de inferir el tipo de words
.signature
se asigna a una variable inmutable, se infiere su tipo.dchar[]
Se utiliza UTF-32 en lugar del UTF-8 char[]
normal, de lo contrario sort()
no se ordenará. Hay formas más eficientes de escribir este programa utilizando solo UTF-8.Entre las organizaciones notables que utilizan el lenguaje de programación D para proyectos se incluyen Facebook , [75] eBay , [76] y Netflix . [77]
D se ha utilizado con éxito para juegos AAA , [78] intérpretes de lenguaje, máquinas virtuales, [79] [80] un núcleo de sistema operativo , [81] programación de GPU , [82] desarrollo web , [83] [84] análisis numérico , [85] aplicaciones GUI , [86] [87] un sistema de información para pasajeros , [88] aprendizaje automático, [89] procesamiento de texto, servidores web y de aplicaciones e investigación.
El conocido grupo de piratas informáticos norcoreano conocido como Lazarus explotó CVE-2021-44228, también conocido como " Log4Shell ", para implementar tres familias de malware escritas en DLang. [90]
La falta de transparencia, agilidad y previsibilidad en el proceso de incorporación de correcciones de fallas y errores conocidos, y la dificultad de introducir cambios menores y mayores en el lenguaje D, se describen de manera inminente en un artículo de blog [91] de un ex colaborador. La aparente frustración descrita allí ha llevado a la bifurcación de OpenD [92] el 1 de enero de 2024.