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

115

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

Dado que, não é toda essa idéia de geração de código-fonte um mal-entendido? Isto é, se existe um gerador de código para algo, então por que não fazer desse algo uma função adequada que possa receber os parâmetros requeridos e fazer a ação correta que o código "gerado" teria feito?

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

Se isso está sendo feito para unir dois idiomas, isso parece falta de biblioteca de interface.

Estou faltando alguma coisa aqui?

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

    
por Utku 29.11.2017 / 04:51
fonte

27 respostas

144

Is source code generation an anti pattern?

Tecnicamente, se gerarmos código, não é fonte , mesmo que seja texto legível por humanos. Código Fonte é código original, gerado por um ser humano ou outra verdadeira inteligência, não traduzido mecanicamente e não imediatamente reproduzível a partir de fonte (verdadeira) (direta ou indiretamente).

If something can be generated, than that thing is data, not code.

Eu diria que tudo é dados de qualquer maneira. Mesmo código-fonte. Especialmente código-fonte! O código-fonte é apenas um dado 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 instruções, o programa é carregado na memória como dados .

Então, tudo é dado , até o código.

Given that [generated code is data], isn't this whole idea of code generation a misunderstanding?

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

That is, if there is a code generator for something, then why not make that something a proper function which can receive the required parameters and do the right action that the "would generated" code would have done?

Esse é um caminho, mas há outros.

The output of code generation is text, which is something designed to be used by a human.

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 & desenvolve; o que nós arquivamos usando o controle do código-fonte. O código gerado, mesmo quando o texto legível por humanos, é tipicamente gerado a partir do código fonte original . O código gerado, em geral, não precisa estar sob controle de origem, pois é gerado novamente durante a compilação.

    
por 29.11.2017 / 05:17
fonte
64

Raciocínio prático

OK, I know that code is data as well. What I don't understand is, why generate source code?

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

A razão clássica para gerar código-fonte em linguagens estáticas como Java foi que linguagens como essas simplesmente não vêm com ferramentas fáceis de usar em linguagem para fazer coisas muito dinâmicas. Por exemplo, nos dias de formação de Java, simplesmente não era possível criar facilmente uma classe com um nome dinâmico (correspondente a um nome de tabela de um banco de dados) e métodos dinâmicos (atributos correspondentes dessa tabela) com tipos de dados dinâmicos (correspondência os tipos dos ditos atributos). Especialmente já que o Java coloca toda a importância, ou melhor, garantias, na capacidade de capturar erros de tipo em tempo de compilação.

Assim, em tal configuração, um programador só pode criar código Java e escrever várias linhas de código manualmente. Muitas vezes, o programador descobrirá que sempre que uma tabela muda, ele precisa voltar e alterar o código para corresponder; e se ele se esquece disso, coisas ruins acontecem. Assim, o programador chegará ao ponto em que ele escreve algumas ferramentas que fazem isso para ele. E, portanto, a estrada começa a geração de código cada vez mais inteligente.

