La generazione del codice sorgente è anti-pattern?

115

Se qualcosa può essere generato, quella cosa è data, non codice.

Detto questo, non è tutta questa idea del generare codice sorgente un fraintendimento? Cioè, se c'è un generatore di codice per qualcosa, allora perché non rendere quella cosa una funzione appropriata che può ricevere i parametri richiesti e fare la giusta azione che il codice "avrebbe generato" avrebbe fatto?

Se viene eseguito per motivi di prestazioni, sembra una lacuna del compilatore.

Se viene eseguito per collegare due lingue, allora sembra una mancanza di libreria di interfaccia.

Mi manca qualcosa qui?

So che il codice è anche un dato. Quello che non capisco è, perché generare il codice sorgente ? Perché non trasformarlo in una funzione in grado di accettare parametri e agire su di essi?

    
posta Utku 29.11.2017 - 04:51
fonte

27 risposte

144

Is source code generation an anti pattern?

Tecnicamente, se generiamo codice, non è source anche se è un testo leggibile dagli esseri umani. Codice sorgente è un codice originale, generato da una vera o altra vera intelligenza, non tradotto meccanicamente e non immediatamente riproducibile dalla (vera) fonte (direttamente o indirettamente).

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

Direi comunque che è dati . Anche il codice sorgente. Soprattutto il codice sorgente! Il codice sorgente è solo dati in un linguaggio progettato per svolgere attività di programmazione. Questi dati devono essere tradotti, interpretati, compilati, generati, se necessario, in altre forme - di dati - alcuni dei quali sono eseguibili.

Il processore esegue le istruzioni fuori memoria. La stessa memoria utilizzata per i dati. Prima che il processore esegua le istruzioni, il programma viene caricato nella memoria come dati .

Quindi tutto è dati , anche codice.

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

È perfettamente necessario avere più passaggi nella compilazione, uno dei quali può essere la generazione di un codice intermedio come testo.

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?

Questo è un modo, ma ce ne sono altri.

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

Non tutte le forme di testo sono destinate al consumo umano. In particolare, il codice generato (come testo) è tipicamente destinato al consumo del compilatore non al consumo umano.

Il codice sorgente è considerato l'originale: il master - ciò che modifichiamo & sviluppare; cosa archiviamo usando il controllo del codice sorgente. Il codice generato, anche quando il testo è leggibile dall'uomo, viene tipicamente rigenerato dal codice sorgente originale. Il codice generato, in generale, non deve essere sottoposto al controllo del codice sorgente poiché viene rigenerato durante la compilazione.

    
risposta data 29.11.2017 - 05:17
fonte
64

Ragionamento pratico

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

Da questa modifica, presumo tu stia chiedendo un livello piuttosto pratico, non teorico.

La ragione classica per generare codice sorgente in linguaggi statici come Java era che linguaggi come quello semplicemente non erano dotati di strumenti linguistici facili da usare per fare cose molto dinamiche. Ad esempio, nei giorni formativi di Java, semplicemente non era possibile creare facilmente una classe con un nome dinamico (corrispondente al nome di una tabella da un DB) e metodi dinamici (corrispondenti attributi da quella tabella) con tipi di dati dinamici (corrispondenti i tipi di detti attributi). Specialmente dal momento che Java attribuisce una grande importanza, anzi, garanzie, alla possibilità di rilevare errori di tipo in fase di compilazione.

Quindi, in tale impostazione, un programmatore può solo creare codice Java e scrivere molte righe di codice manualmente. Spesso, il programmatore troverà che ogni volta che cambia una tabella, deve tornare indietro e cambiare il codice per farlo corrispondere; e se lo dimentica, accadono cose brutte. Quindi, il programmatore arriverà al punto in cui scrive alcuni strumenti che lo fanno per lui. E quindi la strada inizia a generare codice sempre più intelligente.

(Sì, potresti generare il bytecode al volo, ma programmare una cosa simile in Java non sarebbe qualcosa che un programmatore casuale farebbe solo tra la scrittura di alcune righe di codice di dominio.)

Confrontalo con linguaggi molto dinamici, ad esempio Ruby, che considererei l'antitesi per Java sotto molti aspetti (notare che sto dicendo questo senza valutare nessuno dei due approcci: sono semplicemente diversi). Qui è al 100% normale e standard generare dinamicamente classi, metodi ecc. In fase di esecuzione e, cosa più importante, il programmatore può farlo banalmente nel codice, senza andare su un livello "meta". Sì, cose come Ruby on Rails arrivano con la generazione del codice, ma abbiamo trovato nel nostro lavoro che fondamentalmente lo usiamo come una sorta di "modalità tutorial" avanzata per i nuovi programmatori, ma dopo un po 'diventa superfluo (dato che c'è così poco codice scrivere in quell'ecosistema che quando sai cosa stai facendo, scriverlo manualmente diventa più veloce della pulizia del codice generato).

Questi sono solo due esempi pratici del "mondo reale". Quindi hai lingue come LISP in cui il codice è dati, letteralmente. D'altra parte, in linguaggi compilati (senza un motore di runtime come Java o Ruby), c'è (o ero, non ho tenuto il passo con le moderne funzionalità di C ++ ...) semplicemente nessun concetto di definizione di nomi di classi o di metodi in fase di runtime, quindi la generazione del codice il processo di compilazione è lo strumento di scelta per la maggior parte delle cose (altri esempi specifici di C / C ++ potrebbero essere cose come flex, yacc ecc.).

    
risposta data 29.11.2017 - 08:40
fonte
44

why generate code?

Perché programmare con schede perforate (o codici alt nel blocco note ) è un dolore.

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

È vero. Non mi importa delle prestazioni a meno che non sia costretto a farlo.

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

Hmm, non ho idea di cosa stai parlando.

Guarda come questo: il codice sorgente generato e mantenuto è sempre e per sempre un dolore nel sedere. Esiste solo per una ragione. Qualcuno vuole lavorare in una lingua mentre qualcun altro insiste a lavorare in un'altra e nessuno dei due può essere preso la briga di capire come interagire tra di loro in modo che uno di loro capisca come trasformare la propria lingua preferita nella lingua imposta in modo da poter fare cosa loro vogliono.

