A geração do código fonte é um antipadrão?


118

Se algo pode ser gerado, então isso é dado, não código.

Dado isso, toda essa ideia de geração de código fonte não é um mal-entendido? Ou seja, se existe um gerador de código para alguma coisa, por que não fazer disso uma função adequada que possa receber os parâmetros necessários e executar a ação correta que o código "geraria" teria feito?

Se estiver sendo feito por motivos de desempenho, isso soa como uma falha do compilador.

Se estiver sendo feito para conectar dois idiomas, isso soa como uma falta de biblioteca de interface.

Estou faltando alguma coisa aqui?

Eu sei que o código também é dados. O que eu não entendo é, por que gerar código fonte ? Por que não transformá-lo em uma função que pode aceitar parâmetros e agir sobre eles?


11
Um termo relacionado com a geração de código é metaprogramação
UselesssCat

4
pt.wikipedia.org/wiki/Code_as_data , Lisp, FP, scripts, metaprogramação, Von Neumann / arquitetura modificada de Harvard etc. Foi coberto ad nauseam . tl; dr a distinção "código fonte" vs "código de saída", "código" vs "dados" etc. serve para simplificar as coisas. Eles nunca devem ser dogmáticos .
precisa saber é o seguinte

9
@Utku, os melhores motivos para gerar código geralmente estão relacionados ao desejo de fornecer uma descrição de nível superior ao que o idioma atual pode expressar . Se o compilador pode ou não criar um código eficiente, isso realmente não tem nada a ver. Considere os geradores de analisador - um lexer gerado por flexou um analisador gerado por bisonquase certamente será mais previsível, mais correto e mais rápido de executar do que os equivalentes escritos à mão em C; e construído a partir de muito menos código (também sendo menos trabalhoso para manter).
Charles Duffy

1
Talvez você venha de uma linguagem que não possui muitos elementos funcionais, mas em muitas linguagens as funções são de primeira classe - você pode distribuí-las; portanto, nesses tipos de linguagens, o código é um dado, e você pode tratá-lo dessa maneira.
Restioson

1
@Restioson em um código de idioma funcional não são dados. As funções de primeira classe significam exatamente isso: Funções são dados. E não necessariamente dados particularmente bons: você não pode necessariamente modificá-los um pouco (como transformar todas as adições dentro das funções em subtrações, por exemplo). Código são dados em idiomas homoicônicos. (a maioria das línguas homoicônicas possui funções de primeira classe. Mas o contrário não é verdadeiro.).
Lyndon White

Respostas:


150

A geração de código fonte é um antipadrão?

Tecnicamente, se gerarmos código, ele não será fonte, mesmo que seja um texto legível por humanos. Código-fonte é o código original, gerado por uma inteligência humana ou outra inteligência verdadeira, não traduzida mecanicamente e não é imediatamente reproduzível da fonte (verdadeira) (direta ou indiretamente).

Se algo puder ser gerado, esse dado será dado, não código.

Eu diria que tudo são dados de qualquer maneira. Mesmo código fonte. Especialmente código fonte! O código-fonte é apenas dados em uma linguagem projetada para realizar tarefas de programação. Esses dados devem ser traduzidos, interpretados, compilados, gerados conforme necessário em outras formas - de dados - algumas das quais são executáveis.

O processador executa instruções sem memória. A mesma memória usada para dados. Antes de o processador executar as instruções, o programa é carregado na memória como dados .

Então, tudo são dados , mesmo código .

Dado que [código gerado é dados], toda essa ideia de geração de código não é um mal-entendido?

É perfeitamente bom ter várias etapas na compilação, uma das quais pode ser a geração intermediária de código como texto.

Ou seja, se existe um gerador de código para alguma coisa, por que não fazer disso uma função adequada que possa receber os parâmetros necessários e executar a ação correta que o código "geraria" teria feito?

Essa é uma maneira, mas existem outras.


A saída da geração de código é o texto, que é algo projetado para ser usado por um ser humano.

Nem todas as formas de texto são destinadas ao consumo humano. Em particular, o código gerado (como texto) normalmente é destinado ao consumo do compilador e não ao consumo humano.


O código fonte é considerado o original: o mestre - o que editamos e desenvolvemos; o que arquivamos usando o controle do código-fonte. O código gerado, mesmo quando o texto é legível por humanos, normalmente é regenerado a partir do código-fonte original . O código gerado, de um modo geral, não precisa estar sob controle de origem, pois é regenerado durante a compilação.


1
Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
maple_shaft

65

Raciocínio prático

OK, eu sei que o código também é dados. O que eu não entendo é, por que gerar código fonte?

A partir desta edição, suponho que você esteja perguntando em um nível bastante prático, não teórico em Ciência da Computação.

O motivo clássico para gerar código-fonte em linguagens estáticas como Java foi que linguagens como essa simplesmente não vieram com ferramentas fáceis de usar na linguagem para fazer coisas muito dinâmicas. Por exemplo, nos dias de formação do Java, simplesmente não era possível criar facilmente uma classe com um nome dinâmico (combinando um nome de tabela de um banco de dados) e métodos dinâmicos (combinando atributos dessa tabela) com tipos de dados dinâmicos (combinando os tipos dos referidos atributos). Especialmente porque o Java coloca muita importância, ou seja, garante, na capacidade de detectar erros de tipo em tempo de compilação.

Portanto, nessa configuração, um programador pode criar apenas código Java e escrever muitas linhas de código manualmente. Freqüentemente, o programador descobre que sempre que uma tabela muda, ele precisa voltar e alterar o código para corresponder; e se ele esquecer isso, coisas ruins acontecem. Portanto, o programador chegará ao ponto em que ele escreve algumas ferramentas que fazem isso por ele. E, portanto, o caminho começa a gerar códigos cada vez mais inteligentes.

(Sim, você pode gerar o bytecode rapidamente, mas programar uma coisa dessas em Java não seria algo que um programador aleatório faria apenas entre escrever algumas linhas de código de domínio.)

Compare isso com linguagens muito dinâmicas, por exemplo, Ruby, que eu consideraria a antítese do Java em muitos aspectos (observe que estou dizendo isso sem valorizar qualquer uma das abordagens; elas são simplesmente diferentes). Aqui é 100% normal e padrão gerar dinamicamente classes, métodos etc. em tempo de execução e, o mais importante, o programador pode fazê-lo trivialmente no código, sem precisar ir no nível "meta". Sim, coisas como Ruby on Rails vêm com geração de código, mas descobrimos em nosso trabalho que basicamente o usamos como uma espécie de "modo tutorial" avançado para novos programadores, mas depois de um tempo fica supérfluo (pois há muito pouco código escrever nesse ecossistema que, quando você sabe o que está fazendo, a gravação manual fica mais rápida do que a limpeza do código gerado).

Estes são apenas dois exemplos práticos do "mundo real". Então você tem idiomas como LISP, onde o código é dados, literalmente. Por outro lado, em linguagens compiladas (sem um mecanismo de tempo de execução como Java ou Ruby), existe (ou não acompanhei os recursos modernos do C ++ ...) simplesmente nenhum conceito de definição de nomes de classe ou método em tempo de execução, portanto, na geração de código, o processo de construção é a ferramenta escolhida para a maioria das coisas (outros exemplos específicos de C / C ++ seriam flex, yacc etc.).


1
Eu acho que isso é melhor do que as respostas mais votadas. Em particular, o exemplo mencionado com Java e programação de banco de dados faz um trabalho muito melhor de realmente abordar por que a geração de código é usada e é uma ferramenta válida.
Panzercrisis

Atualmente, é possível em Java criar tabelas dinâmicas a partir de um banco de dados? Ou apenas usando um ORM?
Noumenon

"(ou não acompanhei os recursos modernos do C ++ ...)" certamente isso é possível no C ++ há mais de duas décadas, graças aos indicadores de função? Eu não testei, mas tenho certeza de que deve alocar um array de caracteres, preenchê-lo com código de máquina e converter um ponteiro para o primeiro elemento em um ponteiro de função e depois executá-lo? (Supondo que a plataforma de destino não tenha alguma medida de segurança para impedi-lo de fazer isso, o que pode muito bem ser feito.) #
052 Decha

1
"aloque um array de caracteres, preencha-o com o código da máquina e depois converta um ponteiro para o primeiro elemento em um ponteiro de função e execute-o?" Além de ser um comportamento indefinido, é o equivalente em C ++ de "gerar o bytecode rapidamente". Ele cai na mesma categoria de "não considerado por programadores comuns"
Caleth