(Sim, você poderia gerar o bytecode na hora, mas programar tal coisa 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 que são muito dinâmicas, por exemplo, Ruby, que eu consideraria a antítese de Java em muitos aspectos (note que estou dizendo isso sem valorizar nenhuma abordagem; 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 entrar em um nível "meta". Sim, coisas como Ruby on Rails vêm com geração de código, mas descobrimos em nosso trabalho que basicamente usamos isso como uma espécie de "modo tutorial" avançado para novos programadores, mas depois de um tempo ele fica supérfluo (já que há tão pouco código para escrever nesse ecossistema que quando você sabe o que está fazendo, escrevê-lo manualmente fica mais rápido do que limpar o código gerado).

Estes são apenas dois exemplos práticos do "mundo real". Então você tem linguagens como o 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), há (ou não, eu não mantive os recursos modernos de C ++ ...) simplesmente nenhum conceito de definir nomes de classes ou métodos em tempo de execução, Assim, a geração de código do processo de compilação é a ferramenta de escolha para a maioria das coisas (outros exemplos específicos de C / C ++ podem ser flex, yacc, etc.).

    
por 29.11.2017 / 08:40
fonte
44

why generate code?

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

If it is being done for performance reasons, then that sounds like a shortcoming of the compiler.

Verdadeiro. Eu não me importo com o desempenho, a menos que eu seja forçado a isso.

If it is being done to bridge two languages, then that sounds like a lack of interface library.

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

É assim: O código fonte gerado e retido é sempre e para sempre uma dor na bunda. Existe apenas por um motivo. Alguém quer trabalhar em um idioma enquanto outra pessoa insiste em trabalhar em outro, e nenhum deles pode se incomodar em descobrir como interoperar entre eles, então um deles descobre como transformar sua linguagem favorita na linguagem imposta para que eles possam fazer o que eles querem.

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

É um padrão anti? Suspiro, não. Muitos idiomas não existiriam se não estivéssemos dispostos a nos despedir das 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 é deixada em uma metade de uma colcha de retalhos de monstro Frankenstein que eu não suporto. Código gerado é código intocável. Eu odeio olhar para código intocável. No entanto, as pessoas continuam verificando isso. POR QUE? Você também pode verificar o executável.

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

    
por 29.11.2017 / 05:47
fonte
41

why generate source code

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

  • adotou 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 produzi código CRUD de placa de caldeira para 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, seguro e livre de erros (assumiu que o código de saída dos geradores está maduro) . Você pode imaginar o quanto isso reduz o esforço de manutenção e evolução.

Deixe-me também responder à sua pergunta inicial

Is source code generation an anti pattern

Não, não a geração de código fonte, mas existem algumas armadilhas. Como afirmado em O Programador Pragmático , deve-se evitar o uso de um gerador de código quando ele produz um código que é difícil para entender . Caso contrário, os esforços crescentes para usar ou depurar esse código podem facilmente compensar o esforço salvo ao não escrever o código manualmente.

Eu também gostaria de acrescentar que é, na maioria das vezes, uma boa idéia separar as partes geradas do código do código escrito manualmente fisicamente, de forma que a nova geração não sobrescreva quaisquer alterações manuais. No entanto, eu também já lidei com a situação mais de uma vez em que a tarefa era migrar algum código escrito na antiga linguagem X para outra linguagem mais moderna Y, com a intenção de fazer a manutenção posteriormente na linguagem Y. Este é um uso válido caso para geração de código único.

    
por 29.11.2017 / 08:19
fonte
13

why generate source code?

Eu encontrei dois casos de uso para código gerado (em tempo de compilação e nunca marcado):

  1. Gerar automaticamente código clichê, como getters / setters, toString, equals e hashCode de uma linguagem criada para especificar tais coisas (por exemplo, lombok de projeto para Java)
  2. Gerar automaticamente classes de tipo DTO de alguma especificação de interface (REST, SOAP, qualquer que seja) para ser usada no código principal. Isso é semelhante ao problema de ponte de idioma, mas acaba sendo mais limpo e mais simples, com melhor manipulação de tipos do que tentar implementar a mesma coisa sem classes geradas.
por 29.11.2017 / 06:16
fonte
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 dos códigos de dados.

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

Linguagens específicas de domínios triviais podem economizar bastante tempo, e gerar algo que possa ser compilado por ferramentas de linguagem padrão é o caminho a percorrer ao criar essas coisas, o que você preferiria editar, um analisador não trivial hackeado em qualquer idioma nativo. linguagem que você está escrevendo, ou o BNF para um gerado automaticamente?

Ao enviar o texto que é então alimentado para algum compilador de sistema, eu obtenho toda essa otimização de compiladores e configuração específica do sistema sem ter que pensar sobre isso.

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

Agora, se você for difícil de pensar , poderá editar o OUTPUT do analisador de linguagem de brinquedo, o que desapontará claramente na próxima vez que alguém editar os arquivos de idioma de entrada e reconstruções, a resposta é não confirmar o auto gera o IR para o repositório, ele é gerado pelo seu toolchain (E evite ter essas pessoas no seu grupo de desenvolvimento, elas geralmente são mais felizes trabalhando em marketing).

Isso não é tanto uma falha de expressividade em nossos idiomas, como 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 isso geralmente geram muito menos bugs e são muito mais fáceis de manter. Se eu puder dar aos nossos caras de teste e configuração uma planilha que eles possam ajustar e uma ferramenta que eles executam que pega esses dados e cospe um arquivo hexadecimal completo para o flash em minha ECU, então é um grande economia de tempo a configuração mais recente em um conjunto de constantes na linguagem do dia (completo com erros de digitação).

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

É claro que você pode escrever essas coisas para produzir bytecode ou mesmo código executável, mas por que você faria isso? Temos ferramentas para converter um IR nessas coisas.

    
por 29.11.2017 / 15:16
fonte
12

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

OK, I know that code is data as well. What I don't understand is, why generate code? Why not make it into a function which can accept parameters and act on them?

Se você tiver que fazer esta pergunta e não houver uma resposta clara, provavelmente a geração de código é supérflua e apenas contribuirá com exotismo e uma grande quantidade de sobrecarga intelectual para sua base de código.

Entretanto, se você pegar algo como OpenShadingLanguage: link

... então essas questões não precisam ser levantadas, uma vez que são imediatamente respondidas pelos resultados impressionantes.

OSL uses the LLVM compiler framework to translate shader networks into machine code on the fly (just in time, or "JIT"), and in the process heavily optimizes shaders and networks with full knowledge of the shader parameters and other runtime values that could not have been known when the shaders were compiled from source code. As a result, we are seeing our OSL shading networks execute 25% faster than the equivalent shaders hand-crafted in C! (That's how our old shaders worked in our renderer.)

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

    
por 29.11.2017 / 05:28
fonte
8

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

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

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

Você deve observar que a procissão lógica de compilação de código C não gera código de máquina, mas sim 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ê poderia compilar de código legível humano 1 para código legível humano 2 para código legível humano 3 para código de máquina. "Por quê?" Porque não?

Se um código intermediário, legível por humanos, nunca foi gerado, talvez nem tenhamos C ou C ++. Isso é certamente uma possibilidade; as pessoas tomam o caminho de menor resistência a seus objetivos, e se alguma outra língua ganhava vapor primeiro por causa da estagnação do desenvolvimento C, C poderia ter morrido enquanto ainda era jovem. Claro, você poderia argumentar "Mas então talvez nós estaríamos usando alguma outra linguagem, e talvez seria melhor." Talvez, ou talvez seja pior. Ou talvez todos ainda estivéssemos escrevendo em assembly.

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

  1. Às vezes, o código intermediário é desejado para que você possa modificá-lo antes da próxima etapa da construção. Eu vou admitir que esse ponto é o mais fraco.
  2. Às vezes, é porque o trabalho original não foi feito em nenhum idioma legível por humanos, mas sim 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 é um nicho tão complexo ou tão complicado que não é necessário aumentar a complexidade ou a gramática da linguagem de programação apenas para acomodá-lo.
  4. Às vezes você precisa fazer algo muito repetitivo, e não é possível pegar o que você quer na linguagem de uma forma genérica; ou não pode ser representado por ou está em conflito com a gramática da língua.
  5. Um dos objetivos dos computadores é reduzir o esforço humano, e às vezes o código que provavelmente nunca será tocado (baixa probabilidade de manutenção) pode ter meta-código escrito para gerar seu código mais longo em um décimo do tempo; Se eu puder fazer isso em 1 dia ao invés de 2 semanas e não é provável que seja mantido nunca, então é melhor gerá-lo - e na chance de que alguém daqui a 5 anos esteja chateado porque eles realmente fazem precisa mantê-lo, então eles podem passar as 2 semanas escrevendo completamente se quiserem, ou ficarem irritados com 1 semana de manutenção do código desajeitado (mas ainda estamos 1 semana à frente nesse ponto), e isso é se essa manutenção precisa ser feita.
  6. Tenho certeza de que há mais motivos pelos quais estou negligenciando.

Exemplo

Eu trabalhei em projetos antes, onde 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 passava pela planilha e gerava um lote de código C ++ e Java que nos permitia trabalhar com essas mensagens .

Eu não estou dizendo que essa foi a melhor maneira de configurar esse projeto (eu não fiz parte de sua startup), mas era o que tínhamos, e eram centenas (talvez milhares, não tenho certeza) de estruturas e objetos e constantes que estavam sendo gerados; Nesse ponto, provavelmente é tarde demais para tentar refazê-lo em algo como o Rhapsody. Mas mesmo se fosse refeito em algo como o Rhapsody, então ainda temos código gerado a partir do Rhapsody .

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

Exemplo 2

Quando fiz algum trabalho na construção de compiladores, usei a ferramenta Antlr para fazer minha lexação e análise. Eu especifiquei uma gramática de linguagem, então eu usei a ferramenta para citar uma tonelada de código em C ++ ou Java, então eu usei esse código gerado ao lado do meu próprio código e incluí-lo 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, os outros caminhos não seriam melhores do que o código lex / parse gerado que eu tinha.

    
por 29.11.2017 / 21:41
fonte
7

O que está faltando é 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 tem sido muito trabalhoso para refinar a otimização. Se você realmente quiser usar o compilador para realizar algumas operações, você quer 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. Quase sem exceção, todos eles estão fazendo isso porque gostam do desafio, não porque precisam dos recursos que a linguagem fornece. Tudo o que eles fazem pode ser feito em outro idioma; eles estão simplesmente criando um novo idioma porque gostam desses recursos. O que não os conseguirá é um compilador otimizado, rápido, eficiente e otimizado. Isso fará com que algo seja transformado 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 estão perfeitamente em casa com o texto também. Na verdade, formatos como XML (e outros formatos relacionados) são bem-sucedidos porque eles usam texto simples. Formatos de arquivos binários são geralmente obscuros e mal documentados, e um leitor não pode descobrir facilmente como eles funcionam. O XML é relativamente autodocumentado, tornando mais fácil para as pessoas escreverem códigos que usam arquivos formatados em XML. E todas as linguagens de programação estão configuradas para ler e gravar arquivos de texto.

Então, suponha que você queira adicionar algumas novas facilidades para facilitar sua vida. Talvez seja uma ferramenta de layout da GUI. Talvez sejam as interfaces de sinais e slots que o Qt oferece. Talvez seja assim que o Estúdio de Compositores de Código da TI permite configurar o dispositivo com o qual você está trabalhando e colocar as bibliotecas certas no construir. Talvez ele esteja usando um dicionário de dados e definições automáticas de typedefs e variáveis globais (sim, isso ainda é uma coisa muito importante no software incorporado). Seja o que for, a maneira mais eficiente de alavancar seu compilador existente é criar uma ferramenta que leve sua configuração do que é e produza automaticamente o código em seu idioma de preferência.

É fácil desenvolver e testar, porque você sabe o que está acontecendo e pode ler o código-fonte que ele mostra. Você não precisa gastar muitos anos construindo um compilador para rivalizar com o GCC. Você não precisa aprender um novo idioma completo ou exigir que outras pessoas o façam. Tudo o que você precisa fazer é automatizar essa pequena área, e tudo mais permanece o mesmo. Trabalho feito.

    
por 29.11.2017 / 13:06
fonte
7

Uma resposta um pouco mais pragmática, concentrando-se no porquê e não no que é e não é o código-fonte. Observe que gerar código-fonte é uma parte do processo de criação em todos esses casos - portanto, os arquivos gerados não devem encontrar o caminho para o controle de origem.

Interoperabilidade / simplicidade

Tome o Protocol Buffers do Google, um excelente exemplo: 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 - muitas vezes partes diferentes do sistema são escritas em idiomas diferentes.

Implementação / razões técnicas

Pegue o TypeScript - os navegadores não podem interpretá-lo de modo que o processo de compilação use um transpilador (código para o tradutor do código) para gerar JavaScript. De fato, muitas linguagens compiladas novas ou esotéricas começam com o transpilar para C antes de obterem um compilador adequado.

Facilidade de uso

Para projetos incorporados (pense em IoT) escritos em C e usando apenas um único binário (RTOS ou nenhum sistema operacional), é muito fácil gerar um array C com os dados a serem compilados como se o código-fonte normal fosse aberto -los diretamente como recursos.

Editar

Expandindo o 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 deveria necessariamente retornar uma estrutura de valor-chave - o que significa que você usa muito código padrão, você perde algumas verificações em tempo de compilação (em chaves e tipos de valores em particular), obtém um desempenho pior e sem conclusão de código. Imagine todos aqueles void* em C ou aquele grande std::variant em C ++ (se você tiver C + + 17), alguns idiomas podem não ter tal recurso.

    
por 29.11.2017 / 08:35
fonte
6

Is source code generation an anti pattern?

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

    
por 29.11.2017 / 08:43
fonte
6

A geração de código fonte nem sempre é um antipadrão. Por exemplo, atualmente estou escrevendo um framework que por 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 usa o código Java no Selenium para realmente executar a ação quando a estrutura está no modo de repetição. Se eu não usasse a geração de código, teria que ter certeza de que ambos estão sempre em sincronia, 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, então é anti-padrão.

    
por 29.11.2017 / 09:03
fonte
6

Am I missing something here?

Talvez um bom exemplo de onde o código intermediário se tornou o motivo do sucesso? Eu posso te oferecer HTML.

Acredito que seja importante que o HTML seja simples e estático - facilitou a criação de navegadores, permitiu iniciar navegadores para dispositivos móveis mais cedo, etc. Como outros experimentos (applets Java, Flash) mostraram - idiomas mais complexos e poderosos levam para mais problemas. Acontece que os usuários, na verdade, estão em perigo pelos miniaplicativos Java, e visitar esses sites era tão seguro quanto tentar baixar os arquivos baixados via DC ++. O HTML simples, por outro lado, é inofensivo o suficiente para nos permitir verificar qualquer site com razoável crença na segurança de nosso dispositivo.

No entanto, o HTML não estaria 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 fazer HTML utilizável em quase qualquer linguagem de programação:)

That is, if there is a code generator for something, then why not make that something a proper function which can receive the required parameters and do the right action that the "would generated" code would have done?

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

    
por 29.11.2017 / 13:37
fonte
3

why generate source code?

Porque é mais rápido e fácil (e menos sujeito 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 Paradigma visual;
  • Linguagens de nível er alto como o Embedded SQL ou uma linguagem de definição de interface que deve ser pré-processada em algo compilável;
  • Geradores de Lexer e analisadores como flex / bison;

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

    
por 29.11.2017 / 19:23
fonte
2

Às vezes, sua linguagem de programação simplesmente não possui os recursos desejados, tornando 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 simples script Python (ou similar) pode então gerar o código requerido como parte do seu processo de compilação, o qual você então #include no arquivo fonte atual.

Como eu sei disso? Porque é uma solução que alcancei várias vezes ao trabalhar com vários sistemas diferentes, mais recentemente o SourcePawn. Um simples script Python 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ê acaba com duas dúzias dessas linhas (criando todos os meus cvars).

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

    
por 29.11.2017 / 13:36
fonte
1

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

E quando você gera código, o próprio processo de geração de código geralmente precisa ser depurado - por humanos. É muito, muito útil se o código gerado for legível por humanos, para que os 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.

    
por 29.11.2017 / 08:22
fonte
1

Gerando código, apenas uma vez

Nem toda geração de código fonte é um caso de gerar algum código, e depois nunca tocá-lo; então regenera-a a partir da fonte original quando precisar de atualização.

Às vezes, você gera código apenas uma vez e, em seguida, descarta a fonte original, e seguir em frente, manter a nova fonte.

Isso às vezes acontece quando o código é portado de um idioma para outro. Particularmente, se não se espera mais tarde passar por novas alterações no original (por exemplo, o código de idioma antigo não será mantido, ou está realmente completo (por exemplo, no caso de alguma funcionalidade matemática)).

Um caso comum é que escrever um gerador de código para fazer isso, só pode realmente traduzir 90% do código corretamente. e então os ú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 o Cython ou f2c ) produzem. Já que o objetivo é manter o código uma vez. Eles geralmente são feitos como um desconto, para fazer exatamente o que eles precisam. Em muitos aspectos, é a versão de nível seguinte do uso de um código regex / find-replace to port. "Porte assistido por ferramentas", pode-se dizer.

Gerando Código, apenas uma vez, por exemplo um rasto de site.

Intimamente relacionado é se você gerar o código de alguma fonte que você não deseja acessar novamente. Por exemplo. Se as ações necessárias para gerar o código não forem repetíveis, consistentes ou executá-las, será caro. Eu estou trabalhando em um par de projetos agora: DataDeps.jl e DataDepsGenerators.jl .

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

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

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

Importante, os usuários / desenvolvedores trabalhando com DataDeps.jl não precisam instalar ou usar o webscraper para usar o código RegistrationBlock que foi gerado. (E não precisar baixar e instalar um web-scraper economiza um bom tempo, especialmente para as execuções de CI)

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

    
por 30.11.2017 / 05:29
fonte
1

A geração de código "fonte" é uma indicação de uma deficiência da linguagem gerada. Está usando ferramentas para superar isso um anti-padrão? Absolutamente não - deixe-me explicar.

Normalmente, a geração de código é usada porque existe uma definição de nível mais alto que pode descrever o código resultante muito menos detalhado do que a linguagem de nível inferior. Então, a geração de código facilita a eficiência e a clareza.

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

Da mesma forma, usando uma DSL (linguagem específica de domínio), é possível escrever um texto conciso, mas talvez constringir código para uma tarefa específica. Isso tornará menos complicado gerar o comportamento correto do código. Lembre-se de que o código significa e termina . 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 que seja mais simples de manipular e entender. Se isso for satisfeito, não usar um gerador é um antipadrão . Esse antipadrão normalmente vem da noção de que o código "puro" é "mais limpo", da mesma forma que um trabalhador de madeira ou outro artesão pode procurar o uso de ferramentas elétricas ou o uso de CNC para "gerar" peças (pense < um martelo de ouro ).

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

    
por 02.12.2017 / 17:41
fonte
0

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

Os dois tipos mais comuns de dados que conheço e que estão integrados no código-fonte são informações gráficas sobre janelas (número e posicionamento de vários controles) e ORMs. Em ambos os casos, a integração via geração de código facilita a manipulação dos dados, porque você não precisa passar por etapas adicionais "especiais" 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. Usar esses recursos em seu aplicativo era mais difícil do que teria sido se o "formato binário" tivesse sido Pascal.

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

    
por 30.11.2017 / 02:57
fonte
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 para B, onde A é quase o mesmo idioma de B, mas com algumas extensões menores que podem ser feitas apenas codificando em A com menos esforço do que todas as ferramentas personalizadas e construindo o teste de A para B .

O trade off é mais proibitivo em relação à 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 metaprogramação por meio do processamento de texto externo.

O trade off também poderia ter a ver com a quantidade de uso. A linguagem A pode ser substancialmente diferente de B, mas todo o projeto com seu gerador de código customizado 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 , além do staging de compilação ao redor) excede a complexidade de uma solução que acabou de ser feita em B.