Che va bene finché non devo mantenerlo. A quel punto puoi andare tutti a morire.

È un pattern anti? Sigh, no. Molte lingue non esisterebbero nemmeno se non fossimo disposti a dire addio alle carenze delle lingue precedenti e generare il codice delle lingue più vecchie è il numero di nuove lingue che iniziano.

È una base di codice che è rimasta in un patchwork di mostri di Frankenstein metà convertito che non sopporto. Il codice generato è un codice intoccabile. Odio guardare il codice intoccabile. Eppure la gente continua a controllarlo. PERCHÉ? Si potrebbe anche verificare l'eseguibile.

Bene, ora sto sbraitando. Il mio punto è che stiamo tutti "generando codice". È quando tratti codice generato come un codice sorgente che mi stai facendo impazzire. Solo perché sembra che il codice sorgente non lo rende codice sorgente.

    
risposta data 29.11.2017 - 05:47
fonte
41

why generate source code

Il caso d'uso più frequente per i generatori di codice con cui ho dovuto lavorare nella mia carriera sono stati i generatori che

  • ha richiesto una meta-descrizione di alto livello per alcuni tipi di modello di dati o di schemi di database come input (magari uno schema relazionale o qualche tipo di schema XML)

  • e ha prodotto il codice CRUD della piastra di caldaia per le classi di accesso ai dati come output e forse cose aggiuntive come SQL corrispondenti o documentazione.

Il vantaggio qui è che da una riga di una specifica di input breve si ottengono da 5 a 10 righe di debugable, type-safe, senza bug (presupposto che l'output dei generatori di codice sia maturo) che altrimenti bisognava implementare e mantenere manualmente . Puoi immaginare quanto questo riduce la manutenzione e lo sforzo di evoluzione.

Lascia che risponda anche alla tua domanda iniziale

Is source code generation an anti pattern

No, non la generazione di codice sorgente di per sé, ma ci sono davvero alcune insidie. Come indicato in The Pragmatic Programmer , si dovrebbe evitare l'uso di un generatore di codice quando produce codice che è difficile capire . In caso contrario, l'aumento degli sforzi per utilizzare o eseguire il debug di questo codice potrebbe facilmente superare lo sforzo salvato non scrivendo il codice manualmente.

Vorrei anche aggiungere che è più volte una buona idea separare parti di codice generate da codice scritto manualmente fisicamente in un modo che la rigenerazione non sovrascrive alcuna modifica manuale. Tuttavia, ho anche affrontato la situazione più di una volta quando il compito era quello di migrare del codice scritto nella vecchia lingua X in un'altra, più moderna lingua Y, con l'intenzione di proseguire il lavoro in lingua Y. Questo è un uso valido caso per la generazione di codice una tantum.

    
risposta data 29.11.2017 - 08:19
fonte
13

why generate source code?

Ho riscontrato due casi d'uso per codice generato (in fase di compilazione e mai archiviato):

  1. Genera automaticamente codice boilerplate come getter / setter, toString, equals e hashCode da un linguaggio creato per specificare tali elementi (ad esempio project lombok per Java)
  2. Genera automaticamente classi di tipo DTO da alcune specifiche di interfaccia (REST, SOAP, qualunque) per poi essere utilizzate nel codice principale. Questo è simile al problema del tuo bridge di lingua, ma finisce per essere più pulito e semplice, con una migliore gestione dei caratteri rispetto al tentativo di implementare la stessa cosa senza classi generate.
risposta data 29.11.2017 - 06:16
fonte
13

Sussmann ha avuto molte cose interessanti da dire su queste cose nella sua classica "Struttura e interpretazione dei programmi per computer", principalmente sulla dualità del codice dati.

Per me il principale uso della generazione di codice ad hoc consiste nell'utilizzare un compilatore disponibile per convertire un po 'di linguaggio specifico di dominio in qualcosa che posso collegare ai miei programmi. Pensa a BNF, pensa ASN1 (in realtà, no, è brutto), pensa ai fogli di calcolo dei dizionari di dati.

I linguaggi specifici del dominio banale possono essere un enorme risparmio di tempo, e l'output di qualcosa che può essere compilato da strumenti linguistici standard è la strada da seguire quando si creano cose simili, che preferireste modificare, un parser non hackerato a mano in qualunque nativo il linguaggio che stai scrivendo o il BNF per uno generato automaticamente?

Eseguendo l'output del testo che viene poi inserito in un compilatore di sistema, ottengo tutta l'ottimizzazione dei compilatori e la configurazione specifica del sistema senza dovermi pensare.

Uso efficacemente la lingua di input del compilatore come un'altra rappresentazione intermedia, qual è il problema? I file di testo non sono intrinsecamente codice sorgente, possono essere un IR per un compilatore , e se capita di apparire come C o C ++ o Java o altro, a chi importa?

Ora, se sei difficile pensare potresti modificare l'OUTPUT del parser del linguaggio giocattolo, che chiaramente deluderà la prossima volta che qualcuno modifica i file della lingua di input e ricostruisce, la risposta è non impegnarsi l'IR generato automaticamente al repository, lo ha generato dalla tua toolchain (ed evita di avere queste persone nel tuo gruppo di sviluppatori, di solito sono più felici lavorando nel marketing).

Questo non è tanto un fallimento dell'espressività nelle nostre lingue, quanto un'espressione del fatto che a volte puoi ottenere (o massaggiare) parti della specifica in una forma che può essere automaticamente convertita in codice, e che di solito generare molti meno bug ed essere molto più facili da mantenere. Se posso dare ai nostri test e configurazione ragazzi un foglio di calcolo che possono modificare e uno strumento che poi eseguono che prende quei dati e sputa un file esadecimale completo per il flash sulla mia ECU, quindi è un enorme risparmio di tempo rispetto al fatto che qualcuno traduca manualmente l'ultima installazione in un insieme di costanti nella lingua del giorno (completa di errori di battitura).

La stessa cosa con i modelli di costruzione in Simulink e quindi la generazione di C con RTW, quindi la compilazione per il targeting con qualsiasi strumento abbia senso, il C intermedio è illeggibile, e allora? La roba di alto livello di Matlab RTW ha solo bisogno di conoscere un sottoinsieme di C, e il compilatore C si occupa dei dettagli della piattaforma. L'unica volta che un essere umano deve attraversare la C generata è quando gli script RTW hanno un bug, e questo genere di cose è molto più facile da eseguire il debug con un IR nominale nominalmente umano, quindi con solo un albero di analisi binario.

Ovviamente puoi scrivere tali cose per generare codice bytecode o anche eseguibile, ma perché lo faresti? Abbiamo strumenti per convertire un IR in queste cose.

    
risposta data 29.11.2017 - 15:16
fonte
12

Risposta pragmatica: la generazione del codice è necessaria e utile? Fornisce qualcosa che è veramente molto utile e necessario per la base di codice proprietaria, o sembra semplicemente creare un altro modo di fare le cose in un modo che contribuisca a un sovraccarico intellettuale per risultati non ottimali?

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 devi fare questa domanda e non c'è una risposta chiara, probabilmente la generazione del codice è superflua e si limita a contribuire con l'esotismo e una grande quantità di spese intellettuali alla tua base di codice.

Nel frattempo se prendi qualcosa come OpenShadingLanguage: link

... quindi queste domande non devono essere sollevate poiché vengono immediatamente risposte dagli impressionanti risultati.

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.)