1
@Pharap, "certamente isso é possível em C ++ há mais de duas décadas" ... tive que rir um pouco; faz duas décadas desde que codifiquei C ++ pela última vez. :) Mas minha frase sobre C ++ foi mal formulada de qualquer maneira. Eu mudei um pouco, agora deveria ficar mais claro o que eu quis dizer.
AnoE

44

por que gerar código?

Porque programar com cartões perfurados (ou códigos alternativos no bloco de notas ) é uma dor.

Se estiver sendo feito por motivos de desempenho, isso soa como uma falha do compilador.

Verdadeiro. Não me importo com desempenho, a menos que seja forçado.

Se estiver sendo feito para conectar dois idiomas, isso soa como uma falta de biblioteca de interface.

Hmm, não faço ideia do que você está falando.

Olha, é assim: o código-fonte gerado e retido é sempre e para sempre uma chatice. Existe apenas por uma razão. Alguém quer trabalhar em um idioma enquanto alguém insiste em trabalhar em outro e ninguém pode se incomodar em descobrir como interoperar entre eles, para que um deles descubra como transformar seu idioma favorito no idioma imposto para que eles possam fazer o que eles querem.

O que é bom até que eu tenha que mantê-lo. Nesse ponto, todos vocês podem morrer.

É um anti-padrão? Suspiro não. Muitos idiomas nem existiriam se não estivéssemos dispostos a dizer adeus às deficiências dos idiomas anteriores e gerar o código dos idiomas mais antigos é o número de novos idiomas iniciados.

É uma base de código que fica em meio retalhos de monstros Frankenstein convertidos que eu não suporto. O código gerado é um código intocável. Eu odeio olhar código intocável. No entanto, as pessoas continuam verificando. POR QUE? Você também pode estar verificando o executável.

Bem, agora estou reclamando. Meu argumento é que todos nós estamos "gerando código". É quando você trata o código gerado como código fonte que está me deixando louco. Só porque parece que o código fonte não o torna código fonte.


41
Se você o gerar, não é um código SOURCE. É um código intermediário. Eu vou chorar agora.
Candied_orange 29/11

65
ARG !!! Não importa o que parece !!! Texto, binário, DNA, se não é a FONTE, não é o que você deve tocar ao fazer alterações. Não é da conta de ninguém se meu processo de compilação tiver 42 idiomas intermediários pelos quais ele passa. Pare de tocá-los. Pare de verificá-los. Faça as alterações na fonte.
candied_orange

24
XML é texto e claramente não se destina ao consumo humano. :-)
Nick Keighley

38
@utku: "Se algo não é para ser consumido por um ser humano, não deve ser um texto": discordo completamente. Alguns exemplos de contra-argumento: o protocolo HTTP, codificações MIME, arquivos PEM - praticamente qualquer coisa que use base64 em qualquer lugar. Existem várias razões para codificar os dados em um fluxo seguro de 7 bits, mesmo que nenhum ser humano possa vê-los. Para não mencionar a muito maior espaço de coisas que normalmente um ser humano nunca deve interagir com, mas que eles podem querer ocasionalmente: arquivos de log, /etc/arquivos em Unix, etc.
Daniel Pryden

12
Não acho que "programação com cartões perfurados" signifique o que você pensa que significa. Eu estive lá, eu fiz isso, e sim, foi uma dor; mas não tem conexão com "código gerado". Um baralho de cartões perfurados é apenas outro tipo de arquivo - como um arquivo em disco ou um arquivo em fita ou um arquivo em um cartão SD. Antigamente, escrevíamos dados em baralhos de cartas e líamos dados deles. Portanto, se a razão pela qual geramos código é porque a programação com cartões perfurados é uma dor, então isso implica que a programação com qualquer tipo de armazenamento de dados é uma dor.
Solomon Slow

41

por que gerar código fonte

O caso de uso mais frequente para geradores de código com os quais tive que trabalhar em minha carreira foram geradores que

  • utilizou uma meta-descrição de alto nível para algum tipo de modelo de dados ou esquema de banco de dados como entrada (talvez um esquema relacional ou algum tipo de esquema XML)

  • e produziu código CRUD para placas de caldeira como classes de acesso a dados como saída, e talvez coisas adicionais, como SQLs ou documentação correspondentes.

O benefício aqui é que, a partir de uma linha de uma especificação de entrada curta, você obtém de 5 a 10 linhas de código depurável, com segurança de tipo e sem bugs (presumindo que a saída dos geradores de código esteja madura) que, de outra forma, seria necessário implementar e manter manualmente. Você pode imaginar o quanto isso reduz o esforço de manutenção e evolução.

Deixe-me também responder à sua pergunta inicial

A geração de código fonte é um antipadrão

Não, não a geração do código fonte propriamente dita, mas existem de fato algumas armadilhas. Conforme declarado em The Pragmatic Programmer , deve-se evitar o uso de um gerador de código quando ele produz código difícil de entender . Caso contrário, o aumento dos esforços para usar ou depurar esse código pode facilmente superar o esforço economizado ao não se escrever o código manualmente.

Gostaria também de acrescentar que, na maioria das vezes, é uma boa ideia separar fisicamente partes do código gerado do código escrito manualmente, de maneira que a re-geração não substitua nenhuma alteração manual. No entanto, também lidei com a situação mais de uma vez em que a tarefa era migrar algum código escrito no idioma antigo X para outro idioma mais moderno Y, com a intenção de manutenção posterior no idioma Y. Esse é um uso válido caso para geração de código única.


Eu concordo com esta resposta. Usando algo como Torque para java, eu posso gerar automaticamente arquivos de origem java, com campos correspondentes ao banco de dados sql. Isso torna as operações brutas muito mais fáceis. O principal benefício é o tipo de segurança, incluindo a possibilidade de fazer referência apenas aos campos existentes no banco de dados (preenchimento automático de agradecimento).
MTilsted

Sim, para idiomas de tipo estaticamente, esta é a parte importante: você pode garantir que seu código escrito à mão realmente se encaixe no código gerado.
Pa Elo Ebermann 29/11

"migre algum código escrito no idioma antigo" - mesmo assim, a geração única de código pode ser uma grande dor. Por exemplo, após algumas alterações manuais, você detecta um erro no gerador e precisa refazer a geração após a correção. Felizmente, git ou similar geralmente pode aliviar a dor.
Maaartinus

13

por que gerar código fonte?

Encontrei dois casos de uso para código gerado (em tempo de construção e nunca efetuado check-in):

  1. Gere automaticamente código padrão, como getters / setters, toString, equals e hashCode a partir de uma linguagem criada para especificar essas coisas (por exemplo, projeto lombok para Java)
  2. Gere automaticamente classes do tipo DTO a partir de algumas especificações de interface (REST, SOAP, qualquer que seja) para serem usadas no código principal. Isso é semelhante ao problema da ponte de idioma, mas acaba sendo mais limpo e simples, com melhor manipulação de tipos do que tentar implementar a mesma coisa sem classes geradas.

15
Código altamente repetitivo em idiomas inexpressivos. Por exemplo, tive que escrever um código essencial que fizesse o mesmo em muitas estruturas de dados semelhantes, mas não idênticas. Provavelmente poderia ter sido feito com algo como um modelo C ++ (ei, não é essa geração de código?). Mas eu estava usando C. A geração de código me salvou escrevendo muitos códigos quase idênticos.
Nick Keighley

1
@NickKeighley Talvez sua cadeia de ferramentas não estivesse permitindo que você usasse outra linguagem mais adequada?
Wilson

7
Normalmente, você não pode escolher sua linguagem de implementação. O projeto estava em C, isso não era uma opção.
Nick Keighley

1
@Wilson, os idiomas mais expressivos costumam usar geração de código (por exemplo, macros lisp, ruby ​​on rails), mas não precisam ser salvos como texto enquanto isso.
Pete Kirkham

4
Sim, a geração de código é essencialmente meta-programação. Linguagens como Ruby permitem que você faça metaprogramação na própria linguagem, mas C não o faz, então você precisa usar a geração de código.
22817 Sean Burton

13

Sussmann tinha muito a dizer sobre essas coisas em seu clássico "Estrutura e interpretação de programas de computador", principalmente sobre a dualidade código-dados.

Para mim, o principal uso da geração de código adhoc é usar um compilador disponível para converter um pouco de linguagem específica de domínio em algo que eu possa vincular em meus programas. Pense em BNF, pense em ASN1 (na verdade, não é feio), pense em planilhas de dicionário de dados.