Basicamente, se nos comprometermos com a geração de código, provavelmente devemos "ir muito longe ou ir para casa": fazer com que tenha uma semântica substancial e usá-la muito ou não se incomode.

    
por 30.11.2017 / 18:04
fonte
0

Eu não vi isso claramente (eu o vi tocado por uma ou duas respostas, mas não parecia muito claro)

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

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

Na melhor das hipóteses, a edição do código gerado extrai um monte de códigos pobres em seu projeto (o conjunto de códigos INTEIRO agora é realmente SOURCE CODE - não há mais dados). Na pior das hipóteses, o código puxado para dentro do seu programa é um lixo altamente redundante, mal chamado, que é quase completamente inatingível.

Suponho que uma terceira categoria é o código que você usa uma vez (gerador de gui?) e depois edita para ajudar você a começar a aprender. Este é um pouco de cada - pode ser uma boa maneira de começar, mas o seu gerador de GUI será direcionado a usar o código "Gerável" que não será um ótimo começo para você como programador - Além disso, você pode ser tentado a usá-lo novamente para uma segunda GUI, o que significa extrair código SOURCE redundante em seu sistema.

Se o seu ferramental for inteligente o suficiente para proibir qualquer edição do código gerado, vá em frente. Se não, eu diria que é um dos piores anti-padrões lá fora.

    
por 01.12.2017 / 01:09
fonte
0

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