In tal caso non è necessario mettere in discussione l'esistenza del generatore di codice. Se lavori in questo tipo di dominio VFX, la tua risposta immediata di solito è più simile a "stai zitto e prendi i miei soldi!" oppure, "wow, dobbiamo anche fare qualcosa di simile."

    
risposta data 29.11.2017 - 05:28
fonte
8

No, la generazione di codice intermedio non è un anti-modello. La risposta all'altra parte della tua domanda, "Perché farlo?", È una domanda molto ampia (e separata), anche se fornirò comunque alcune ragioni.

Le ramificazioni storiche di non avere mai codice intermedio leggibile dall'uomo

Prendiamo C e C ++ come esempi poiché sono tra le lingue più famose.

Bisogna notare che la processione logica della compilazione degli output del codice C non è codice macchina ma piuttosto codice assembly leggibile da umani. Allo stesso modo, i vecchi compilatori C ++ erano soliti compilare fisicamente codice C ++ in codice C. In questa catena di eventi, è possibile compilare dal codice 1 leggibile dall'uomo al codice 2 leggibile dall'uomo al codice 3 leggibile dall'uomo al codice macchina. "Perché?" Perché no?

Se il codice intermedio leggibile dall'uomo non è mai stato generato, non potremmo nemmeno avere C o C ++. Questa è certamente una possibilità; le persone prendono la via della minor resistenza ai loro obiettivi, e se qualche altra lingua ha guadagnato vapore prima a causa della stagnazione dello sviluppo C, C potrebbe essere morta mentre era ancora giovane. Certo, potresti discutere "Ma forse potremmo usare un altro linguaggio, e forse sarebbe meglio". Forse, o forse sarebbe peggio. O forse staremmo ancora scrivendo in assemblea.

Perché utilizzare un codice intermedio leggibile dall'uomo?

  1. A volte è necessario il codice intermedio per poterlo modificare prima del prossimo passo nella costruzione. Ammetto che questo punto è il più debole.
  2. A volte è perché il lavoro originale non è stato eseguito in alcun linguaggio leggibile dall'uomo, ma in uno strumento di modellazione della GUI.
  3. A volte devi fare qualcosa di molto ripetitivo, e la lingua non dovrebbe soddisfare ciò che stai facendo perché è una cosa di nicchia o una cosa così complicata che non ha business aumentare la complessità o la grammatica del linguaggio di programmazione solo per ospitarvi.
  4. A volte devi fare qualcosa di molto ripetitivo, e non è possibile per ottenere ciò che vuoi nella lingua in modo generico; o non può essere rappresentato da o in conflitto con la grammatica della lingua.
  5. Uno degli obiettivi dei computer è ridurre lo sforzo umano, e talvolta il codice che è improbabile che venga mai toccato di nuovo (bassa probabilità di manutenzione) può avere codice meta-codice scritto per generare il codice più lungo in un decimo di tempo; se posso farlo in 1 giorno invece che in 2 settimane e non è probabile che venga mantenuto sempre, allora è meglio che lo generi - e nella remota possibilità che qualcuno da 5 anni sia infastidito perché in realtà fa devono mantenerlo, quindi possono passare le 2 settimane a scriverlo completamente se lo desiderano, o essere infastiditi di una settimana dal mantenimento del codice scomodo (ma siamo ancora 1 settimana avanti a quel punto), e questo è se quella manutenzione deve essere eseguita a tutti.
  6. Sono sicuro che ci sono altre ragioni che trascuro.

Esempio

Ho lavorato su progetti prima di dove il codice deve essere generato sulla base di dati o informazioni in qualche altro documento. Ad esempio, un progetto aveva tutti i suoi messaggi di rete e dati costanti definiti in un foglio di calcolo e uno strumento che passava attraverso il foglio di calcolo e generava un lotto di C ++ e codice Java che ci permetteva di lavorare con quei messaggi .

Non sto dicendo che quello era il modo migliore per impostare quel progetto (non ero parte della sua startup), ma era quello che avevamo, ed erano centinaia (forse anche migliaia, non sono sicuro) di strutture e oggetti e costanti che venivano generati; a quel punto è probabilmente troppo tardi per provare a rifarlo in qualcosa come Rhapsody. Ma anche se fosse stato rifatto in qualcosa come Rhapsody, allora abbiamo ancora il codice generato da Rhapsody comunque .

Inoltre, avere tutti i dati in un foglio di calcolo era buono in un modo: ci permetteva di rappresentare i dati in modi che non potevamo avere se fossero tutti solo nei file del codice sorgente.

Esempio 2

Quando ho fatto un po 'di lavoro nella costruzione del compilatore, ho usato lo strumento Antlr per fare il lexing e l'analisi. Ho specificato una grammatica linguistica, poi ho usato lo strumento per sputare un sacco di codice in C ++ o Java, poi ho usato quel codice generato insieme al mio codice personale e l'ho incluso nella compilazione.

