www.digitalmars.com
Last update Wed May 31 17:11:57 2006

Visão Geral

O que é D?

D é uma linguagem de programação de sistemas e aplicações de propósito geral. Ela tem um nível mais alto que C++, mas retém a habilidade de escrever código de alto desempenho e interfacear diretamente com as API's do sistema operacional e com o hardware. D é bem-vindo para escrever programas de média e grande escala com milhões de linhas e com times de desenvolvedores. D é fácil de aprender, provê muitas facilidades para ajudar o programador, e é bem-vinda para agressiva técnologia de otimização do compilador.

D não é uma linguagem de script, nem uma linguagem interpretada. Não vem com uma VM, uma religião, pu uma filosofia de anulação. É uma linguagem prática para programadores práticos que precisam ter o trabalho feito rapidamente, com confiança, e deixar manutenção para trás, fácil para entender o código.

D é a culminação de décadas de experiência implementando compiladores para muitas linguagens  diversas, e tentando construir grandes projetos usando essas linguagens. D tira inspiração dessas outras linguagens (mais especialmente C++) e tempera-a com experiência e prática do mundo real. D Man

Por que D?

Por que afinal. Quem precisa de outra linguagem de programação?

A indústria de software teve um longo caminho desde que a linguagem D foi inventada. Muitos novos conceitos foram adicionados à linguagem com C++, mas compatibilidade passada com C foi mantida, incluindo compatibilidade com quase todas as fraquezas do projeto original. Houveram muitas tentativas de consertar essas fraquezas, mas a questão de compatibilidade frustrou-as. Enquanto isso, C e C++ sofreram constante acrescimo de novas características. Estas novos características devem caber na estrutura existente sem requerer reescrever coódigo antigo. O resultado final é muito complicado - o padrão C tem aproximadamente 500 páginas, eo o padrão C++ tem mais de 750 páginas! C++ é uma linguagem difícil e cara para implementar, resultando em variações de implementações que tornam frustrante escrever código C++ totalmente portável.

Programadores C++ tendem a programar em ilhas particulares da linguagem, isto é, tornando muito proficiente usar certas características enquanto evitar outros conjuntos de características. Enquanto o código é normalmente portável de compilador para compilador, pode ser difícil portá-lo de programador para programador. Uma grande força de C++ é que ele pode suportar muitos estilos de programação diferentes - mas em termos de muito uso, os estilos sobrepostos e contraditórios são um obstáculo.

C++ implementa coisas como vetores redimensionáveis e concatenação de strings como parte da biblioteca padrão, não como parte da linguagem núcleo. Não ser parte da linguagem núcleo tem muitas conseqüências de subdesempenho.

Pode o poder e capacidade de C++ ser extraído, re-projetado, e reformado em uma linguagem que seja simples, ortogonal e pratica? Pode tudo isso ser posto em um pacote que seja fácil para escritores de compiladores implmentarem corretamente , e que permita aos compiladores gerar códifo agrssivamente otimizado?

Moderna técnologica de compiladores tem progredido ao ponto onde características da linguagem para o propósito de compensar técnologias de compiladores primitivos podem ser omitidas. (um exemplo disso seria a palavra 'register' em C, um exemplo mais sútil é o pré-processador de macro em C.) Nós podemos confiar em modernas técnologias de otimização do compilador para não precisar de características da linguagem para obter código de qualidade dos compiladores primitivos.

Principais Metas de D

Características para Manter de C/C++

O olhar geral de  D é como C e C++. Isto torna mais fácil de aprender e portar código para  D. Transição de C/C++ para D deveria ser natural. O programador não terá que aprender um modo inteiramente novo de fazer as coisas.

Usar D não significa que o programador ficará restrito a uma vm (virtual machine - máquina virtual) especializada como a vm do Java vm ou a vm do Smalltalk. Não há uma vm para D, é um compilador direto que gera arquivos objeto linkaveis. D se conecta ao sistema operacional como C faz. As ferramentas familiares como make se encaixarão corretamente no desenvolvimento em D.

Características para Derrubar

D é para quem

D não é para quem


Principais Características de D

Essa seção lista algumas das mais interessantes características de D em varias categorias.

Programação Orientada a Objetos

Classes

A natureza orientada a objetos de D vem das classes. O modelo de herança é herança simples aumentada com interfaces. A classe Object é a raíz da hierarquia, então todas as classes implementam um conjunto comum de funcionalidades. Classes são instânciadas por referência, então não é necessário código complexo para limpeza após exceções.