Dados são as informações exatamente na forma 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 que as máquinas dispensem os humanos do processamento de informações por si mesmos.

O descarregamento de 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 de mapeamento de informações intermediárias. Como geradores de código, compiladores, transpilers, etc.

why generate source code? Why not make it into a function which can accept parameters and act on them?

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

    
por 30.11.2017 / 10:43
fonte
0

If something can be generated, then that thing is data, not code.

Na medida em que você estipula mais tarde que o código é dado, sua proposição reduz para "Se algo pode ser gerado, então essa coisa não é código". Você diria, então, que o código assembly gerado por um compilador C não é código? E se acontecer de coincidir exatamente com o código de montagem que escrevo à mão? Você é bem-vindo para ir até lá se quiser, mas eu não irei com você.

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

Given that, isn't this whole idea of source code generation a misunderstanding?

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", então não deve haver nenhum problema conceitual com a geração de código em geral.

That is, if there is a code generator for something, then why not make that something a proper function which can receive the required parameters and do the right action that the "would generated" code would have done?

Bem, essa é uma questão totalmente diferente, sobre a razão para empregar a geração de código, em vez de sua natureza. Você está propondo a alternativa de que, em vez de escrever ou usar um gerador de código, alguém grave uma função que calcule o resultado diretamente. Mas em que idioma? Já se foram os dias em que alguém escreveu diretamente em código de máquina e, se você escreve seu código em qualquer outra linguagem, depende de um gerador de código na forma de um compilador e / ou assembler para produzir um programa que seja executado.