In quale altro modo dovrebbe essere stato fatto? Forse potresti venire con un altro modo; probabilmente ci sono altri modi. Ma per quel lavoro, gli altri modi non sarebbero stati migliori del codice lex / parse generato.

    
risposta data 29.11.2017 - 21:41
fonte
7

Ciò che ti manca è riutilizzo .

Abbiamo uno strumento straordinario per trasformare il testo del codice sorgente in binario, chiamato compilatore. I suoi input sono ben definiti (di solito!), Ed è stato attraverso un sacco di lavoro per perfezionare come fa l'ottimizzazione. Se in realtà vuoi utilizzare il compilatore per eseguire alcune operazioni, devi utilizzare un compilatore esistente e non scrivere il tuo.

Molte persone inventano nuovi linguaggi di programmazione e scrivono i propri compilatori. Praticamente senza eccezioni, lo fanno tutti perché si divertono con la sfida, non perché hanno bisogno delle funzionalità fornite da quel linguaggio. Tutto ciò che fanno potrebbe essere fatto in un'altra lingua; stanno semplicemente creando un nuovo linguaggio perché a loro piacciono queste caratteristiche. Ciò che tuttavia non li otterrà è un compilatore ben sintonizzato, veloce, efficiente e ottimizzante. Otterrà loro qualcosa che può trasformare il testo in binario, certo, ma non sarà buono come tutti i compilatori esistenti .

Il testo non è solo qualcosa che gli umani leggono e scrivono. I computer sono perfettamente a loro agio anche con il testo. In effetti formati come XML (e altri formati correlati) hanno successo perché usano semplicemente testo. I formati di file binari sono spesso oscuri e scarsamente documentati e un lettore non può facilmente scoprire come funzionano. XML è relativamente auto-documentante, rendendo più facile per le persone scrivere codice che utilizza file in formato XML. E tutti i linguaggi di programmazione sono impostati per leggere e scrivere file di testo.

Quindi, supponiamo di voler aggiungere una nuova funzione per semplificarti la vita. Forse è uno strumento di layout GUI. Forse sono le interfacce signal-and-slot che fornisce Qt . Forse è il modo in cui Code Composer Studio di TI ti consente di configurare il dispositivo con cui stai lavorando e di inserire le librerie giuste nel costruire. Forse sta prendendo un dizionario di dati e generando automaticamente typedef e definizioni di variabili globali (sì, questo è ancora molto importante nel software embedded). Qualunque cosa sia, il modo più efficiente per sfruttare il compilatore esistente è creare uno strumento che assuma la tua configurazione di qualunque cosa sia e produca automaticamente il codice nella tua lingua preferita.

È facile da sviluppare e facile da testare, perché sai cosa sta succedendo e puoi leggere il codice sorgente che sputa. Non è necessario spendere anni uomo per costruire un compilatore per rivaleggiare con GCC. Non è necessario imparare una nuova lingua completa o richiedere ad altre persone di farlo. Tutto quello che devi fare è automatizzare questa piccola area, e tutto il resto rimane lo stesso. Lavoro fatto.

    
risposta data 29.11.2017 - 13:06
fonte
7

Una risposta un po 'più pragmatica, concentrandosi sul perché e non su ciò che è e non è il codice sorgente. Nota che la generazione del codice sorgente fa parte del processo di compilazione in tutti questi casi, quindi i file generati non dovrebbero trovare la loro strada nel controllo del codice sorgente.

Interoprability / semplicità

Prendi i buffer del protocollo di Google, un primo esempio: scrivi una singola descrizione del protocollo di alto livello che può essere poi utilizzata per generare l'implementazione in più lingue - spesso diverse parti del sistema sono scritte in lingue diverse.

Implementazione / motivi tecnici

Prendi TypeScript - i browser non possono interpretarlo in modo che il processo di compilazione utilizzi un transpiler (codice per il traduttore del codice) per generare JavaScript. Infatti molti linguaggi compilati nuovi o esoterici iniziano con il transpiling in C prima che ottengano un compilatore appropriato.

Facilità d'uso

Per i progetti embedded (penso IoT) scritti in C e usando solo un singolo binario (RTOS o nessun SO) è abbastanza facile generare un array C con i dati da compilare come se il codice sorgente normale, come opzionato per il collegamento direttamente in quanto risorse.

Modifica

Espansione su protobuf: la generazione del codice consente agli oggetti generati di essere classi di prima classe in qualsiasi lingua. In un linguaggio compilato un parser generico restituirebbe necessariamente una struttura di valori-chiave - il che significa che si ha un codice molto buono, si perdono alcuni controlli in fase di compilazione (su chiavi e tipi di valori in particolare), prestazioni peggiori e nessun completamento del codice. Immagina tutti quei void* in C o quell'enorme std::variant in C ++ (se hai C ++ 17), alcune lingue potrebbero non avere affatto tale funzionalità.

    
risposta data 29.11.2017 - 08:35
fonte
6

Is source code generation an anti pattern?

È un work-around per un linguaggio di programmazione non sufficientemente espressivo. Non è necessario generare codice in una lingua che contenga una meta-programmazione integrata adeguata.

    
risposta data 29.11.2017 - 08:43
fonte
6

La generazione del codice sorgente non è sempre un anti-modello. Ad esempio, sto attualmente scrivendo un framework che, con specifiche date, genera codice in due lingue diverse (Javascript e Java). Il framework utilizza il Javascript generato per registrare le azioni del browser dell'utente e utilizza il codice Java in Selenium per eseguire effettivamente l'azione quando il framework è in modalità replay. Se non avessi usato la generazione del codice, dovrei assicurarmi manualmente che entrambi siano sempre sincronizzati, il che è ingombrante ed è anche una duplicazione logica in qualche modo.

Se tuttavia si utilizza la generazione di codice sorgente per la sostituzione di funzionalità come i generici, allora è anti-pattern.

    
risposta data 29.11.2017 - 09:03
fonte
6

Am I missing something here?

Forse un buon esempio in cui il codice intermedio si è rivelato essere la ragione del successo? Posso offrirti HTML.