Linguagens específicas de domínio trivial podem economizar muito tempo, e produzir algo que pode ser compilado por ferramentas de linguagem padrão é o caminho a seguir ao criar essas coisas, que você prefere editar, um analisador hackeado não trivial em qualquer idioma nativo que você seja escrita ou o BNF para um gerado automaticamente?

Ao enviar o texto que é alimentado para algum compilador do sistema, recebo toda a otimização e configuração específica do sistema, sem ter que pensar sobre isso.

Estou efetivamente usando a linguagem de entrada do compilador como apenas outra representação intermediária, qual é o problema? Os arquivos de texto não são inerentemente código fonte, eles podem ser um IR para um compilador e, se parecerem com C ou C ++ ou Java ou o que for, quem se importa?

Agora, se você estiver pensando que pode editar a SAÍDA do analisador de linguagem de brinquedos, o que desapontará claramente na próxima vez que alguém editar os arquivos de linguagem de entrada e reconstruir, a resposta é não comprometer o IR gerado automaticamente para o repositório, gerado pelo seu conjunto de ferramentas (e evite ter essas pessoas em seu grupo de desenvolvedores, elas geralmente são mais felizes trabalhando em marketing).

Isso não é tanto uma falha de expressividade em nossas línguas, mas uma expressão do fato de que às vezes você pode obter (ou massagear) partes da especificação em um formato que pode ser automaticamente convertido em código e que geralmente gera muito menos erros e seja muito mais fácil de manter. Se eu posso fornecer aos nossos técnicos de teste e configuração uma planilha que eles podem ajustar e uma ferramenta que eles executam que pega esses dados e cospe um arquivo hexadecimal completo para o flash na minha ECU, é uma enorme economia de tempo com a tradução manual de alguém a configuração mais recente em um conjunto de constantes no idioma do dia (completo com erros de digitação).

A mesma coisa com a construção de modelos no Simulink e a geração de C com RTW e a compilação para o destino com qualquer ferramenta que faça sentido, o C intermediário é ilegível, e daí? O material de alto nível do Matlab RTW precisa conhecer apenas um subconjunto de C, e o compilador C cuida dos detalhes da plataforma. O único momento em que um humano precisa vasculhar o C gerado é quando os scripts RTW têm um bug, e esse tipo de coisa é muito mais fácil de depurar com um IR legível nominalmente humano e com apenas uma árvore de análise binária.

É claro que você pode escrever essas coisas na saída de código de código ou mesmo executável, mas por que você faria isso? Temos ferramentas para converter um RI nessas coisas.


Isso é bom, mas eu acrescentaria que há uma troca ao determinar qual RI usar: usar C como RI torna algumas coisas mais fáceis e outras mais difíceis, quando comparado com, por exemplo, a linguagem assembly x86. A escolha é ainda mais significativa ao escolher entre, digamos, o código da linguagem Java e o bytecode Java, pois há muito mais operações que existem apenas em uma ou na outra linguagem.
Daniel Pryden

2
Mas a linguagem assembly X86 produz um IR ruim ao direcionar um núcleo ARM ou PPC! Todas as coisas são uma troca na engenharia, é por isso que chamam de Engenharia. Seria de esperar que as possibilidades do bytecode Java fossem um superconjunto estrito das possibilidades da linguagem Java, e que isso geralmente seja verdade à medida que você se aproxima do metal, independentemente da cadeia de ferramentas e de onde você injeta o RI.
Dan Mills

Ah, eu concordo totalmente: meu comentário foi em resposta ao seu parágrafo final, questionando por que você produziria bytecode ou alguma coisa de nível inferior - às vezes você precisa do nível inferior. (Em Java, especificamente, há um monte de coisas úteis que você pode fazer com bytecode que você não pode fazer na linguagem Java em si.)
Daniel Pryden

2
Não discordo, mas há um custo em usar um IR mais próximo do metal, não apenas em reduzida generalidade, mas no fato de que você geralmente acaba sendo responsável por mais da otimização de baixo nível realmente irritante. O fato de que geralmente hoje em dia pensamos em termos de otimização da escolha do algoritmo em vez da implementação é uma reflexão sobre o quão longe os compiladores chegaram, às vezes você precisa se aproximar bastante do metal nessas coisas, mas pense duas vezes antes de jogar fora os compiladores capacidade de otimizar usando um IR de nível muito baixo.
Dan Mills

1
"eles geralmente são mais felizes trabalhando em marketing" Catty, mas engraçado.
dmckee

13

Resposta pragmática: a geração de código é necessária e útil? Ele fornece algo que é realmente muito útil e necessário para a base de código proprietária ou parece apenas criar outra maneira de fazer as coisas de uma maneira que contribua com mais sobrecarga intelectual para resultados abaixo do ideal?

OK, eu sei que o código também é dados. O que eu não entendo é, por que gerar código? Por que não transformá-lo em uma função que pode aceitar parâmetros e agir sobre eles?

Se você precisar fazer essa pergunta e não houver uma resposta clara, provavelmente a geração de código é supérflua e apenas contribui com exotismo e uma grande sobrecarga intelectual para sua base de código.

Enquanto isso, se você usar algo como o OpenShadingLanguage: https://github.com/imageworks/OpenShadingLanguage

... então essas perguntas não precisam ser levantadas, pois são imediatamente respondidas pelos resultados impressionantes.

O OSL usa a estrutura do compilador LLVM para converter redes de sombreador em código de máquina em tempo real (apenas a tempo ou "JIT"), e no processo otimiza fortemente sombreadores e redes com conhecimento completo dos parâmetros do sombreador e outros valores de tempo de execução que não poderiam eram conhecidos quando os shaders foram compilados a partir do código fonte. Como resultado, estamos vendo nossas redes de sombreamento OSL executadas 25% mais rápido que os sombreadores equivalentes criados manualmente em C! (Foi assim que nossos shaders antigos funcionavam em nosso renderizador.)

Nesse caso, você não precisa questionar a existência do gerador de código. Se você trabalha nesse tipo de domínio VFX, sua resposta imediata é geralmente mais nas linhas de "cale a boca e pegue meu dinheiro!" ou "uau, também precisamos fazer algo assim".


converter redes shader em código de máquina . Isso soa como um compilador em vez de um gerador de código, não?
Utku

2
Basicamente, é necessária uma rede nodal que o usuário conecta e gera código intermediário que é compilado pelo JIT pelo LLVM. A distinção entre compilador e gerador de código é meio confusa. Você estava pensando mais nas linhas de recursos de geração de código em linguagens como modelos em C ++ ou no pré-processador C?

Eu estava pensando em qualquer gerador que produzisse código fonte.
precisa

Entendo, onde a produção ainda é para consumo humano, presumo. O OpenSL também gera código fonte intermediário, mas é um código de baixo nível que está próximo da montagem para o consumo de LLVM. Normalmente, não é um código que deve ser mantido (em vez disso, os programadores mantêm os nós usados ​​para gerar o código). Na maioria das vezes, acho que esses tipos de geradores de código têm mais probabilidade de serem abusados ​​do que úteis o suficiente para justificar seu valor, especialmente se você precisar regenerar constantemente o código como parte do seu processo de construção. Às vezes, eles ainda têm um lugar genuíno embora para colmatar as lacunas ...

... do (s) idioma (s) disponível (s) quando usado para um domínio específico. O QT tem um desses controversos com seu compilador de meta-objetos (MOC). O MOC reduz o padrão, normalmente necessário para fornecer propriedades e reflexão, sinais e slots e assim por diante em C ++, mas não a ponto de justificar claramente sua existência. Costumo pensar que o QT poderia ter sido melhor sem o fardo pesado da geração de código do MOC.

8

Não, gerar código intermediário não é um antipadrão. A resposta para a outra parte da sua pergunta, "Por que fazer isso?", É uma pergunta muito ampla (e separada), embora, de qualquer maneira, eu cite algumas razões.

Ramificações históricas de nunca ter código legível por humanos intermediário

Vamos usar C e C ++ como exemplos, pois estão entre os idiomas mais famosos.

Você deve observar que a procissão lógica da compilação do código C não gera código de máquina, mas código de montagem legível por humanos. Da mesma forma, os compiladores C ++ antigos costumavam compilar fisicamente o código C ++ no código C. Nessa cadeia de eventos, você pode compilar do código legível humano 1 para o código legível humano 2 para o código legível humano 3 para o código de máquina. "Por quê?" Por que não?