Por que, então, você prefere escrever em Java ou C ou Lisp ou o que for? Até montador? 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 acontece com a maioria dos geradores de código de nível superior também. Os casos protótipos são provavelmente geradores de analisadores e analisadores, como lex e yacc . Sim, você poderia escrever um scanner e um analisador diretamente em C ou em alguma outra linguagem de programação de sua escolha (mesmo código de máquina bruta), e às vezes um. Mas, para um problema de qualquer complexidade significativa, usar uma linguagem de propósito especial de nível superior, como lex ou yacc, torna o código escrito à mão mais fácil de escrever, ler e manter. 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 de C e a instanciação de modelos C ++ como exercícios na geração de código; você se opõe a isso? Se não, então eu acho que você precisará realizar alguma ginástica mental para racionalizar a aceitação daqueles, mas rejeitar outros sabores de geração de código.

If it is being done for performance reasons, then that sounds like a shortcoming of the compiler.

Por quê? Você está basicamente supondo que se 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 realizar o cálculo e emitir mais dados que chamamos de "saída". (De um certo ponto de vista, pode-se chamar um programa tão universal de "sistema operacional".) Mas por que você acha que um compilador deve ser tão eficaz na otimização de um programa de propósito geral quanto é otimizar um programa mais especializado? programa? Os dois programas têm características e capacidades diferentes.