Credo che fosse importante che HTML fosse semplice e statico: rendeva facile creare browser, permetteva di avviare i browser mobili in anticipo, ecc. Come dimostrano ulteriori esperimenti (applet Java, Flash): più potenti e complessi linguaggi portano a più problemi Si scopre che gli utenti sono effettivamente minacciati dalle applet Java e visitare tali siti web è sicuro quanto provare crack di giochi scaricati tramite DC ++. Il semplice HTML, d'altra parte, è abbastanza innocuo da permetterci di controllare qualsiasi sito con ragionevole certezza nella sicurezza del nostro dispositivo.

Tuttavia, l'HTML non sarebbe affatto vicino a dove si trova ora se non fosse stato generato dal computer. La mia risposta non comparirebbe nemmeno su questa pagina finché qualcuno non lo ha riscritto manualmente dal database in un file HTML. Fortunatamente puoi rendere l'HTML utilizzabile in quasi tutti i linguaggi di programmazione:)

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?

Riesci a immaginare un modo migliore per visualizzare la domanda e tutte le risposte e i commenti per l'utente piuttosto che usare l'HTML come codice intermedio generato?

    
risposta data 29.11.2017 - 13:37
fonte
3

why generate source code?

Perché è più veloce e più facile (e meno incline agli errori) della scrittura manuale del codice, soprattutto per compiti noiosi e ripetitivi. Puoi anche utilizzare lo strumento di alto livello per verificare e convalidare il tuo progetto prima di scrivere una singola riga di codice.

Casi di utilizzo comune:

  • Strumenti di modellazione come Rose o Visual Paradigm;
  • linguaggi di livello alto er come Embedded SQL o un linguaggio di definizione dell'interfaccia che deve essere preelaborato in qualcosa di compilabile;
  • Generatori di Lexer e parser come flex / bison;

Per quanto riguarda il tuo "perché non basta renderlo una funzione e passare direttamente i parametri", nota che nessuno dei suddetti ambienti di esecuzione è di per sé. Non c'è modo di collegare il tuo codice contro di loro.

    
risposta data 29.11.2017 - 19:23
fonte
2

A volte, il tuo linguaggio di programmazione non ha i servizi che desideri, rendendo davvero impossibile scrivere funzioni o macro per fare ciò che vuoi. O forse potresti fare ciò che vuoi, ma il codice per scriverlo sarebbe brutto. Un semplice script Python (o simile) può quindi generare il codice richiesto come parte del processo di compilazione, che quindi #include nel file sorgente reale.

Come faccio a saperlo? Perché è una soluzione che ho raggiunto più volte lavorando con vari sistemi diversi, più di recente SourcePawn. Un semplice script Python che analizza una semplice riga di codice sorgente e produce due o tre righe di codice generato è molto meglio che creare manualmente il codice generato, quando si finiscono con due dozzine di righe (creando tutte le mie cvars).

Codice sorgente dimostrativo / di esempio disponibile se le persone lo vogliono.

    
risposta data 29.11.2017 - 13:36
fonte
1

È richiesto un modulo di testo per un facile utilizzo da parte degli esseri umani. I computer elaborano anche il codice in forma di testo abbastanza facilmente. Pertanto il codice generato dovrebbe essere generato nella forma che è più facile da generare e più facile da consumare dai computer, e questo è un testo molto spesso leggibile.

E quando generi codice, spesso il processo di generazione del codice deve essere sottoposto a debug, dagli umani. È molto, molto utile se il codice generato è leggibile dall'uomo in modo che gli utenti possano rilevare i problemi nel processo di generazione del codice. Qualcuno deve scrivere il codice per generare il codice, dopotutto. Non succede dal nulla.

    
risposta data 29.11.2017 - 08:22
fonte
1

Generazione del codice, solo una volta

Non tutta la generazione di codice sorgente è un caso di generare del codice, e quindi non toccarlo mai; quindi rigenerarlo dalla fonte originale quando è necessario aggiornarlo.

A volte si genera codice solo una volta, quindi si elimina la fonte originale, e andando avanti mantieni la nuova fonte.

Questo a volte accade quando si porta il codice da una lingua all'altra. In particolare se non ci si aspetta di voler effettuare il porting successivo su nuove modifiche nell'originale (ad esempio, il vecchio codice non verrà mantenuto, o sarà effettivamente completo (ad esempio nel caso di alcune funzionalità matematiche)).

Un caso comune è che scrivere un generatore di codice per fare ciò, potrebbe tradurre solo il 90% del codice correttamente. e quindi quell'ultimo 10% deve essere riparato a mano. Molto più veloce della traduzione 100% a mano.

Tali generatori di codice sono spesso molto diversi dal tipo di generatori di codice che producono i traduttori full language (come Cython o f2c ). Poiché l'obiettivo è quello di rendere il codice di manutenzione una volta. Sono spesso fatti come un 1 fuori, per fare esattamente quello che devono. In molti modi è la versione di livello successivo dell'uso di un regex / find-replace al codice di porta. "Porting assistito da strumenti" che potresti dire.

Generazione del codice, una sola volta, ad es. un sito web di scrapbook.

Strettamente correlato è se si genera il codice da una fonte che non si desidera accedere nuovamente. Per esempio. Se le azioni necessarie per generare il codice non sono ripetibili o coerenti, o eseguirle è costoso. Sto lavorando a un paio di progetti in questo momento: DataDeps.jl e DataDepsGenerators.jl .

DataDeps.jl aiuta gli utenti a scaricare i dati (come set di dati standard ML). Per fare questo ha bisogno di ciò che chiamiamo un RegistrationBlock. Questo è un codice che specifica alcuni metadati, come dove scaricare i file da, e un checksum, e un messaggio che spiega all'utente qualsiasi termine / codifica / quale sia lo stato della licenza sui dati.

Scrivere quei blocchi può essere fastidioso. E tali informazioni sono spesso disponibili in (strutturate o non strutturate) dai siti web in cui sono ospitati i dati. Quindi DataDepsGenerators.jl, utilizza un webscraper per generare il RegistrationBlockCode, per alcuni siti che ospitano molti dati.