Se um código intermediário legível por humanos nunca foi gerado, talvez nem sequer tivéssemos C ou C ++. Essa é certamente uma possibilidade; as pessoas seguem o caminho de menor resistência a seus objetivos e, se alguma outra língua ganhou força primeiro por causa da estagnação do desenvolvimento de C, C poderia ter morrido enquanto ainda era jovem. Obviamente, você poderia argumentar "Mas talvez usássemos outra linguagem e talvez fosse melhor". Talvez, ou talvez fosse pior. Ou talvez todos ainda estivéssemos escrevendo na montagem.

Por que usar código intermediário legível por humanos?

  1. Às vezes, o código intermediário é desejado para que você possa modificá-lo antes da próxima etapa na construção. Admito que este ponto é o mais fraco.
  2. Às vezes, é porque o trabalho original não foi feito em nenhuma linguagem legível por humanos, mas em uma ferramenta de modelagem de GUI.
  3. Às vezes, você precisa fazer algo muito repetitivo, e a linguagem não deve atender ao que você está fazendo, porque é algo de nicho ou coisa tão complicada que não é necessário aumentar a complexidade ou a gramática da linguagem de programação apenas para acomodar você.
  4. Às vezes, você precisa fazer algo muito repetitivo e não há maneira possível de obter o que deseja no idioma de maneira genérica; ou não pode ser representado por ou conflita com a gramática do idioma.
  5. Um dos objetivos dos computadores é reduzir o esforço humano e, às vezes, é improvável que um código seja tocado novamente (baixa probabilidade de manutenção) pode ter um meta-código escrito para gerar seu código mais longo em um décimo do tempo; se eu posso fazê-lo em um dia em vez de 2 semanas e não é susceptível de ser mantido sempre, então é melhor eu gerá-lo - e na chance que alguém 5 anos a partir de agora está irritado porque eles realmente não precisa mantê-lo, em seguida, eles podem passar as 2 semanas escrevendo completamente se quiserem ou ficarem irritados com uma semana de manutenção do código estranho (mas ainda estamos com uma semana de antecedência nesse ponto), e é se essa manutenção precisar ser feita .
  6. Estou certo de que há mais razões pelas quais estou ignorando.

Exemplo

Já trabalhei em projetos antes em que o código precisa ser gerado com base em dados ou informações em algum outro documento. Por exemplo, um projeto tinha todas as suas mensagens de rede e dados constantes definidos em uma planilha e uma ferramenta que passaria pela planilha e geraria muitos códigos C ++ e Java que nos permitem trabalhar com essas mensagens.

Não estou dizendo que essa era a melhor maneira de montar esse projeto (eu não fazia parte de sua inicialização), mas era o que tínhamos, e eram centenas (talvez até milhares, não tenho certeza) de estruturas, objetos e constantes que estavam sendo gerados; nesse ponto, provavelmente é tarde demais para tentar refazê-lo em algo como o Rhapsody. Mas mesmo que tenha sido refeito em algo como o Rhapsody, ainda assim temos código gerado a partir do Rhapsody .

Além disso, ter todos esses dados em uma planilha era bom de uma maneira: nos permitia representar os dados de maneiras que não poderíamos ter, se fossem apenas arquivos de código-fonte.

Exemplo 2

Quando fiz alguns trabalhos na construção do compilador, usei a ferramenta Antlr para fazer minha análise e lexing. Especifiquei uma gramática da linguagem, depois usei a ferramenta para cuspir uma tonelada de código em C ++ ou Java, depois usei esse código gerado ao lado do meu próprio código e o incluí na compilação.

De que outra forma isso deveria ter sido feito? Talvez você possa pensar em outra maneira; provavelmente existem outras maneiras. Mas, para esse trabalho, as outras maneiras não seriam melhores do que o código lex / analise gerado que eu tinha.


Eu usei o código intermediário como uma espécie de formato de arquivo e rastreamento de depuração quando os dois sistemas eram incompatíveis, mas tinham algum tipo de API estável, em uma linguagem de script muito esotérica. Não era para ser lido manualmente, mas poderia ter sido da mesma maneira que o xml. Mas isso é mais comum do que você imagina, depois que todas as páginas da web funcionam dessa maneira, como alguém apontou.
Joojaa

7

O que você está perdendo é reutilização .

Temos uma ferramenta incrível para transformar o texto do código-fonte em binário, chamado de compilador. Suas entradas são bem definidas (geralmente!), E passou por muito trabalho para refinar como faz a otimização. Se você realmente deseja usar o compilador para executar algumas operações, deseja usar um compilador existente e não escrever o seu próprio.

Muitas pessoas inventam novas linguagens de programação e escrevem seus próprios compiladores. Praticamente sem exceção, todos estão fazendo isso porque gostam do desafio, não porque precisam dos recursos que essa linguagem fornece. Tudo o que eles fazem poderia ser feito em outro idioma; eles estão simplesmente criando um novo idioma porque gostam desses recursos. O que isso não os atrai é um compilador bem ajustado, rápido, eficiente e otimizador. Isso lhes dará algo que pode transformar o texto em binário, com certeza, mas não será tão bom quanto todos os compiladores existentes .

O texto não é apenas algo que os humanos lêem e escrevem. Os computadores também estão perfeitamente em casa com o texto. De fato, formatos como XML (e outros formatos relacionados) são bem-sucedidos porque usam texto sem formatação. Os formatos de arquivo binário geralmente são obscuros e mal documentados, e um leitor não consegue descobrir facilmente como eles funcionam. O XML é relativamente auto-documentado, facilitando a criação de código para as pessoas que usam arquivos no formato XML. E todas as linguagens de programação são configuradas para ler e gravar arquivos de texto.

Então, suponha que você queira adicionar novas instalações para facilitar sua vida. Talvez seja uma ferramenta de layout da GUI. Talvez sejam as interfaces de sinais e slots que o Qt fornece. Talvez seja dessa maneira que o Code Composer Studio da TI permita configurar o dispositivo com o qual você está trabalhando e puxar as bibliotecas certas para a compilação. Talvez esteja usando um dicionário de dados e typedefs gerados automaticamente e definições de variáveis ​​globais (sim, isso ainda é muito importante no software incorporado). Seja o que for, a maneira mais eficiente de alavancar seu compilador existente é criar uma ferramenta que faça sua configuração do que quer que seja e produza automaticamente código no idioma de sua escolha.

É fácil de desenvolver e fácil de testar, porque você sabe o que está acontecendo e pode ler o código-fonte que ele expõe. Você não precisa gastar muitos anos na construção de um compilador para rivalizar com o GCC. Você não precisa aprender um novo idioma completo ou exigir que outras pessoas o aprendam. Tudo o que você precisa fazer é automatizar essa pequena área e tudo o mais permanece o mesmo. Tarefa concluída.


Ainda assim, a vantagem da base de texto do XML é que, se necessário , ele pode ser lido e gravado por humanos (eles normalmente não se incomodam quando funcionam, mas certamente o fazem durante o desenvolvimento). Em termos de desempenho e eficiência de espaço, os formatos binários geralmente são muito melhores (o que muitas vezes não importa, porque o gargalo está em outro lugar).
leftaroundabout

@leftaroundabout Se você precisa desse desempenho e eficiência de espaço, com certeza. A razão pela qual muitos aplicativos acessaram os formatos baseados em XML atualmente é que desempenho e eficiência de espaço não são os principais critérios de antes, e o histórico mostrou como os formatos de arquivo binários são mantidos de maneira inadequada. (Documentos antigos do MS Word para um exemplo clássico!) Porém, o ponto permanece: o texto é tão adequado para os computadores lerem quanto os humanos.
Graham

Certamente, um formato binário mal projetado pode, na verdade, ter desempenho pior do que um formato de texto devidamente pensado, e mesmo um formato binário decente geralmente não é muito mais compacto do que o XML compactado com algum algoritmo de compactação de uso geral. O melhor dos dois mundos da IMO é usar uma especificação legível por humanos por meio de tipos de dados algébricos e gerar automaticamente uma representação binária eficiente do AST desses tipos. Veja, por exemplo, a biblioteca plana .
leftaroundabout

7

Uma resposta um pouco mais pragmática, focando no porquê e não no que é e não é o código-fonte. Observe que a geração de código-fonte faz parte do processo de compilação em todos esses casos - portanto, os arquivos gerados não devem entrar no controle de origem.