If it is being done to bridge two languages, then that sounds like a lack of interface library.

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

Am I missing something here?

Várias coisas, eu acho.

I know that code is data as well. What I don't understand is, why generate source code? Why not make it into a function which can accept parameters and act on them?

Os geradores de código transformam o código escrito em um idioma para codificar 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, especialmente, por que eles poderiam querer misturar idiomas de níveis subjetivamente diferentes.

Mas eu já falei sobre isso. Escolhe-se uma linguagem para uma tarefa específica, baseada em parte em sua clareza e expressividade para essa tarefa. Na medida em que o código menor tem menos bugs, em média, e é mais fácil de manter, há também uma tendência para linguagens de nível mais alto, pelo menos para o trabalho em grande escala. Mas 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 tratadas de maneira mais eficaz ou mais concisa em outro. Usar a ferramenta certa para o trabalho, às vezes, significa empregar a geração de código.

    
por 01.12.2017 / 16:13
fonte
0

Respondendo a pergunta no contexto do seu comentário:

The compiler's duty is to take a code written in human-readable form and convert it to machine-readable form. Hence, if the compiler cannot create a code that is efficient, then the compiler is not doing its job properly. Is that wrong?

Um compilador nunca será otimizado para sua tarefa. A razão para isso é simples: é otimizada 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 maneira específica do domínio, fazendo concessões que os compiladores não conseguiram.