Potrebbe non generarli correttamente. Quindi lo sviluppatore che usa il codice generato può e dovrebbe controllarlo e correggerlo. Le probabilità sono che vogliano assicurarsi che non abbia mancato le informazioni sulla licenza, ad esempio.

È importante sottolineare che gli utenti / sviluppatori che lavorano con DataDeps.jl non hanno bisogno di installare o utilizzare il webscraper per utilizzare il codice RegistrationBlock che è stato generato. (E non è necessario scaricare e installare un web-raschietto per risparmiare un bel po 'di tempo, in particolare per le esecuzioni di CI)

Generare codice sorgente una volta non è un antipattern. e normalmente non può essere sostituito con metaprogrammazione.

    
risposta data 30.11.2017 - 05:29
fonte
1

La generazione del codice "sorgente" è un'indicazione di una mancanza della lingua che viene generata. Sta usando gli strumenti per superare questo anti-pattern? Assolutamente no, lasciami spiegare.

Generalmente la generazione del codice viene utilizzata perché esiste una definizione di livello superiore che può descrivere il codice risultante in modo molto meno dettagliato della lingua di livello inferiore. Quindi la generazione del codice facilita l'efficienza e la precisione.

Quando scrivo c ++, lo faccio perché mi consente di scrivere codice più efficiente rispetto all'utilizzo di assembler o codice macchina. Il codice macchina è ancora generato dal compilatore. All'inizio, c ++ era semplicemente un preprocessore che generava il codice C. Le lingue di uso generale sono ideali per generare un comportamento generico.

Allo stesso modo, utilizzando un DSL (linguaggio specifico del dominio) è possibile scrivere in modo terso, ma forse il codice è ristretto ad un compito specifico. Ciò renderà meno complicato generare il comportamento corretto del codice. Ricorda che il codice è un mezzo e finisce . Quello che uno sviluppatore sta cercando è un modo efficace per generare comportamenti.

Idealmente il generatore può creare codice veloce da un input più semplice da manipolare e capire. Se questo è soddisfatto non usare un generatore è un anti-pattern . Questo anti-pattern deriva in genere dall'idea che il codice "puro" è "più pulito", allo stesso modo in cui un lavoratore del legno o un altro artigiano potrebbe guardare all'uso di utensili elettrici, o usare il CNC per "generare" pezzi (si pensi < a href="https://en.wikipedia.org/wiki/Law_of_the_instrument"> golden hammer ).

D'altra parte, se l'origine del codice generato è più difficile da mantenere o generare codice che non è abbastanza efficiente, l'utente cade nella trappola dell'uso di strumenti sbagliati (a volte a causa dello stesso golden hammer ).

    
risposta data 02.12.2017 - 17:41
fonte
0

Generazione del codice sorgente significa assolutamente che il codice generato è dato. Ma sono dati di prima classe, dati che il resto del programma può manipolare.

I due tipi più comuni di dati di cui sono a conoscenza sono integrati nel codice sorgente sono informazioni grafiche su Windows (numero e posizione di vari controlli) e ORM. In entrambi i casi l'integrazione tramite la generazione del codice semplifica la manipolazione dei dati, perché non è necessario passare attraverso passaggi "speciali" per utilizzarli.

Quando lavoravamo con i Mac originali (1984), le definizioni di finestre e finestre sono state create usando un editor reso che manteneva i dati in un formato binario. L'utilizzo di queste risorse nella tua applicazione è stato più difficile di quanto sarebbe stato se il "formato binario" fosse stato Pascal.

Quindi, no, la generazione del codice sorgente non è un anti-pattern, ma consente di rendere i dati parte dell'applicazione, il che rende più facile l'utilizzo.

    
risposta data 30.11.2017 - 02:57
fonte
0

La generazione del codice è un anti-modello quando costa più di quanto non lo sia. Questa situazione si verifica quando la generazione avviene da A a B dove A è quasi la stessa lingua di B, ma con alcune estensioni minori che potrebbero essere fatte semplicemente codificando in A con meno sforzo di tutti gli strumenti personalizzati e la gestione temporanea di build per A a B .

Il trade off è più proibitivo contro la generazione di codice in linguaggi che non dispongono di strutture di meta-programmazione (macro strutturali) a causa delle complicazioni e delle inadeguatezze nel raggiungere la metaprogrammazione attraverso la messa in scena dell'elaborazione esterna del testo.

Lo scarso trade off potrebbe anche avere a che fare con la quantità di utilizzo. Il linguaggio A potrebbe essere sostanzialmente diverso da B, ma l'intero progetto con il suo generatore di codice personalizzato utilizza solo A in una o due piccole posizioni, in modo tale che la complessità totale (piccoli bit di A, più il generatore di codice A - > B , oltre alla messa in scena di build circostante) supera la complessità di una soluzione appena eseguita in B.

Fondamentalmente, se ci impegniamo a generare il codice, dovremmo probabilmente "andare alla grande o tornare a casa": fare in modo che abbia una semantica sostanziosa e usarlo molto o non preoccuparti.

    
risposta data 30.11.2017 - 18:04
fonte
0

Non l'ho visto chiaramente (l'ho visto toccato da una o due risposte, ma non sembrava molto chiaro)

Generare codice (come hai detto, come se si trattasse di dati) non è un problema - è un modo per riutilizzare un compilatore per uno scopo secondario.

Modificare il codice generato è uno dei più insidiosi, cattivi, orripilanti anti-schemi che tu abbia mai incontrato. Non farlo.

