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?
flex
ou um analisador gerado porbison
quase 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).Respostas:
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).
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 .
É perfeitamente bom ter várias etapas na compilação, uma das quais pode ser a geração intermediária de código como texto.
Essa é uma maneira, mas existem outras.
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.
fonte
Raciocínio prático
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.).
fonte
Porque programar com cartões perfurados (ou códigos alternativos no bloco de notas ) é uma dor.
Verdadeiro. Não me importo com desempenho, a menos que seja forçado.
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.
fonte
/etc/
arquivos em Unix, etc.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
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.
fonte
Encontrei dois casos de uso para código gerado (em tempo de construção e nunca efetuado check-in):
fonte
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.
fonte
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?
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.
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".
fonte
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?
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.
fonte
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.
fonte
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 grandesstd::variant
em C ++ (se você tiver C ++ 17), algumas linguagens podem não ter esse recurso.fonte
É 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.
fonte
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.
fonte
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 :)
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?
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:
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.
fonte
À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
#include
no 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.
fonte
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.
fonte
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.
fonte
f2c
+cc
), mas o código resultante não era realmente um bom ponto de partida para uma versão C do programa, AFAIK.f2c
De volta ao dia)sed
percorre 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?.?.)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 ).
fonte
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.
fonte
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.
fonte
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.
fonte
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.
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?
fonte
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".
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.
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
lex
eyacc
. 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.
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.
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?
Várias coisas, eu acho.
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.
fonte
Respondendo à pergunta dentro do contexto do seu comentário:
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.
fonte
Existem algumas maneiras diferentes de usar a geração de código. Eles podem ser divididos em três grupos principais:
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.)
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.
fonte
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.
fonte
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.
fonte
gcc -fverbose-asm -O -S
não é código-fonte (e não é apenas ou principalmente dados), mesmo que seja alguma forma de texto sempre alimentada ao GNUas
e às vezes lida por humanos.