Como exemplo, trabalhei em software onde um analista pode precisar escrever algum código. Eles poderiam escrever seu algoritmo em C ++ e adicionar em todas as verificações de limites e truques de memoização dos quais dependem, mas isso requer conhecer um lote 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 eu posso fazer truques exóticos para maximizar o desempenho como a 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 eles obtenham o produto fora da porta mais facilmente do que qualquer ferramenta de propósito geral.

Eu também fiz exatamente o oposto. Eu tenho outro trabalho que fiz que tinha um mandato "sem geração de código". Nós ainda queríamos facilitar a vida daqueles que usam o software, então usamos grandes quantidades de metaprogramação de templates para fazer o compilador gerar o código na hora. Assim, eu só precisava da linguagem C ++ de uso geral para fazer o meu trabalho.

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

Se tivéssemos usado a geração de código, poderíamos ter recursos de manipulação de erros muito mais poderosos, com erros legíveis para humanos. C'est la vie.

    
por 02.12.2017 / 02:54
fonte
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 uma linguagem diferente como resultado de uma etapa no processo de compilação. Para o compilador típico, isso seria uma linguagem de nível mais baixo, mas poderia ser para outra linguagem de alto nível, como no caso das linguagens que compilam para JavaScript.
  • Gerando ou transformando código na linguagem de código-fonte como uma etapa no processo de compilação. Isto é o que macros faz.
  • Gerando código com uma ferramenta separadamente do processo de compilação normal. A saída disso é o código que vive como arquivos junto com o código fonte regular e é compilado junto com ele. Por exemplo, 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 é um passo intermediário que é muito claramente separado do código fonte. Mas na terceira forma não há separação formal entre o código-fonte e o código gerado, exceto que o código gerado provavelmente tem um comentário que diz "não edite este código". Ele ainda abre o risco de desenvolvedores editando 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 tipada estaticamente. Por exemplo, quando integração com entidades ORM, é realmente útil ter wrappers strongmente tipados para as tabelas do banco de dados. Claro que você poderia lidar com a integração dinamicamente em tempo de execução, mas perderia segurança de tipo e suporte de ferramenta (conclusão de código). Um benefício importante da linguagem de tipo estático é 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 predominante em linguagens dinamicamente tipificadas, já que em uma linguagem como essa não oferece nenhum benefício em comparação às conversões de tempo de execução.)

That is, if there is a code generator for something, then why not make that something a proper function which can receive the required parameters and do the right action that the "would generated" code would have done?

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 somente em tempo de execução.

Pode haver um meio termo: o F # suporta o conceito de provedores de tipos, que são basicamente interfaces strongmente tipadas geradas programaticamente em tempo de compilação. Esse conceito provavelmente poderia substituir muitos usos de geração de código e fornecer uma separação mais clara de preocupações.

    
por 02.12.2017 / 00:33
fonte
0

Os conjuntos de instruções do processador são fundamentalmente imperativos , mas as linguagens de programação podem ser declarativo . Executar um programa escrito em uma linguagem declarativa inevitavelmente requer algum tipo de geração de código. Como mencionado em esta resposta e outros, a principal razão para gerar código-fonte em uma linguagem legível para humanos é levar vantagem das sofisticadas otimizações realizadas pelos compiladores.

    
por 03.12.2017 / 21:52
fonte
-3

If something can be generated, then that thing is data, not code.

Você entendeu o caminho errado. Deve ler

Se algo puder ser inserido em um gerador para interpretáveis , então essa coisa é código, não dados.

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

    
por 30.11.2017 / 04:45
fonte