Interoprabilidade / simplicidade

Tome como exemplo os buffers de protocolo do Google: você escreve uma única descrição de protocolo de alto nível que pode ser usada para gerar a implementação em vários idiomas - geralmente diferentes partes do sistema são escritas em diferentes idiomas.

Implementação / razões técnicas

Tome o TypeScript - os navegadores não podem interpretá-lo, portanto o processo de construção usa um transpiler ( conversor de código para código) para gerar JavaScript. De fato, muitas linguagens compiladas novas ou esotéricas começam com a transpilação para C antes de obter um compilador adequado.

Fácil de usar

Para projetos incorporados (pense em IoT) escritos em C e usando apenas um único binário (RTOS ou sem SO), é bastante fácil gerar uma matriz C com os dados a serem compilados como se fosse um código-fonte normal, ao contrário de vinculá-los diretamente como recursos.

Editar

A expansão no protobuf: a geração de código permite que os objetos gerados sejam classes de primeira classe em qualquer idioma. Em uma linguagem compilada, um analisador genérico retornaria, necessariamente, uma estrutura de valor-chave - o que significa que você precisa de muito código padrão, perde algumas verificações em tempo de compilação (em particular chaves e tipos de valores), obtém desempenho pior e sem conclusão de código. Imagine todos aqueles void*em C ou muito grandes std::variantem C ++ (se você tiver C ++ 17), algumas linguagens podem não ter esse recurso.


Pela primeira razão, acho que a idéia do OP seria ter uma implementação genérica em cada idioma (que pega a descrição dos buffers de protocolo e analisa / consome o formato on-the-wire). Por que isso seria pior do que gerar código?
Paŭlo Ebermann 29/11

@ PaŭloEbermann, além do argumento usual de desempenho, uma interpretação tão genérica tornaria impossível usar essas mensagens como objetos de primeira classe em linguagens compiladas (e possivelmente interpretadas) - em C ++, por exemplo, esse intérprete retornaria, necessariamente, uma estrutura de valor-chave . É claro que você pode colocar esse kv em suas aulas, mas pode se transformar em muito código clichê. E também há a conclusão de código também. E verificação do tempo de compilação - seu compilador não verificará se seus literais não possuem erros de digitação.
Jan Dorniak

Concordo ... você poderia adicionar isso à resposta?
Paŭlo Ebermann 29/11

@ PaŭloEbermann done
Jan Dorniak

6

A geração de código fonte é um antipadrão?

É uma solução alternativa para uma linguagem de programação insuficientemente expressiva. Não há necessidade de gerar código em um idioma que contenha metaprogramação interna adequada.


3
Também é uma solução alternativa para ter que escrever um compilador completo, com código de objeto nativo para uma linguagem mais expressiva. Gere C, deixe um compilador com um bom otimizador cuidar do resto.
Blrfl 29/11

Nem sempre. Às vezes, você tem um ou mais bancos de dados contendo algumas definições para, por exemplo, sinais em um barramento. Então você deseja reunir essas informações, talvez fazer algumas verificações de consistência e depois escrever um código que faça interface entre os sinais provenientes do barramento e as variáveis ​​que você espera ter no seu código. Se você puder me mostrar uma linguagem com metaprogramação que facilite o uso de algumas planilhas do Excel fornecidas pelo cliente, um banco de dados e outras fontes de dados e crie o código necessário, com algumas verificações necessárias da validade e consistência dos dados, todos os meios me mostram.
codemonkey

@ CodeMonkey: algo como a implementação ActiveRecord do Ruby on Rails 'vem à mente. Não há necessidade de duplicar o esquema da tabela do banco de dados no código. Apenas mapeie uma classe para uma tabela e escreva a lógica de negócios usando os nomes das colunas como propriedades. Não consigo imaginar nenhum tipo de padrão que pudesse ser produzido por um gerador de código que também não pudesse ser gerenciado pela metaprogramação do Ruby. Os modelos C ++ também são extremamente poderosos, embora um pouco misteriosos. As macros Lisp são outro poderoso sistema de metaprogramação na linguagem.
Kevin cline

@kevincline, o que eu quis dizer com código baseado em alguns dados do banco de dados (pode ser construído a partir dele), mas não no próprio banco de dados. Ou seja, tenho informações sobre quais sinais recebo na tabela A. do Excel. Tenho um banco de dados B com informações sobre esses sinais etc. Agora, quero ter uma classe que acesse esses sinais. Não há conexão com o banco de dados ou a planilha do Excel na máquina que executa o código. Usando o C ++ Templating realmente complicado para gerar esse código em tempo de compilação, em vez de um simples gerador de código. Eu vou pegar codegen.
precisa saber é o seguinte

6

A geração do código-fonte nem sempre é um antipadrão. Por exemplo, atualmente estou escrevendo uma estrutura que, por uma determinada especificação, gera código em duas linguagens diferentes (Javascript e Java). A estrutura usa o Javascript gerado para registrar as ações do navegador do usuário e o código Java no Selenium para realmente executar a ação quando a estrutura está no modo de reprodução. Se eu não usasse a geração de código, teria que me certificar manualmente de que ambos estejam sempre sincronizados, o que é complicado e também é uma duplicação lógica de alguma forma.

Se, no entanto, alguém estiver usando a geração de código-fonte para substituir recursos como genéricos, será anti-padrão.


Obviamente, você poderia escrever seu código uma vez no ECMAScript e executá-lo em Nashorn ou Rhino na JVM. Ou, você pode escrever uma JVM no ECMAScript (ou tentar compilar o Avian no WebAssembly usando o Emscripten) e executar seu código Java no navegador. Não estou dizendo que essas são ótimas ideias (bem, elas provavelmente são terríveis :-D), mas pelo menos são possíveis, se não viáveis.
Jörg W Mittag 29/11

Em teoria, é possível, mas não é uma solução geral. O que acontece se eu não conseguir executar um dos idiomas dentro de outro? Por exemplo, coisa adicional: acabei de criar um modelo simples do Netlogo usando a geração de código e tenho uma documentação interativa do sistema, que está sempre sincronizada com o gravador e o replayer. E, em geral, a criação de um requisito e a geração de código mantém as coisas que são executadas semanticamente em sincronia.
Hristo Vrigazov 29/11

6

Estou faltando alguma coisa aqui?

Talvez um bom exemplo de código intermediário seja o motivo do sucesso? Eu posso lhe oferecer HTML.

Acredito que era importante que o HTML fosse simples e estático - facilitou a criação de navegadores, permitiu o início de navegadores para dispositivos móveis, etc. . Acontece que os usuários realmente estão ameaçados pelos miniaplicativos Java e visitar esses sites era tão seguro quanto tentar quebra de jogos baixadas via DC ++. O HTML simples, por outro lado, é inofensivo o suficiente para permitir que você verifique qualquer site com uma crença razoável na segurança de nosso dispositivo.

No entanto, o HTML não chegaria nem perto de onde está agora se não fosse gerado por computador. Minha resposta nem apareceria nesta página até que alguém a reescrevesse manualmente do banco de dados para o arquivo HTML. Felizmente, você pode criar HTML utilizável em quase qualquer linguagem de programação :)

Ou seja, se existe um gerador de código para alguma coisa, por que não fazer disso uma função adequada que possa receber os parâmetros necessários e executar a ação correta que o código "geraria" teria feito?

Você pode imaginar uma maneira melhor de exibir a pergunta e todas as respostas e comentários para o usuário do que usar HTML como um código intermediário gerado?


Sim, eu posso imaginar uma maneira melhor. O HTML é um legado de uma decisão de Tim Berners-Lee de permitir a criação rápida de um navegador da Web somente de texto. Isso estava perfeitamente bem na época, mas não faríamos o mesmo com o benefício da retrospectiva. O CSS tornou todos os vários tipos de elementos de apresentação (DIV, SPAN, TABLE, UL, etc.) Desnecessários.
Kevin cline

@kevincline Eu não estou dizendo que o HTML, como tal, é sem falhas, eu estava apontando que a introdução da linguagem de marcação (que pode ser gerada por um programa) funcionou muito bem nesse caso.
Džuris

Portanto, HTML + CSS é melhor do que apenas HTML. Até escrevi documentação interna para alguns projetos nos quais trabalhei diretamente em HTML + CSS + MathJax. Mas a maioria das páginas que visito parece ter sido produzida por geradores de código.
David K

3

por que gerar código fonte?