Al massimo, la modifica del codice generato estrae un mucchio di codice scadente nel tuo progetto (l'INTERA serie di codici è ora veramente CODICE SORGENTE, non più dati). Nel peggiore dei casi, il codice inserito nel programma è una spazzatura ridondante e mal denominata, quasi completamente non gestibile.

Suppongo che una terza categoria sia il codice che usi una volta (generatore di gui?), quindi modifica per aiutarti a iniziare / imparare. Questo è un po 'di ciascuno - può essere un buon modo per iniziare, ma il tuo generatore di GUI sarà mirato all'utilizzo del codice "Generativo" che non sarà un ottimo inizio per te come programmatore - Inoltre, potresti essere tentato di usarlo di nuovo per una seconda GUI che significa estrarre codice SOURCE ridondante nel tuo sistema.

Se la tua attrezzatura è abbastanza intelligente da impedire qualsiasi modifica del codice generato, fallo. Altrimenti, lo chiamerei uno dei peggiori anti-pattern là fuori.

    
risposta data 01.12.2017 - 01:09
fonte
0

Codice e dati sono entrambi: Informazioni.

I dati sono le informazioni esattamente nel formato che ti serve (e valore). Il codice è anche informazione, ma in forma indiretta o intermedia. In sostanza, il codice è anche una forma di dati.

Più in particolare, il codice è informazioni per le macchine che scaricano gli umani dall'elaborare le informazioni da soli.

Lo scaricamento di esseri umani dall'elaborazione delle informazioni è il motivo più importante. I passaggi intermedi sono accettabili a patto che rendano la vita facile. Ecco perché esistono strumenti di mappatura delle informazioni intermedi. Come generatori di codice, compilatori, transporter, ecc.

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

Supponiamo che qualcuno ti offra tale funzione di mappatura, la cui implementazione è oscura per te. Finché la funzione funziona come promesso, ti importerebbe se internamente generi codice sorgente o no?

    
risposta data 30.11.2017 - 10:43
fonte
0

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

Dato che in seguito si stabilisce che il codice è costituito da dati, la tua proposizione si riduce a "Se qualcosa può essere generato, allora quella cosa non è codice." Diresti, quindi, che il codice assembly generato da un compilatore C non è un codice? E se accadesse che coincidesse esattamente con il codice assembly che scrivo a mano? Sei libero di andarci se lo desideri, ma non verrò con te.

Iniziamo invece con una definizione di "codice". Senza essere troppo tecnico, una buona definizione per gli scopi di questa discussione sarebbe "istruzioni utilizzabili dalla macchina per eseguire un calcolo."

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

Ebbene sì, la tua proposta di partenza è che il codice non può essere generato, ma io rifiuto quella proposta. Se accetti la mia definizione di "codice", non ci dovrebbero essere problemi concettuali con la generazione di codice in generale.

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?

Questa è una domanda completamente diversa, sulla ragione per l'utilizzo della generazione di codice, piuttosto che sulla sua natura. Stai proponendo un'alternativa che invece di scrivere o utilizzare un generatore di codice, uno scrive una funzione che calcola direttamente il risultato. Ma in che lingua? Sono finiti i tempi in cui chiunque scriveva direttamente in codice macchina e se scrivete il vostro codice in qualsiasi altra lingua, dipendete da un generatore di codice sotto forma di compilatore e / o assemblatore per produrre un programma effettivamente eseguito.

Perché, allora, preferisci scrivere in Java o C o Lisp o altro? Anche assemblatore? Asserisco che è almeno in parte perché tali linguaggi forniscono astrazioni per dati e operazioni che rendono più facile esprimere i dettagli del calcolo che si desidera eseguire.

Lo stesso vale per la maggior parte dei generatori di codice di livello superiore. I casi prototipici sono probabilmente generatori di scanner e parser come lex e yacc . Sì, è possibile scrivere uno scanner e un parser direttamente in C o in un altro linguaggio di programmazione di propria scelta (anche codice macchina grezzo), e talvolta lo si fa. Ma per un problema di qualsiasi complessità significativa, l'uso di un linguaggio di alto livello, per scopi speciali come lex o yacc, rende il codice scritto a mano più facile da scrivere, leggere e mantenere. Di solito anche molto più piccolo.

Dovresti anche considerare cosa intendi esattamente per "generatore di codice". Prenderò in considerazione la preelaborazione in C e l'istanziazione di modelli C ++ come esercizi nella generazione del codice; ti opponi a questi? Altrimenti, penso che dovrai eseguire alcuni esercizi di ginnastica mentale per razionalizzare l'accettazione di quelli, ma rifiutare altri sapori della generazione del codice.

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

Perché? In pratica, si presuppone che si debba avere un programma universale a cui l'utente alimenta i dati, alcuni classificati come "istruzioni" e altri come "input", e che procede per eseguire il calcolo ed emettere più dati che chiamiamo "output". (Da un certo punto di vista, si potrebbe chiamare un tale programma universale un "sistema operativo".) Ma perché si suppone che un compilatore debba essere altrettanto efficace nell'ottimizzare un programma di questo tipo per scopi generali quanto nell'ottimizzare un sistema più specializzato programma? I due programmi hanno caratteristiche e capacità diverse.

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

Lo dici come se avere una libreria di interfaccia universale a qualche grado sarebbe necessariamente una buona cosa. Forse lo sarebbe, ma in molti casi una tale biblioteca sarebbe grande e difficile da scrivere e mantenere, e forse anche rallentare. E se una tale bestia in effetti non esiste per servire il particolare problema in questione, allora chi sei tu per insistere affinché venga creato, quando un approccio di generazione del codice può risolvere il problema molto più rapidamente e facilmente?

Am I missing something here?

Molte cose, penso.

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?

I generatori di codice trasformano il codice scritto in una lingua in codice in un linguaggio diverso, di solito di livello inferiore. Stai chiedendo, quindi, perché le persone vorrebbero scrivere programmi che utilizzano più lingue e, soprattutto, perché potrebbero voler mescolare linguaggi di livelli soggettivamente differenti.

Ma l'ho già toccato. Si sceglie una lingua per un particolare compito basato in parte sulla sua chiarezza ed espressività per quel compito. Dato che il codice più piccolo ha in media meno errori ed è più facile da mantenere, c'è anche un pregiudizio verso i linguaggi di livello superiore, almeno per il lavoro su larga scala. Ma un programma complesso comporta molti compiti, e spesso alcuni di essi possono essere affrontati più efficacemente in una lingua, mentre altri sono affrontati in modo più efficace o più conciso in un'altra. Utilizzare lo strumento giusto per il lavoro a volte significa impiegare la generazione del codice.

    
risposta data 01.12.2017 - 16:13
fonte
0

Rispondere alla domanda nel contesto del tuo commento:

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?

Un compilatore non sarà mai ottimizzato per la tua attività. Il motivo è semplice: è ottimizzato per eseguire molte attività. È uno strumento generico utilizzato da molte persone per molti compiti diversi. Una volta che sai qual è il tuo compito, puoi approcciare il codice in un modo specifico del dominio, facendo compromessi che i compilatori non potrebbero.

Ad esempio, ho lavorato su software in cui un analista potrebbe aver bisogno di scrivere del codice. Potrebbero scrivere il loro algoritmo in C ++ e aggiungere tutti i trucchi dei limiti e gli appunti di memoizzazione da cui dipendono, ma ciò richiede la conoscenza di un lotto sui meccanismi interni del codice. Preferirebbero scrivere qualcosa di semplice e lasciarmi generare un algoritmo per generare il codice finale C ++. Poi posso fare trucchi esotici per massimizzare le prestazioni come l'analisi statica che non mi aspetterei mai che i miei analisti possano sopportare. La generazione del codice consente loro di scrivere in un modo specifico del dominio che consente loro di portare il prodotto fuori dalla porta più facilmente di qualsiasi altro strumento per scopi generici.

Ho anche fatto l'esatto opposto. Ho un altro lavoro che ho svolto che ha avuto un mandato "nessuna generazione di codice". Volevamo ancora semplificare la vita a chi utilizzava il software, quindi abbiamo utilizzato enormi quantità di metaprogrammazione dei modelli per fare in modo che il compilatore generasse il codice al volo. Quindi, avevo solo bisogno del linguaggio C ++ per scopi generali per svolgere il mio lavoro.

Tuttavia, c'è un problema. Era tremendamente difficile garantire che gli errori fossero leggibili. Se hai già utilizzato il codice metaprogrammo del modello in precedenza, sai che un singolo errore innocente può generare un errore che richiede 100 righe di nomi di classi incomprensibili e argomenti del modello per capire cosa è andato storto. Questo effetto era così pronunciato che il processo di debug raccomandato per gli errori di sintassi era "Scorri il log degli errori finché non vedi la prima volta che uno dei tuoi file ha un errore. Vai su quella linea, e strizza l'occhio fino a quando non ti rendi conto di ciò che ha sbagliato. "

Se avessimo usato la generazione di codice, avremmo potuto avere capacità di gestione degli errori molto più potenti, con errori leggibili dall'uomo. C'est la vie.

    
risposta data 02.12.2017 - 02:54
fonte
0

Ci sono alcuni modi diversi di usare la generazione del codice. Potrebbero essere divisi in tre gruppi principali:

  • Generazione di codice in una lingua diversa come output da una fase del processo di compilazione. Per il compilatore tipico questo sarebbe un linguaggio di livello inferiore, ma potrebbe essere per un altro linguaggio di alto livello come nel caso delle lingue che compilano in JavaScript.
  • Generazione o trasformazione del codice nella lingua del codice sorgente come passo nel processo di compilazione. Questo è ciò che macro fa.
  • Generazione di codice con uno strumento separatamente dal normale processo di compilazione. L'output di questo è un codice che vive come file insieme al normale codice sorgente e viene compilato insieme ad esso. Ad esempio, le classi di entità per un ORM potrebbero essere generate automaticamente da uno schema di database, oppure oggetti di trasferimento dati e interfacce di servizio potrebbero essere generati da una specifica di interfaccia come un file WSDL per SOAP.

Immagino che tu stia parlando del terzo tipo di codice generato, poiché questa è la forma più controverso. Nelle prime due forme il codice generato è un passaggio intermedio che è separato molto chiaramente dal codice sorgente. Ma nella terza forma non c'è una separazione formale tra il codice sorgente e il codice generato, ad eccezione del codice generato che probabilmente ha un commento che dice "non modificare questo codice". Si ferma il rischio che gli sviluppatori modifichino il codice generato che sarebbe davvero brutto. Dal punto di vista del compilatore, il codice generato è il codice sorgente.

Tuttavia, tali forme di codice generato possono essere davvero utili in un linguaggio tipizzato staticamente. Ad esempio, quando si integra con le entità ORM, è davvero utile disporre di wrapper strongmente tipizzati per le tabelle del database. Certo, potrebbe gestire l'integrazione dinamicamente in fase di runtime, ma si perderebbe la sicurezza del tipo e il supporto degli strumenti (completamento del codice). Uno dei principali vantaggi del linguaggio di tipo staticamente è il supporto del sistema di tipi al tipo di scrittura piuttosto che solo in fase di runtime. (Viceversa, questo tipo di generazione di codice non è molto diffuso nelle lingue digitate dinamicamente, poiché in tale linguaggio non offre alcun vantaggio rispetto alle conversioni di runtime.)

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?

Poiché la sicurezza del tipo e il completamento del codice sono funzionalità che si desiderano al momento della compilazione (e durante la scrittura del codice in un IDE), ma le funzioni regolari vengono eseguite solo in fase di runtime.

Tuttavia potrebbe esserci una via di mezzo: F # supporta il concetto di provider di tipi che è fondamentalmente interfacce strongmente tipizzate generate a livello di codice in fase di compilazione. Questo concetto potrebbe probabilmente sostituire molti usi della generazione del codice e fornire una separazione più chiara dei problemi.

    
risposta data 02.12.2017 - 00:33
fonte
0

I set di istruzioni del processore sono fondamentalmente imperativo , ma i linguaggi di programmazione possono essere declarative . L'esecuzione di un programma scritto in un linguaggio dichiarativo richiede inevitabilmente un certo tipo di generazione del codice. Come menzionato in questa risposta e altri, una delle principali ragioni per generare codice sorgente in un linguaggio leggibile dall'uomo è prendere vantaggio delle sofisticate ottimizzazioni eseguite dai compilatori.

    
risposta data 03.12.2017 - 21:52
fonte
-3

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

L'hai capito nel modo sbagliato. Dovrebbe leggere

Se qualcosa può essere inserito in un generatore per interpretabili , allora quella cosa è il codice, non i dati.

È il formato sorgente per quella fase di compilazione e il formato sink è ancora codice.

    
risposta data 30.11.2017 - 04:45
fonte

Leggi altre domande sui tag