Sobrecarga de Operadores

Classes podem ser feitas para trabalharem com operadores existentes para extender o sistema de tipos para suportar tipos novos. Um exemplo seria criar uma classe numerogrante e então sobrecarregar os operadores +, -, * e / para habilitar uso da sintaxe algébrica com eles.

Produtividade

Módulos

Arquivos fonte tem uma correspondência um-para-um com módulos. Ao invés de incluir (#include) o texto de um arquivo de declarações, apenas importa um módulo. Não há necessidade de se preocupar com múltiplas importações de um mesmo módulo, nem precisa envolver arquivos de cabeçalho com #ifndef/#endif ou #pragma once, etc.

Declaração vs Definição

C++ normalmente requer que funções e classes sejam declaradas duas vezes - a declaração que vai no arquivo de cabeçalho .h, e a definição que vai em um arquivo fonte .c. Isso é um erro propenso e um processo tedioso. Obviamente, os programadores deveriam precisar escrever isso uma vez, e o compilador deveria então extrair a informação de declaração e tornar disponível para importação simbólica. É exatamente assim que D trabalha.

Exemplo:

class ABC
{
int func() { return 7; }
static int z = 7;
}
int q;
Não há necessidade de uma definição separada das funções-membro, membros estáticos, externos, nem sintaxe desajeitada como:
int ABC::func() { return 7; }
int ABC::z = 7;
extern int q;
Nota: Claro que, em C++, funções triviais como { return 7; } são escritas "em linha" também, mas funções complexas não são. Em adição, se houver qualquer referência dianteira, as funções precisam estar prototipadas. O seguinte não irá trabalhar em C++:
class Foo
{
int foo(Bar *c) { return c->bar(); }
};

class Bar
{
public:
int bar() { return 3; }
};
Mas o código equivalente em D trabalhará:
class Foo
{
int foo(Bar c) { return c.bar; }
}

class Bar
{
int bar() { return 3; }
}
Se uma função em D é inlined (tornada inline) ou não é determinado pelas configurações do otimizador.

Gabaritos

Gabaritos em D oferecem um modo limpo de suportar programação genérica enquanto oferecem o poder da especialização parcial.

Arrays associativos

Arrays associativos são arrays com um tipo de dados arbitrário como índice ao invés de ser limitado a índices inteiros. Em essência, arrays associativos são tabelas hash. Arrays associativos tornam rápido, eficiênte, construir tabelas de símbolos livres de bugs.

Typedefs Reais

Typedefs em C e C++ são realmente apelidos, como nenhum novo tipo é realmente introduzido. D implementa typedefs reais, onde:
typedef int handle;
realmente cria um novo tipo handle. Verificação de tipo é obrigatória, e typedefs participam na sobrecarga de funções. Por exemplo:
int foo(int i);
int foo(handle h);

Documentação

Documentação tem sido feita tradicionalmente de duas formas - primeiro há comentários que documentam o que uma função faz, e então isso é reescrito em uma página html separada. E naturalmente, com o passar do tempo, eles tendem a divergir pois o código é atualizado e a documentação separada não. Ser capaz de gerar o requisito documentção diretamente dos comentários embutidos no arquivo fonte não só cortará pela metade o tempo necessário para preparar a documentação, mas será muito mais fácil manter a documentação sincronizada com o código. Ddoc é a especificação para o gerador de documentação de D. Essa página foi gerada pelo Ddoc também.

Embora existam ferramentas de terceiros para fazer isso em C++, eles tem muitas falhas sérias:

Funções

D tem o suporte esperado para todas as funções ordinárias, incluindo funções globais, funções sobrecarregadas, funções inline, funções-membro, funções virtuais, ponteiros para funções, etc. Além disso:

Funções Aninhadas

Funções podem ser aninhadas dentro de outras funções. Isso é altamente útil para fatoração de código, loclização, e técnicas de fechamento de funções.

Funções Literais

Funções anonimas podem ser embutidas diretamente em uma expressão.

Fechamentos Dinâmicos

Funções aninhadas e funções-membro de classes podem ser referenciadas com fechamentos (também chamados delegados), tornando programação genérica muito mais fácil e segura quanto ao tipo.

Parâmetros In, Out, Inout

Especificar isso não somente ajuda a fazer funções mais auto-documentáveis, elimina muito da necessidade de ponteiros sem sacrificar nada, e abre possibilidades para mais ajuda do compilador em encontrar problemas de codificação.

Tal torna possível para D interfacear diretamente com uma variedade maior de APIs estrangeiras. Não haveria necessidades de trabalhos paralelos como "Linguagem de Definição de Interfaces".

Arrays

Arrays em C tem várias faltas que podem ser corrigidas: Arrays em D vêm em diversas variedades: ponteiros, arrays estáticos, arrays dinâmicos e arrays associativos. Veja Arrays.

Strings

Manipulação de strings é tão comum, e tão confusa em C e C++, que é necessário suporte direto na linguagem. Linguagens modernas controlam concatenação de strings, cópia, etc., e assim faz D. Strings são uma consequencia direta do controle melhorado de arrays.

Gerenciamento de Recursos

Gerenciamento Automático de Memória

Alocação de memória em D é totalmente com coleta de lixo. Experiência empirica sugere que muitas das complicadas características de C++ são necessárias para administrar desalocação de memória. Com coleta de lixo, a linguagem se torna muito mais simples.

Há uma percepção que coleta de lixo é para programadores preguiçosos e principiantes. Eu me lembro quando isso foi dito sobre C++, depois de tudo, não há nada em C++ que não possa ser feito em C, ou em assembly quanto ao assunto.

Coleta de lixo elimina a faixa de código de alocação de memória tediosa e propensa a erros, necessária em C e C++. Isso não significa somente tempo de desenvolvimento muito mais rápido e  menores custos de manutenção, mas o programa resultante freqüentemente roda mais rápido!

Claro, coletores de lixo podem ser usados com C++, e eu tenho usado eles em meus próprio projetos em C++. Porém a linguagem não é amigável para coletores, impedindo a efetividade deles. Muitas das bibliotecas de código não podem ser usadas com coletores.

Para discussão mais completa sobre isso, veja coleta de lixo.

Gerenciamento Explícito de Memória

Apesar de D ser uma linguagem com coleta de lixo, as operações new e delete podem ser sobrecarregadas para classes particulares em que um alocador customizado pode ser usado.

RAII

RAII é uma moderna técnica de desenvolvimento de software para administrar alocação e desalocação de recursos. D suporta RAII de maneira controlada e previsível que é independente do ciclo de coleta de lixo.

Desempenho

Agregados Leves

D suporta estruturas simples no estilo C, para compatibilidade com estruturas de dados em C e porque elas são úteis quando todo poder das classes é desnecessário.

Inline Assembler

Device drivers, aplicações de systema de alto desempenho, sistema embutidos, e código especializado algumas vezes precisam imergir em linguagem assembly para ter o trabalho feito. Enquanto implementações de D não são necessárias para implementar o inline assembler, ele é definido como parte da linguagem. A maioria do código assembly necessário pode ser controlado com ele, obviando a necessidade para montadores separados ou DLLs.

Muitas implementações de D também suportarão funções intrínsicas análogo ao suporte de C para manipulação de portas de E/S, acesso direto a operações especiais com pontos flutuantes, etc.

Confiança

Uma linguagem moderna deveri fazer todo possível para ajudar o programador a eliminar bugs no código.Ajuda pode vir de muitas formas; de tornar fácil o uso de técnicas mais robustas, de compilar sinalizando código obviamente incorreto, de fazer checagens em tempo de execução.

Contratos

Programação de Contrato (inventada por B. Meyer) é uma técnica revolucionária para para ajudar assegurando a "justeza" do programa. A versão de Ddo DBC inclui pré-condições de funções, pós-condições de funções, classes invariantes, e contratos de asserção. Veja Contratos para a implementação de D.

Testes de Unidade

Testes de Unidade podem ser adiconados à classes, tal que sejam executados quando o programa for iniciado. Isso ajuda na verificando, em toda construção, que implementações de classes não foram quebradas sem advertência. Os testes de unidade fazem parte do código fonte para uma classe. Criá-los se torna parte natural do processo de desenvolvimento de classes, ao invés de jogar o código terminado na parede para o grupo de testes.

Testes de unidade podem ser feitos em outras linguagens, mas o resultado é um pouco confuso e as linguagens não estão se acomodando ao conceito. Teste de unidade é uma característica principal de D. Para funções de biblioteca isso trabalha muito bem, servindo para garantir que as funções realmente trabalhemq para ilustrar como usar as funções.

Considere os muitos códigos de bibliotecas e aplicações em C++ disponíveis para download na web. Quantas delas vem com *quaisquer* testes de verificação, isolando testes de unidade? Menos de 1%? A prática habitual é que se compila, assumimos que trabalha. E queremos saber se as advertências que o compilador cospe no processo são bugs reais ou apenas queixas sobre lêndeas.

Junto com Programação de Contrato, testes de unidade tornam D de longe a melhor linguagem para escrever aplicações robustas seguras. Teste de unidade nos da uma estimativa rápida-e-suja da qualidade de alguns pedaços de código D desconhecidos derrubados em nossos colos - se não há testes de unidade nem contratos, é inaceitável.

Atributos e Instruções de Depuração

Agora depurar é parte da sintaxe da linguagem. O código pode ser habilitado ou desabilitado em tempo de compilação, sem o uso de macros ou comando de pré-precessamento. A sintaxe de depuração habilita um reconhecimento consistente, portável e compreensível que o código fonte precisa ser capaz para gerar compilações de depuração e de lançamento.

Captura de Exceções

O modelo superior try-catch-finally é usado ao invé de apenas try-catch. Não há necessidade de criar objetos bobos apenas para ter o destrutor implemente apenas a semântica finally.

Sincronização

Programação multithread está ficando mais e mais popular, e D provê primitivas para construir programas multithread. Sincronização pode ser feita a nível de método ou objeto.
synchronized int func() { ... }
Funções sincronizadas permitem apenas um thread por vez executando a função.

A instrução de sincronização coloca um mutex ao redor do bloco de instruções, controlndo acesso pelo objeto ou global.

Suporte a Técnicas Robustas

Checagens em tempo de compilação

Checagem em tempo de execução

Compatibilidade

Precedencia de operadores e regras de avaliação

Retém D operadores de C e suas regras de precedência, regras de ordem de avaliação, e regras de promoção. Isso evita bugs sutis que poderiam surgir de ser usado do modo como C faz as coisas que tem uma grande dificuldade de achar bugs devido à diferente semântica.

Acesso direto a APIs em C

Não somente D tem tipos de dados que correspondem aos tipos de dados em C, ele prove acesso direto às funções em C. Não há necessidade de escrever funções envolvendo, nem código para copiar membros agregados um a um.

Suporte para todos os tipos de dados de C

Tornando possível interfacear com quaisquer APIs C  ou bibliotecas existentes. Esse suporte incui estruturas, uniões, enums, ponteiros, e todos os tipos do C99. D incui a capacidade de ajustar o alinhamento de membros de estruturs para assegurar compatibilidade com tipos de dados impostos externamente.

Captura de Exceções do SO

O mecanismo de captura de exceções de Dirá se conectar com o modo como o sistema operacional controla exceções em uma aplicação.

Uso de Ferramentas Existentes

D produz código em formato de arquivo objeto padrão, possibilitando o uso de montadores padrão, linkers, depuradores, profilers, compressores de executáveis, e outros analisadores, como também se unido com código escrito em outras linguagens.

Gerenciamento de Projeto 

Versoionando

D provê suporte nativo para geração de múltiplas versões de um programa a partir do mesmo texto. Isso substitui a técnica #if/#endif do pré-processador de C.

Condenação

Como código evolui com o tempo, alguns velgos códigos de biblioteca são substituídos por versões novas e melhores. As velhas versões devem estar disponível para suporte a código legado, mas elas podem ser marcadas como condenadas. Código que usa versões condenadas serão normalmente sinalizadas como ilegais, mas seriam permitidos por um interruptod do compilador. Isso tornará fácil para programadores de manutenção identificar quaisquer dependencias de código condenado.

Programa de Amostra em D (sieve.d)

/* Números primos do crivo de Eratosthenes */
bool[8191] flags;

int main()
{ int i, count, prime, k, iter;

printf("10 iterations\n");
for (iter = 1; iter <= 10; iter++)
{ count = 0;
flags[] = 1;
for (i = 0; i < flags.length; i++)
{ if (flags[i])
{ prime = i + i + 3;
k = i + prime;
while (k < flags.length)
{
flags[k] = 0;
k += prime;
}
count += 1;
}
}
}
printf ("\n%d primes", count);
return 0;
}