Porque é mais rápido e fácil (e menos propenso a erros) do que escrever o código manualmente, especialmente para tarefas repetitivas e entediantes. Você também pode usar a ferramenta de alto nível para verificar e validar seu design antes de escrever uma única linha de código.

Casos de uso comuns:

  • Ferramentas de modelagem como Rose ou Visual Paradigm;
  • Alto er idiomas nível como SQL incorporado ou uma linguagem de definição de interface que devem ser pré-processados em algo compilable;
  • Geradores Lexer e analisador, como flex / bison;

Quanto ao seu "por que não apenas torná-lo uma função e passar parâmetros diretamente para ele", observe que nenhuma das opções acima são ambientes de execução por si só. Não há como vincular seu código a eles.


2

Às vezes, sua linguagem de programação simplesmente não possui as facilidades que você deseja, tornando realmente impossível escrever funções ou macros para fazer o que você deseja. Ou talvez você possa fazer o que quiser, mas o código para escrevê-lo seria feio. Um script Python simples (ou semelhante) pode gerar o código necessário como parte do seu processo de construção, que você coloca #includeno arquivo de origem real.

Como eu sei disso? Porque é uma solução que eu alcancei várias vezes ao trabalhar com vários sistemas diferentes, mais recentemente o SourcePawn. Um script Python simples que analisa uma linha simples de código fonte e produz duas ou três linhas de código gerado é muito melhor do que criar manualmente o código gerado, quando você termina com duas dúzias dessas linhas (criando todos os meus cvars).

Código fonte demonstrativo / exemplo disponível, se as pessoas o desejarem.


1

O formulário de texto é necessário para facilitar o consumo por seres humanos. Os computadores também processam código em forma de texto com bastante facilidade. Portanto, o código gerado deve ser gerado da forma mais fácil de gerar e mais fácil de consumir pelos computadores, e esse texto geralmente é legível.

E quando você gera código, o próprio processo de geração de código geralmente precisa ser depurado - por seres humanos. É muito, muito útil se o código gerado for legível por humanos, para que humanos possam detectar problemas no processo de geração de código. Alguém tem que escrever o código para gerar código, afinal. Isso não acontece do nada.


1

Gerando código, apenas uma vez

Nem toda geração de código-fonte é um caso de gerar algum código e nunca tocá-lo; em seguida, regenere-o da fonte original quando precisar de atualização.

Às vezes, você gera código apenas uma vez, descarta a fonte original e, em seguida, mantém a nova fonte.

Às vezes, isso acontece ao transportar código de um idioma para outro. Particularmente, se não se espera querer portar mais tarde novas alterações no original (por exemplo, o código do idioma antigo não será mantido, ou está realmente completo (por exemplo, no caso de algumas funcionalidades matemáticas)).

Um caso comum é que a criação de um gerador de código para fazer isso pode traduzir apenas 90% do código corretamente. e então esses últimos 10% precisam ser consertados manualmente. O que é muito mais rápido do que traduzir 100% à mão.

Esses geradores de código geralmente são muito diferentes do tipo de gerador de código que os tradutores de idiomas completos (como Cython ou f2c) produzem. Como o objetivo é fazer o código de manutenção uma vez. Eles geralmente são feitos como um desconto, para fazer exatamente o que eles precisam. De muitas maneiras, é a versão de próximo nível do uso de um regex / find-replace para o código da porta. "Ferramenta assistida portando" você poderia dizer.

Gerando código, apenas uma vez, a partir de, por exemplo, uma raspagem de site.

Intimamente relacionado é se você gerar o código de alguma fonte que não deseja acessar novamente. Por exemplo, se as ações necessárias para gerar o código não puderem ser repetidas, consistentes ou executadas, será caro. No momento, estou trabalhando em um par de projetos: DataDeps.jl e DataDepsGenerators.jl .

O DataDeps.jl ajuda os usuários a baixar dados (como conjuntos de dados ML padrão). Para fazer isso, ele precisa do que chamamos de RegistrationBlock. Esse é um código que especifica alguns metadados, como de onde baixar os arquivos e uma soma de verificação e uma mensagem explicando ao usuário quaisquer termos / códigos / qual é o status de licenciamento dos dados.

Escrever esses blocos pode ser irritante. E essas informações geralmente estão disponíveis em (estruturados ou não estruturados) nos sites em que os dados estão hospedados. Portanto, o DataDepsGenerators.jl, usa um raspador da Web para gerar o RegistrationBlockCode, para alguns sites que hospedam muitos dados.

Pode não gerá-los corretamente. Portanto, o desenvolvedor que usa o código gerado pode e deve verificar e corrigi-lo. As probabilidades são de que eles querem ter certeza de que não errou nas informações de licenciamento, por exemplo.

É importante ressaltar que os usuários / desenvolvedores que trabalham com DataDeps.jl não precisam instalar ou usar o webscraper para usar o código RegistrationBlock que foi gerado. (E não é necessário fazer o download e instalar um raspador da Web para economizar um pouco de tempo. Principalmente para as execuções de IC)

Gerar código fonte uma vez não é um antipadrão. e normalmente não pode ser substituído por metaprogramação.


"report" é uma palavra em inglês que significa algo diferente de "port again". Tente "reportar" para tornar a frase mais clara. (Comentando porque muito pequeno para uma edição sugerida.)
Peter Cordes

Boa captura @PeterCordes Eu reformulei.
Lyndon Branco

Mais rápido, mas potencialmente muito menos sustentável, dependendo de quão horrível é o código gerado. O Fortran para C era uma coisa do passado (os compiladores C estavam mais disponíveis, para que as pessoas usassem f2c+ cc), mas o código resultante não era realmente um bom ponto de partida para uma versão C do programa, AFAIK.
Peter Cordes

1
Potencialmente, potencialmente não. Não é falha no conceito de geradores de código que alguns geradores de código criem código não-sustentável. Em particular, uma ferramenta artesanal, que não precisa capturar todos os casos, pode criar um código perfeitamente agradável. Se 90% do código é apenas uma lista de constantes de matriz, por exemplo, a geração desses construtores de matrizes como uma única pode ser trivialmente muito bem feita e com pouco esforço. (Por outro lado, a saída de código C por Cython não pode ser mantida por seres humanos Porque não se destina a ser como você diz para.. f2cDe volta ao dia)
Lyndon Branco

1
A mesa grande era apenas o argumento mais simples e mais reduzido. Pode-se dizer o mesmo para, por exemplo, conversões for-loops ou condições. De fato, sedpercorre um longo caminho, mas às vezes é preciso um poder um pouco mais expressivo. A linha entre a lógica do programa e os dados geralmente é boa. Às vezes a distinção não é útil. JSON é (/ was) apenas código de construtor de objeto javascript. No meu exemplo Eu também estou gerando código objeto construtor (é dados talvez (talvez não, pois algumas vezes ele tem chamadas de função) É melhor tratada como código sim?.?.)
Lyndon Branco

1

A geração do código "fonte" é uma indicação de uma falha no idioma gerado. O uso de ferramentas para superar isso é um antipadrão? Absolutamente não - deixe-me explicar.

Normalmente, a geração de código é usada porque existe uma definição de nível superior que pode descrever o código resultante muito menos detalhado que o idioma de nível inferior. Portanto, a geração de código facilita a eficiência e a dispersão.

Quando escrevo c ++, faço isso porque me permite escrever um código mais eficiente do que usar o código do montador ou da máquina. O código de máquina imóvel é gerado pelo compilador. No começo, o c ++ era simplesmente um pré-processador que gerava código C. Linguagens de uso geral são ótimas para gerar comportamento de uso geral.

Da mesma forma, usando uma DSL (linguagem específica de domínio), é possível escrever concisa, mas talvez código restrito a uma tarefa específica. Isso tornará menos complicado gerar o comportamento correto do código. Lembre-se que o código é meio para e fim . O que um desenvolvedor está procurando é uma maneira eficiente de gerar comportamento.

Idealmente, o gerador pode criar código rápido a partir de uma entrada mais simples de manipular e entender. Se isso for cumprido, não usar um gerador é um anti-padrão . Esse antipadrão geralmente vem da noção de que o código "puro" é "mais limpo", da mesma forma que um trabalhador da madeira ou outro artesão pode considerar o uso de ferramentas elétricas ou o CNC para "gerar" peças de trabalho (pense em ouro) martelo ).

Por outro lado, se a fonte do código gerado for mais difícil de manter ou gerar código que não seja eficiente o suficiente, o usuário estará caindo na armadilha de usar as ferramentas erradas (em algum momento por causa do mesmo martelo de ouro ).


0

A geração do código fonte significa absolutamente que o código gerado é dado. Mas são dados de primeira classe, dados que o restante do programa pode manipular.

Os dois tipos mais comuns de dados que eu conheço e que estão integrados ao código-fonte são informações gráficas sobre janelas (número e posicionamento de vários controles) e ORMs. Nos dois casos, a integração via geração de código facilita a manipulação dos dados, porque você não precisa executar etapas "especiais" extras para usá-los.

Ao trabalhar com os Macs originais (1984), as definições de diálogo e janela foram criadas usando um editor de recursos que mantinha os dados em um formato binário. O uso desses recursos em seu aplicativo foi mais difícil do que teria sido se o "formato binário" tivesse sido Pascal.

Portanto, não, a geração do código fonte não é um antipadrão, pois permite tornar os dados parte do aplicativo, o que facilita o uso.


0

A geração de código é um antipadrão quando custa mais do que consegue. Essa situação ocorre quando a geração ocorre de A a B, onde A é quase o mesmo idioma que B, mas com algumas extensões menores que podem ser feitas apenas com a codificação de A com menos esforço do que todas as ferramentas personalizadas e a preparação para A a B .

O trade-off é mais proibitivo contra a geração de código em linguagens que não possuem recursos de metaprogramação (macros estruturais) devido às complicações e inadequações da consecução da metaprogramação através da preparação do processamento de texto externo.

O fraco comércio também pode ter a ver com a quantidade de uso. O idioma A pode ser substancialmente diferente de B, mas todo o projeto com seu gerador de código personalizado usa apenas A em um ou dois lugares pequenos, de modo que a quantidade total de complexidade (pequenos bits de A, mais o gerador de código A -> B, mais o teste de compilação circundante) excede a complexidade de uma solução recém-criada em B.

Basicamente, se nos comprometemos com a geração de código, provavelmente devemos "ir grande ou voltar para casa": torná-la semântica substancial e usá-la muito ou não se incomodar.


Por que você removeu o parágrafo "Quando Bjarne Stroustrup implementou C ++ ..." pela primeira vez? Eu acho que foi interessante.
precisa

@Utku Outras respostas cobrem isso do ponto de vista da compilação de uma linguagem sofisticada e inteira, na qual o restante de um projeto é inteiramente escrito. Eu não acho que seja representativo da maioria do que é chamado de "geração de código".
Kaz

0

Não vi isso declarado claramente (vi-o abordado por uma ou duas respostas, mas não parecia muito claro)

Gerar código (como você disse, como se fossem dados) não é um problema - é uma maneira de reutilizar um compilador para fins secundários.

A edição de código gerado é um dos anti-padrões mais insidiosos, maus e horríveis que você já encontrou. Não faça isso.

Na melhor das hipóteses, a edição do código gerado atrai um monte de código ruim para o seu projeto (o conjunto INTEIRO de código agora é realmente SOURCE CODE - não mais dados). Na pior das hipóteses, o código inserido no seu programa é um lixo altamente redundante e mal nomeado, que é quase completamente impossível de manter.

Suponho que uma terceira categoria seja o código que você usa uma vez (gerador de GUI?) E edite para ajudá-lo a começar / aprender. Isso é um pouco de cada um - PODE ser uma boa maneira de iniciar, mas seu gerador de GUI será direcionado para o código "Gerável" que não será um bom começo para você como programador - Além disso, você pode ser tentado a usá-lo novamente para uma segunda GUI, o que significa inserir um código SOURCE redundante em seu sistema.

Se suas ferramentas forem inteligentes o suficiente para proibir qualquer edição do código gerado, faça isso. Caso contrário, eu chamaria de um dos piores anti-padrões existentes.


0

Código e dados são: Informações.

Dados são as informações exatamente no formato que você precisa (e valor). Código também é informação, mas de forma indireta ou intermediária. Em essência, o código também é uma forma de dados.

Mais especificamente, o código é uma informação para as máquinas descarregarem os humanos do processamento de informações sozinhos.

Descarregar os seres humanos do processamento de informações é o motivo mais importante. Etapas intermediárias são aceitáveis ​​desde que facilitem a vida. É por isso que existem ferramentas intermediárias de mapeamento de informações. Como geradores de código, compiladores, transpiladores, etc.

por que gerar código fonte? Por que não transformá-lo em uma função que pode aceitar parâmetros e agir sobre eles?

Digamos que alguém ofereça essa função de mapeamento, cuja implementação é obscura para você. Enquanto a função funcionar como prometido, você se importaria se internamente estivesse gerando código-fonte ou não?


0

Se algo pode ser gerado, então isso é dado, não código.

Na medida em que você estipula posteriormente que o código são dados, sua proposta se reduz a "Se algo puder ser gerado, esse item não será código". Você diria, então, que o código de assembly gerado por um compilador C não é código? E se coincidir exatamente com o código de montagem que escrevo à mão? Você pode ir lá, se quiser, mas eu não vou com você.

Vamos começar com uma definição de "código". Sem ser muito técnico, uma definição muito boa para os propósitos desta discussão seria "instruções acionáveis ​​por máquina para executar um cálculo".

Dado isso, toda essa ideia de geração de código fonte não é um mal-entendido?

Bem, sim, sua proposição inicial é que o código não pode ser gerado, mas eu rejeito essa proposição. Se você aceitar minha definição de "código", não deverá haver nenhum problema conceitual com a geração de código em geral.

Ou seja, se existe um gerador de código para alguma coisa, por que não fazer disso uma função adequada que possa receber os parâmetros necessários e executar a ação correta que o código "geraria" teria feito?

Bem, essa é uma pergunta totalmente diferente, sobre o motivo de empregar geração de código, e não sobre sua natureza. Você está propondo a alternativa de que, em vez de escrever ou usar um gerador de código, alguém escreve uma função que calcula o resultado diretamente. Mas em que idioma? Longe vão os dias em que alguém escreveu diretamente no código da máquina e, se você escrever o código em qualquer outro idioma, depende de um gerador de código na forma de um compilador e / ou montador para produzir um programa que realmente seja executado.

Por que, então, você prefere escrever em Java ou C ou Lisp ou o que quer? Montador mesmo? Afirmo que é pelo menos em parte porque essas linguagens fornecem abstrações para dados e operações que facilitam a expressão dos detalhes da computação que você deseja executar.

O mesmo se aplica à maioria dos geradores de código de nível superior. Os casos prototípicos provavelmente são geradores de scanner e analisador, como lexe yacc. Sim, você pode escrever um scanner e um analisador diretamente em C ou em alguma outra linguagem de programação de sua escolha (até mesmo código de máquina bruto), e às vezes um faz. Porém, para um problema de qualquer complexidade significativa, o uso de uma linguagem para fins especiais de nível superior, como lex ou yacc, facilita o código manuscrito, a gravação, a leitura e a manutenção. Geralmente muito menor também.

Você também deve considerar o que exatamente você quer dizer com "gerador de código". Eu consideraria o pré-processamento C e a instanciação de modelos C ++ como exercícios na geração de código; você se opõe a isso? Caso contrário, acho que você precisará executar algumas ginástica mentais para racionalizar a aceitação delas, mas rejeitar outros tipos de geração de código.

Se estiver sendo feito por motivos de desempenho, isso soa como uma falha do compilador.

Por quê? Você está basicamente afirmando que um deve ter um programa universal para o qual o usuário alimenta dados, alguns classificados como "instruções" e outros como "entrada", e que prossegue para executar o cálculo e emitir mais dados que chamamos de "saída". (De um certo ponto de vista, pode-se chamar esse programa universal de "sistema operacional".) Mas por que você supõe que um compilador deve ser tão eficaz na otimização de um programa de uso geral quanto na otimização de um programa mais especializado? programa? Os dois programas têm características diferentes e capacidades diferentes.

Se estiver sendo feito para conectar dois idiomas, isso soa como uma falta de biblioteca de interface.

Você diz que como se ter uma biblioteca de interface universal até certo ponto fosse necessariamente uma coisa boa. Talvez fosse, mas em muitos casos essa biblioteca seria grande e difícil de escrever e manter, e talvez até lenta. E se esse animal não existe de fato para atender o problema específico em questão, quem é você para insistir em que um seja criado, quando uma abordagem de geração de código pode resolver o problema com muito mais rapidez e facilidade?

Estou faltando alguma coisa aqui?

Várias coisas, eu acho.

Eu sei que o código também é dados. O que eu não entendo é, por que gerar código fonte? Por que não transformá-lo em uma função que pode aceitar parâmetros e agir sobre eles?

Geradores de código transformam código escrito em um idioma em código em um idioma diferente, geralmente de nível inferior. Você está perguntando, então, por que as pessoas gostariam de escrever programas usando vários idiomas e, principalmente, por que eles podem querer misturar idiomas de níveis subjetivamente diferentes.

Mas eu já toquei nisso. A pessoa escolhe um idioma para uma tarefa específica, baseada em parte em sua clareza e expressividade para essa tarefa. Como o código menor possui menos bugs, em média, e é mais fácil de manter, também existe uma tendência para idiomas de nível superior, pelo menos para trabalhos em larga escala. Porém, um programa complexo envolve muitas tarefas e, muitas vezes, algumas delas podem ser tratadas de maneira mais eficaz em um idioma, enquanto outras são mais efetivas ou concisas em outro. Às vezes, usar a ferramenta certa para o trabalho significa empregar geração de código.


0

Respondendo à pergunta dentro do contexto do seu comentário:

O dever do compilador é pegar um código escrito em formato legível por humanos e convertê-lo em formato legível por máquina. Portanto, se o compilador não puder criar um código eficiente, o compilador não está fazendo seu trabalho corretamente. Isso está errado?

Um compilador nunca será otimizado para sua tarefa. O motivo disso é simples: ele é otimizado para executar muitas tarefas. É uma ferramenta de uso geral usada por muitas pessoas para muitas tarefas diferentes. Depois de saber qual é a sua tarefa, você pode abordar o código de uma maneira específica do domínio, fazendo trocas que os compiladores não podiam.

Como exemplo, trabalhei em software em que um analista pode precisar escrever algum código. Eles poderiam escrever seu algoritmo em C ++ e adicionar todas as verificações de limites e truques de memoização dos quais eles dependem, mas isso exige saber muito sobre o funcionamento interno do código. Eles preferem escrever algo simples e deixe-me lançar um algoritmo para gerar o código C ++ final. Então, posso fazer truques exóticos para maximizar o desempenho, como análise estática, que eu nunca esperaria que meus analistas suportassem. A geração de código permite que eles escrevam de uma maneira específica do domínio, o que permite que o produto seja lançado mais facilmente do que qualquer ferramenta de uso geral.

Eu também fiz exatamente o oposto. Tenho outro trabalho que fiz que tinha um mandato "sem geração de código". Ainda queríamos facilitar a vida das pessoas que usavam o software, por isso usamos grandes quantidades de metaprogramação de modelos para fazer o compilador gerar o código rapidamente. Portanto, eu só precisava da linguagem C ++ de propósito geral para fazer meu trabalho.

No entanto, há um problema. Era tremendamente difícil garantir que os erros fossem legíveis. Se você já usou código metaprogramado por modelo antes, sabe que um único erro inocente pode gerar um erro que leva 100 linhas de nomes de classe incompreensíveis e argumentos de modelo para entender o que deu errado. Esse efeito foi tão pronunciado que o processo de depuração recomendado para erros de sintaxe foi "Percorra o log de erros até ver a primeira vez que um de seus arquivos apresenta um erro. Vá para essa linha e aponte os olhos até perceber o que está fazendo. fez errado. "

Se tivéssemos usado a geração de código, poderíamos ter recursos de manipulação de erro muito mais poderosos, com erros legíveis por humanos. É a vida.


0

Existem algumas maneiras diferentes de usar a geração de código. Eles podem ser divididos em três grupos principais:

  • Gerando código em um idioma diferente como saída de uma etapa no processo de compilação. Para o compilador típico, essa seria uma linguagem de nível inferior, mas poderia ser para outra linguagem de alto nível, como no caso das linguagens que são compiladas no JavaScript.
  • Gerar ou transformar código no idioma do código-fonte como uma etapa do processo de compilação. É isso que as macros fazem.
  • Gerando código com uma ferramenta separadamente do processo de compilação regular. A saída disso é um código que vive como arquivos junto com o código fonte regular e é compilado junto com ele. Por exemplo, as classes de entidade para um ORM podem ser geradas automaticamente a partir de um esquema de banco de dados ou objetos de transferência de dados e interfaces de serviço podem ser gerados a partir de uma especificação de interface como um arquivo WSDL para SOAP.

Eu acho que você está falando sobre o terceiro tipo de código gerado, já que esta é a forma mais controversa. Nas duas primeiras formas, o código gerado é uma etapa intermediária, que é muito bem separada do código-fonte. Mas, na terceira forma, não existe uma separação formal entre o código-fonte e o código gerado, exceto que o código gerado provavelmente possui um comentário que diz "não edite este código". Ainda abre o risco de os desenvolvedores editarem o código gerado, o que seria realmente feio. Do ponto de vista do compilador, o código gerado é o código-fonte.

No entanto, essas formas de código gerado podem ser realmente úteis em uma linguagem de tipo estaticamente. Por exemplo, quando a integração com entidades ORM, é realmente útil ter wrappers fortemente tipados para as tabelas do banco de dados. Claro que você poderia lidar com a integração dinamicamente no tempo de execução, mas perderia a segurança de tipo e o suporte de ferramentas (conclusão de código). Um grande benefício da linguagem de tipo estaticamente é o suporte do sistema de tipos no tipo de escrita, e não apenas no tempo de execução. (Por outro lado, esse tipo de geração de código não é muito prevalente em linguagens de tipo dinâmico, uma vez que, nesse idioma, não oferece nenhum benefício comparado às conversões em tempo de execução.)

Ou seja, se existe um gerador de código para alguma coisa, por que não fazer disso uma função adequada que possa receber os parâmetros necessários e executar a ação correta que o código "geraria" teria feito?

Como segurança de tipo e conclusão de código são recursos que você deseja em tempo de compilação (e ao escrever código em um IDE), mas funções regulares são executadas apenas em tempo de execução.

Porém, pode haver um meio termo: O F # suporta o conceito de provedores de tipo, que são basicamente interfaces fortemente tipadas, geradas programaticamente em tempo de compilação. Esse conceito provavelmente poderia substituir muitos usos da geração de código e fornecer uma separação mais limpa das preocupações.


0

Os conjuntos de instruções do processador são fundamentalmente imperativos , mas as linguagens de programação podem ser declarativas . A execução de um programa escrito em uma linguagem declarativa exige inevitavelmente algum tipo de geração de código. Conforme mencionado nesta resposta e em outras, uma das principais razões para gerar código-fonte em uma linguagem legível por humanos é aproveitar as otimizações sofisticadas executadas pelos compiladores.


-3

Se algo pode ser gerado, então isso é dado, não código.

Você entendeu errado. Deve ler

Se algo pode ser alimentado em um gerador para interpretáveis , então isso é código, não dados.

É o formato de origem para esse estágio de compilação, e o formato do coletor ainda é o código.


1
Definição incorreta do código fonte . O código fonte é principalmente para os humanos que trabalham nele (e esse mero fato define, veja também o que é software livre pela FSF). O código do assembler gerado com gcc -fverbose-asm -O -Snão é código-fonte (e não é apenas ou principalmente dados), mesmo que seja alguma forma de texto sempre alimentada ao GNU ase às vezes lida por humanos.
Basile Starynkevitch 30/11

Além disso, muitas implementações de linguagens são compiladas no código C , mas o C gerado não é um código-fonte genuíno (por exemplo, não pode ser facilmente trabalhado por humanos).
Basile Starynkevitch

Por fim, seu hardware (por exemplo, seu chip AMD ou Intel ou a placa mãe do computador) está interpretando o código da máquina (que obviamente não é o código-fonte). BTW O IBM1620 possuía um código de máquina digitável por teclado (BCD), mas esse fato não o tornou "código-fonte". Todo o código não é fonte.
Basile Starynkevitch

@BasileStarynkevitch Ah, você me pegou lá. Não devo tentar comprimir muito minha afirmação espirituosa, ou elas mudam de significado. Certo, o código- fonte deve ser o código mais original que é alimentado no primeiro estágio de compilação.
Bergi

Nenhum código fonte é código para humanos. É tão difícil e subjetivo definir como música (vs. som). Não se trata de tentar encontrar o software que o consome.
Basile Starynkevitch
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.