Czy generowanie kodu źródłowego jest anty-wzorcem?

118

Jeśli coś można wygenerować, to chodzi o dane, a nie kod.

Biorąc to pod uwagę, czy ten cały pomysł generowania kodu źródłowego nie jest nieporozumieniem? To znaczy, jeśli istnieje jakiś generator kodu, to dlaczego nie uczynić tego czymś właściwym, które może otrzymać wymagane parametry i wykonać właściwe działanie, które wykonałby kod „wygenerowany”?

Jeśli robi się to ze względu na wydajność, brzmi to jak wada kompilatora.

Jeśli robi się to w celu połączenia dwóch języków, brzmi to jak brak biblioteki interfejsów.

Czy coś mi umyka?

Wiem, że kod to także dane. Nie rozumiem tylko, po co generować kod źródłowy ? Dlaczego nie przekształcić go w funkcję, która akceptuje parametry i działa na nie?

Utku
źródło
11
Termin związany z generowaniem kodu to metaprogramowanie
UselesssCat
4
en.wikipedia.org/wiki/Code_as_data , Lisp, FP, skryptowanie, metaprogramowanie, Von Neumann / zmodyfikowana architektura Harvarda itp. Zostało to objęte reklamą . tl; dr rozróżnienie „kod źródłowy” vs „kod wyjściowy”, „kod” vs „dane” itp. mają na celu uproszczenie . Nigdy nie powinni być dogmatyczni .
vaxquis
9
@Utku, lepsze powody generowania kodu często dotyczą chęci przedstawienia opisu wyższego poziomu, niż jest w stanie wyrazić Twój obecny język . To, czy kompilator może, czy nie może tworzyć wydajnego kodu, tak naprawdę nie ma z tym nic wspólnego. Rozważmy generatory parsera - leksykon wygenerowany przez flexlub parser wygenerowany przez bisonprawie na pewno będzie bardziej przewidywalny, bardziej poprawny i często szybszy do wykonania niż odpowiedniki napisane ręcznie w C; i zbudowany z dużo mniejszej ilości kodu (a więc również mniej pracy do utrzymania).
Charles Duffy,
1
Być może pochodzisz z języka, który nie ma wielu elementów funkcjonalnych, ale w wielu językach funkcje są pierwszej klasy - możesz je przekazywać, więc w tego rodzaju językach kod to dane i możesz traktować to w ten sposób.
Restioson,
1
@Restioson w kodzie języka funkcjonalnego nie jest danymi. Funkcje pierwszej klasy oznaczają dokładnie to, że: Funkcje są danymi. I niekoniecznie szczególnie dobre dane: niekoniecznie musisz zmutować je trochę (powiedzmy, zmutuj wszystkie dodatki w funkcjach na odejmowania). Kod to dane w językach homoiconic. (większość języków homoiconic ma funkcje pierwszej klasy. Ale odwrotność nie jest prawdą.).
Lyndon White,

Odpowiedzi:

149

Czy generowanie kodu źródłowego jest anty-wzorcem?

Technicznie, jeśli generujemy kod, nie jest on źródłem, nawet jeśli jest to tekst, który jest czytelny dla ludzi. Kod źródłowy to oryginalny kod, generowany przez człowieka lub inną prawdziwą inteligencję, nie tłumaczony mechanicznie i nie dający się natychmiast odtworzyć z (prawdziwego) źródła (bezpośrednio lub pośrednio).

Jeśli coś można wygenerować, to rzeczą są dane, a nie kod.

Powiedziałbym, że i tak wszystko jest danymi . Nawet kod źródłowy. Zwłaszcza kod źródłowy! Kod źródłowy to tylko dane w języku zaprojektowanym do realizacji zadań programistycznych. Dane te należy tłumaczyć, interpretować, kompilować, generować w razie potrzeby na inne formy - danych - z których niektóre mogą być wykonywane.

Procesor wykonuje instrukcje z pamięci. Ta sama pamięć, która jest używana dla danych. Zanim procesor wykona instrukcje, program jest ładowany do pamięci jako dane .

Tak więc wszystko to dane , nawet kod .

Biorąc pod uwagę, że [generowany kod to dane], czy ten pomysł generowania kodu nie jest nieporozumieniem?

Kompilacja wielu kroków jest w porządku, z których jednym może być pośrednie generowanie kodu jako tekstu.

To znaczy, jeśli istnieje jakiś generator kodu, to dlaczego nie uczynić tego czymś właściwym, które może otrzymać wymagane parametry i wykonać właściwe działanie, które wykonałby kod „wygenerowany”?

To jeden sposób, ale są też inne.


Generacją kodu jest tekst, który jest przeznaczony do użycia przez człowieka.

Nie wszystkie formularze tekstowe są przeznaczone do spożycia przez ludzi. W szczególności wygenerowany kod (jako tekst) jest zwykle przeznaczony do konsumpcji przez kompilator, a nie do konsumpcji przez ludzi.


Kod źródłowy jest uważany za oryginalny: master - to, co edytujemy i rozwijamy; co archiwizujemy za pomocą kontroli kodu źródłowego. Wygenerowany kod, nawet jeśli tekst czytelny dla człowieka, jest zazwyczaj generowany z oryginalnego kodu źródłowego . Generowany kod, ogólnie mówiąc, nie musi być pod kontrolą źródła, ponieważ jest generowany podczas kompilacji.

Erik Eidt
źródło
1
Komentarze nie są przeznaczone do rozszerzonej dyskusji; ta rozmowa została przeniesiona do czatu .
wałek klonowy
65

Praktyczne rozumowanie

OK, wiem, że kod to także dane. Nie rozumiem tylko, po co generować kod źródłowy?

Z tej edycji zakładam, że pytasz na raczej praktycznym poziomie, a nie teoretycznej informatyki.

Klasycznym powodem generowania kodu źródłowego w językach statycznych, takich jak Java, było to, że takie języki po prostu tak naprawdę nie miały łatwych w użyciu narzędzi w języku do robienia bardzo dynamicznych rzeczy. Na przykład w czasach tworzenia Javy po prostu nie było możliwe łatwe utworzenie klasy o nazwie dynamicznej (dopasowanie nazwy tabeli z bazy danych) i metod dynamicznych (dopasowanie atrybutów z tej tabeli) z dynamicznymi typami danych (dopasowanie rodzaje wspomnianych atrybutów). Zwłaszcza, że Java przywiązuje dużą wagę, nie, gwarantuje, że jest w stanie wychwycić błędy typu w czasie kompilacji.

Zatem w takim ustawieniu programista może jedynie tworzyć kod Java i ręcznie pisać wiele wierszy kodu. Często programista przekona się, że za każdym razem, gdy zmienia się tabela, musi wrócić i zmienić kod, aby dopasować; a jeśli zapomni o tym, zdarzają się złe rzeczy. Dlatego programista przejdzie do momentu, w którym napisze kilka narzędzi, które to dla niego zrobią. I tak droga zaczyna się coraz bardziej inteligentnie generować kod.

(Tak, można wygenerować kod bajtowy w locie, ale programowanie takiej rzeczy w Javie nie byłoby czymś, co przypadkowy programista zrobiłby tylko pomiędzy napisaniem kilku linii kodu domeny.)

Porównaj to z bardzo dynamicznymi językami, na przykład Ruby, który pod wieloma względami uważałbym za antytezę dla Javy (zauważ, że mówię to nie doceniając żadnego z tych podejść; są po prostu różne). Tutaj jest w 100% normalne i standardowe, aby dynamicznie generować klasy, metody itp. W czasie wykonywania, a co najważniejsze, programista może to zrobić trywialnie bezpośrednio w kodzie, bez przechodzenia na poziom „meta”. Tak, rzeczy takie jak Ruby on Rails pochodzą z generowania kodu, ale stwierdziliśmy w naszej pracy, że zasadniczo używamy tego jako pewnego rodzaju zaawansowanego „trybu samouczka” dla nowych programistów, ale po pewnym czasie staje się zbędny (ponieważ jest tak mało kodu pisać w tym ekosystemie, że kiedy wiesz, co robisz, ręczne pisanie staje się szybsze niż czyszczenie wygenerowanego kodu).

To tylko dwa praktyczne przykłady z „prawdziwego świata”. Następnie masz języki takie jak LISP, gdzie kod to dane, dosłownie. Z drugiej strony, w skompilowanych językach (bez silnika wykonawczego, takiego jak Java czy Ruby), nie ma (lub było, nie nadążałem za nowoczesnymi funkcjami C ++ ...) po prostu nie ma pojęcia definiowania nazw klas lub metod w czasie wykonywania, tak więc generowanie kodu proces kompilacji jest narzędziem z wyboru dla większości rzeczy (innymi bardziej specyficznymi przykładami dla C / C ++ są takie jak flex, yacc itp.).

AnoE
źródło
1
Myślę, że jest to lepsze niż bardziej głosowane odpowiedzi. W szczególności przykład wspomniany przy programowaniu w Javie i bazach danych znacznie lepiej radzi sobie z tym, dlaczego używa się generowania kodu i jest ważnym narzędziem.
Panzercrisis
Czy w dzisiejszych czasach w Javie można tworzyć tabele dynamiczne z bazy danych? Czy tylko przy użyciu ORM?
Noumenon,
„(a może nie nadążałem za nowoczesnymi funkcjami C ++ ...)” na pewno było to możliwe w C ++ od ponad dwóch dekad dzięki wskaźnikom funkcji? Nie testowałem tego, ale jestem pewien, że powinno być możliwe przydzielenie tablicy znaków, wypełnienie jej kodem maszynowym, a następnie rzutowanie wskaźnika na pierwszy element do wskaźnika funkcji, a następnie uruchomienie go? (Zakładając, że platforma docelowa nie ma żadnych środków bezpieczeństwa, które powstrzymałyby cię przed zrobieniem tego, co może zrobić).
Pharap
1
„przydzielić tablicę znaków, wypełnić ją kodem maszynowym, a następnie rzucić wskaźnik na pierwszy element na wskaźnik funkcji, a następnie uruchomić go?” Poza niezdefiniowanym zachowaniem, jest to C ++ odpowiednik „generowania kodu bajtowego w locie”. Należy do tej samej kategorii „nieuwzględnionych przez zwykłych programistów”
Caleth
1
@Pharap, „z pewnością było to możliwe w C ++ od ponad dwóch dekad”… musiałem się trochę zaśmiać; minęły około 2 dekady, odkąd ostatnio kodowałem C ++. :) Ale moje zdanie o C ++ zostało sformułowane źle. Trochę go zmieniłem, teraz powinno być bardziej zrozumiałe.
AnoE,
44

po co generować kod?

Ponieważ programowanie kartami dziurkowanymi (lub kodami alt w notatniku ) jest uciążliwe.

Jeśli robi się to ze względu na wydajność, brzmi to jak wada kompilatora.

Prawdziwe. Nie dbam o wydajność, chyba że jestem do tego zmuszony.

Jeśli robi się to w celu połączenia dwóch języków, brzmi to jak brak biblioteki interfejsów.

Hmm, nie mam pojęcia o czym mówisz.

Wygląda to tak: Wygenerowany i zachowany kod źródłowy jest zawsze i na zawsze uciążliwy. Istnieje tylko z jednego powodu. Ktoś chce pracować w jednym języku, podczas gdy ktoś inny nalega na pracę w innym, a nikomu nie przeszkadza, aby wymyślić sposób współpracy między nimi, więc jeden z nich zastanawia się, jak zamienić swój ulubiony język na język narzucony, aby mogli zrobić to, co chcą.

Co jest w porządku, dopóki nie będę musiał go utrzymać. W którym momencie wszyscy możecie umrzeć.

Czy to jest anty-wzór? Westchnienie, nie. Wiele języków nawet by nie istniało, gdybyśmy nie chcieli pożegnać się z wadami poprzednich języków, a generowanie kodu starszych języków jest początkiem liczby nowych języków.

To podstawa kodu, która pozostała w pół przekształconym patchworku potwora Frankensteina, którego nie znoszę. Wygenerowany kod jest nietykalnym kodem. Nienawidzę patrzeć na nietykalny kod. Jednak ludzie ciągle to sprawdzają. DLACZEGO? Równie dobrze możesz sprawdzać plik wykonywalny.

Cóż, teraz mam na myśli. Chodzi mi o to, że wszyscy „generujemy kod”. To kiedy traktujesz wygenerowany kod jak kod źródłowy, doprowadzasz mnie do szału. Tylko dlatego, że wygląda na to, że kod źródłowy nie czyni go kodem źródłowym.

candied_orange
źródło
41
Jeśli go wygenerujesz, nie będzie to kod SOURCE. To kod pośredni. Idę teraz płakać.
candied_orange
65
ARG !!! Nie ważne jak to wygląda !!! Tekst, binarny, DNA, jeśli nie jest to ŹRÓDŁO, to nie jest to, co powinieneś dotknąć podczas wprowadzania zmian. To nie interes, jeśli mój proces kompilacji ma 42 języki pośrednie, przez które przechodzi. Przestań ich dotykać. Przestań je rejestrować. Wprowadź zmiany u źródła.
candied_orange
24
XML to tekst i po prostu nie jest przeznaczony do spożycia przez ludzi. :-)
Nick Keighley
38
@utku: „Jeśli coś nie jest przeznaczone do spożycia przez człowieka, nie powinien to być tekst”: Całkowicie się nie zgadzam. Niektóre kontrprzykłady z góry mojej głowy: protokół HTTP, kodowanie MIME, pliki PEM - prawie wszystko, co używa base64 gdziekolwiek. Istnieje wiele powodów, aby kodować dane w 7-bitowym bezpiecznym strumieniu, nawet jeśli nikt nie powinien ich nigdy zobaczyć. Nie wspominając już o znacznie większej przestrzeni rzeczy, z którymi normalnie człowiek nigdy nie powinien wchodzić w interakcje, ale które mogą chcieć od czasu do czasu: pliki dziennika, /etc/pliki w
systemie
12
Nie sądzę, że „programowanie kartami dziurkowanymi” oznacza to, co według ciebie oznacza. Byłem tam, zrobiłem to i tak, to był ból; ale nie ma połączenia z „wygenerowanym kodem”. Talia perforowanych kart to po prostu inny rodzaj pliku - na przykład plik na dysku, plik na taśmie lub plik na karcie SD. Wcześniej zapisywaliśmy dane na pokładach kart i czytaliśmy dane z nich. Jeśli więc generujemy kod, ponieważ programowanie kartami dziurkującymi jest uciążliwe, oznacza to, że programowanie przy użyciu dowolnego rodzaju przechowywania danych jest uciążliwe.
Solomon Slow
41

po co generować kod źródłowy

Najczęstszym przypadkiem użycia generatorów kodu, z którymi musiałem pracować w swojej karierze, były generatory, które

  • wziął jako dane wejściowe jakiś meta-opis wysokiego poziomu dla jakiegoś modelu danych lub schematu bazy danych (może to być schemat relacyjny lub jakiś schemat XML)

  • i wygenerował kod CRUD płyty kotłowej dla klas dostępu do danych jako dane wyjściowe, a może dodatkowe rzeczy, takie jak odpowiednie zapytania SQL lub dokumentacja.

Korzyścią jest to, że z jednego wiersza krótkiej specyfikacji wejściowej otrzymujesz od 5 do 10 wierszy debugowalnego, bezpiecznego typu, wolnego od błędów (zakładając, że dane wyjściowe generatorów kodu są dojrzałe) kodu, który w innym przypadku musiałbyś zaimplementować i utrzymywać ręcznie. Możesz sobie wyobrazić, jak bardzo zmniejsza to nakłady na utrzymanie i rozwój.

Pozwól, że odpowiem również na twoje pierwsze pytanie

Generowanie kodu źródłowego jest anty-wzorcem

Nie, nie generowanie kodu źródłowego per se, ale rzeczywiście istnieją pewne pułapki. Jak stwierdzono w The Pragmatic Programmer , należy unikać używania generatora kodu, gdy wytwarza on kod trudny do zrozumienia . W przeciwnym razie zwiększone wysiłki związane z użyciem lub debugowaniem tego kodu mogą łatwo przewyższyć wysiłek zaoszczędzony przez ręczne pisanie kodu.

Chciałbym również dodać, że zazwyczaj dobrym pomysłem jest oddzielenie generowanych części kodu od kodu napisanego ręcznie, w taki sposób, aby ponowne generowanie nie zastępowało żadnych ręcznych zmian. Jednak kilkakrotnie poradziłem sobie z sytuacją, w której zadaniem było migrowanie kodu napisanego w starym języku X do innego, bardziej nowoczesnego języka Y, z zamiarem późniejszego utrzymania go w języku Y. Jest to prawidłowe użycie przypadek generowania kodu jednorazowego.

Doktor Brown
źródło
Zgadzam się z tą odpowiedzią. Używając czegoś takiego jak Torque dla java, mogę automatycznie generować pliki źródłowe java, z polami pasującymi do bazy danych SQL. To sprawia, że ​​operacje crud są znacznie łatwiejsze. Główną zaletą jest bezpieczeństwo typu, w tym tylko możliwość odniesienia do pól istniejących w bazie danych (dziękuję, autouzupełnianie).
MTilsted,
Tak, w przypadku języków wpisywanych statycznie jest to ważna część: możesz upewnić się, że odręczny kod rzeczywiście pasuje do wygenerowanego.
Paŭlo Ebermann
„migruj część kodu napisanego w starym języku” - nawet wtedy jednorazowe wygenerowanie kodu może być dużym problemem. Na przykład po kilku ręcznych zmianach wykrywasz błąd w generatorze i musisz ponownie wygenerować generację po poprawce. Na szczęście git lub podobne mogą zwykle złagodzić ból.
maaartinus,
13

po co generować kod źródłowy?

Napotkałem dwa przypadki użycia wygenerowanego (w czasie kompilacji i nigdy nie zalogowanego) kodu:

  1. Automatycznie generuj kod główny, taki jak getters / setters, toString, equals i hashCode z języka zbudowanego w celu określenia takich rzeczy (np. Lombok projektu dla Javy)
  2. Automatycznie generuj klasy typu DTO z niektórych specyfikacji interfejsu (REST, SOAP, cokolwiek), aby następnie użyć ich w kodzie głównym. Jest to podobne do problemu z mostkiem językowym, ale ostatecznie jest czystsze i prostsze, z lepszą obsługą typów niż próba zaimplementowania tego samego bez wygenerowanych klas.
Może czynnik
źródło
15
Wysoce powtarzalny kod w niewyrażalnych językach. Na przykład musiałem napisać kod, który tak samo zrobił na wielu podobnych, ale nie identycznych strukturach danych. To prawdopodobnie mógłby zrobić coś jak C ++ szablonu (hej nie jest to generowanie kodu?). Ale korzystałem z C. Generowanie kodu uratowało mi pisanie wielu niemal identycznych kodów.
Nick Keighley,
1
@NickKeighley Być może twój zestaw narzędzi nie pozwalał ci używać innego, bardziej odpowiedniego języka?
Wilson
7
Zazwyczaj nie można wybrać języka implementacji. Projekt był w C, co nie było opcją.
Nick Keighley
1
@Wilson, bardziej ekspresyjne języki często używają generowania kodu (np. Makra lisp, ruby ​​na szynach), w międzyczasie nie wymagają zapisywania jako tekstu.
Pete Kirkham
4
Tak, generowanie kodu jest zasadniczo metaprogramowaniem. Języki takie jak Ruby pozwalają na wykonywanie metaprogramowania w samym języku, ale C nie, więc zamiast tego musisz używać generowania kodu.
Sean Burton
13

Sussmann miał wiele ciekawych rzeczy do powiedzenia na temat takich rzeczy w swoim klasycznym „Strukturze i interpretacji programów komputerowych”, głównie o dualności kod-dane.

Dla mnie głównym zastosowaniem generowania kodu adhoc jest wykorzystanie dostępnego kompilatora do konwersji jakiegoś małego języka specyficznego dla domeny na coś, co mogę połączyć z moimi programami. Pomyśl o BNF, pomyśl o ASN1 (Właściwie nie, to jest brzydkie), pomyśl o arkuszach kalkulacyjnych słownika danych.

Ciekawe języki specyficzne dla domeny mogą znacznie zaoszczędzić czas, a tworzenie czegoś, co można skompilować za pomocą standardowych narzędzi językowych, jest właściwą drogą przy tworzeniu takich rzeczy, które wolisz edytować, niebanalny parser zhakowany ręcznie w dowolnym języku ojczystym, jakim jesteś. pisanie czy BNF dla automatycznie wygenerowanego?

Wysyłając tekst, który jest następnie podawany do jakiegoś kompilatora systemowego, uzyskuję optymalizację tych kompilatorów i konfigurację specyficzną dla systemu bez konieczności myślenia o tym.

Skutecznie używam języka wejściowego kompilatora jako kolejnej pośredniej reprezentacji, na czym polega problem? Pliki tekstowe nie są z natury kodami źródłowymi, mogą być IR dla kompilatora , a jeśli wyglądają jak C, C ++, Java lub cokolwiek innego, kogo to obchodzi?

Teraz, jeśli nie myślisz , że możesz edytować WYJŚCIE analizatora składni języka zabawek, co wyraźnie rozczaruje, gdy następnym razem ktoś edytuje pliki języka wejściowego i przebudowuje, odpowiedzią jest, aby nie zatwierdzać automatycznie wygenerowanej podczerwieni do repozytorium, generowane przez Twój zestaw narzędzi (I unikaj posiadania takich osób w grupie deweloperów, zwykle są bardziej zadowoleni z pracy w marketingu).

W naszych językach nie jest to tak bardzo porażką ekspresyjności, jak wyrazem faktu, że czasami można uzyskać (lub masować) części specyfikacji w formie, która może zostać automatycznie przekonwertowana na kod, i która zwykle spłodzi znacznie mniej błędów i być o wiele łatwiejszym w utrzymaniu. Jeśli mogę dać naszym testerom i konfiguratorom arkusz kalkulacyjny, który mogą ulepszyć, a następnie uruchomić narzędzie, które pobiera te dane i wyrzuca pełny plik heksadecymalny dla Flasha na moim ECU, to ogromna oszczędność czasu na ręcznym tłumaczeniu najnowsza konfiguracja w zestawie stałych w języku dnia (w komplecie literówki).

To samo dotyczy budowania modeli w Simulink, a następnie generowania C za pomocą RTW, a następnie kompilowania do celu za pomocą dowolnego narzędzia sensownego, pośrednie C jest nieczytelne, więc co? Wysoki poziom Matlab RTW musi znać tylko podzbiór języka C, a kompilator C zajmuje się szczegółami platformy. Jedyny raz człowiek musi przeszukiwać wygenerowane C, gdy skrypty RTW mają błąd, a tego rodzaju rzeczy jest o wiele łatwiejsze do debugowania za pomocą nominalnie czytelnej dla człowieka podczerwieni niż tylko binarnego drzewa parsowania.

Możesz oczywiście napisać takie rzeczy do kodu bajtowego lub nawet kodu wykonywalnego, ale dlaczego miałbyś to zrobić? Mamy narzędzia do konwersji IR na te rzeczy.

Dan Mills
źródło
To dobrze, ale dodam, że przy ustalaniu, który IR należy użyć, istnieje kompromis: użycie C jako IR sprawia, że ​​niektóre rzeczy są łatwiejsze, a inne trudniejsze w porównaniu, powiedzmy, z językiem asemblera x86. Wybór jest jeszcze bardziej znaczący przy wyborze między, powiedzmy, kodem języka Java a kodem bajtu Java, ponieważ istnieje wiele innych operacji, które istnieją tylko w jednym lub drugim języku.
Daniel Pryden
2
Ale język asemblera X86 powoduje słabą podczerwień podczas celowania w rdzeń ARM lub PPC! Wszystkie rzeczy są kompromisem w inżynierii, dlatego nazywają to inżynierią. Można by mieć nadzieję, że możliwości kodu bajtowego Java były ścisłym nadzorem nad możliwościami języka Java i że jest to ogólnie prawdziwe, gdy zbliżasz się do metalu niezależnie od łańcucha narzędzi i miejsca, w którym wstrzykujesz IR.
Dan Mills
Och, całkowicie się zgadzam: mój komentarz był odpowiedzią na ostatnie pytanie, dlaczego kiedykolwiek wypisałeś kod bajtowy lub coś na niższym poziomie - czasami potrzebujesz niższego poziomu. (Szczególnie w Javie istnieje wiele przydatnych rzeczy, które można zrobić za pomocą kodu bajtowego, których nie można zrobić w samym języku Java.)
Daniel Pryden
2
Nie zgadzam się, ale korzystanie z podczerwieni bliżej metalu jest nie tylko kosztem, ale nie tylko ogólności, ale również faktu, że zwykle jesteś odpowiedzialny za bardziej irytującą optymalizację niskiego poziomu. Fakt, że w dzisiejszych czasach myślimy raczej o optymalizacji wyboru algorytmu niż o implementacji, jest odbiciem tego, jak daleko zaszły kompilatory, czasem trzeba bardzo zbliżyć się do metalu w tych rzeczach, ale zastanów się dwa razy, zanim wyrzucisz kompilatory możliwość optymalizacji przy użyciu zbyt niskiego poziomu podczerwieni.
Dan Mills,
1
„zazwyczaj są bardziej zadowoleni z pracy w marketingu” Catty, ale zabawne.
dmckee,
13

Pragmatyczna odpowiedź: czy generowanie kodu jest konieczne i przydatne? Czy zapewnia coś, co jest naprawdę bardzo przydatne i potrzebne dla zastrzeżonej bazy kodów, czy też wydaje się, że po prostu tworzy inny sposób robienia rzeczy w sposób, który przyczynia się do większego intelektualnego obciążenia dla uzyskania nieoptymalnych wyników?

OK, wiem, że kod to także dane. Nie rozumiem tylko, po co generować kod? Dlaczego nie przekształcić go w funkcję, która akceptuje parametry i działa na nie?

Jeśli musisz zadać to pytanie i nie ma jednoznacznej odpowiedzi, prawdopodobnie generowanie kodu jest zbędne i jedynie przyczynia się do egzotyki oraz znacznego intelektualnego obciążenia bazy kodowej.

Tymczasem jeśli weźmiesz coś takiego jak OpenShadingLanguage: https://github.com/imageworks/OpenShadingLanguage

... to nie trzeba stawiać takich pytań, ponieważ natychmiastowe odpowiedzi dają imponujące wyniki.

OSL korzysta ze struktury kompilatora LLVM do tłumaczenia sieci modułów cieniujących w locie (w samą porę lub „JIT”), a proces bardzo mocno optymalizuje moduły cieniujące i sieci z pełną znajomością parametrów modułu cieniującego i innych wartości środowiska wykonawczego, które nie mogły były znane, gdy shadery zostały skompilowane z kodu źródłowego. W rezultacie obserwujemy, jak nasze sieci cieniujące OSL działają o 25% szybciej niż równoważne moduły cieniujące wykonane ręcznie w C! (Tak działali nasi dawni shadery w naszym rendererze).

W takim przypadku nie trzeba kwestionować istnienia generatora kodu. Jeśli pracujesz w tego typu domenach VFX, twoja natychmiastowa reakcja zwykle brzmi: „zamknij się i weź moje pieniądze!” lub „wow, musimy też zrobić coś takiego”.

marstato
źródło
przetłumacz sieci shaderów na kod maszynowy . To brzmi jak kompilator, a nie generator kodu, prawda?
Utku
2
Zasadniczo pobiera sieć węzłową, którą łączy użytkownik i generuje kod pośredni, który jest kompilowany przez JL przez LLVM. Różnica między kompilatorem a generatorem kodu jest niejasna. Czy zastanawiałeś się więcej nad liniami funkcji generowania kodu w językach takich jak szablony w C ++ lub preprocesorze C?
Myślałem o dowolnym generatorze, który wyprowadzałby kod źródłowy.
Utku
Rozumiem, że zakładam, że produkcja nadal jest przeznaczona do spożycia przez ludzi. OpenSL generuje również pośredni kod źródłowy, ale jest to kod niskiego poziomu, który jest bliski montażu dla zużycia LLVM. Zwykle nie jest to kod, który powinien być utrzymywany (zamiast tego programiści utrzymują węzły używane do generowania kodu). Przez większość czasu myślę, że tego typu generatory kodu są bardziej narażone na nadużycia niż na tyle przydatne, aby uzasadnić ich wartość, szczególnie jeśli musisz stale generować kod w ramach procesu kompilacji. Czasami wciąż mają autentyczne miejsce, by zaradzić niedociągnięciom ...
... w języku (-ach) dostępnym, gdy jest używany w określonej domenie. QT ma jeden z tych kontrowersyjnych ze swoim kompilatorem meta-obiektów (MOC). MOC redukuje płytę kotłową, której normalnie potrzebujesz, aby zapewnić właściwości i odbicie oraz sygnały i gniazda i tak dalej w C ++, ale nie w takim stopniu, aby jasno uzasadnić jego istnienie. Często myślę, że QT mógłby być lepszy bez uciążliwego generowania kodu MOC.
8

Nie, generowanie kodu pośredniego nie jest anty-wzorcem. Odpowiedź na drugą część twojego pytania „Dlaczego to robisz?” Jest bardzo szerokim (i osobnym) pytaniem, chociaż i tak podam kilka powodów.

Historyczne konsekwencje braku pośredniego kodu czytelnego dla człowieka

Weźmy jako przykłady C i C ++, ponieważ należą one do najbardziej znanych języków.

Należy zauważyć, że logiczny proces kompilacji kodu C generuje nie kod maszynowy, ale raczej kod zestawu czytelny dla człowieka. Podobnie, stare kompilatory C ++ służą do fizycznej kompilacji kodu C ++ do kodu C. W tym łańcuchu zdarzeń można skompilować kod od czytelnego dla człowieka kodu 1 do czytelnego dla człowieka kodu 2 do czytelnego dla człowieka kodu 3 do kodu maszynowego. "Dlaczego?" Dlaczego nie?

Jeśli pośredni, czytelny dla człowieka kod nigdy nie zostałby wygenerowany, moglibyśmy w ogóle nie mieć C lub C ++. Z pewnością jest to możliwa; ludzie podążają ścieżką najmniejszego oporu wobec swoich celów, a jeśli jakiś inny język zyskałby na popularności z powodu stagnacji rozwoju C, C mógł umrzeć, gdy był jeszcze młody. Oczywiście można by się spierać: „Ale może użylibyśmy innego języka, a może byłoby lepiej”. Może, a może byłoby gorzej. A może wszyscy nadal piszemy na zgromadzeniu.

Dlaczego warto korzystać z pośredniego kodu czytelnego dla człowieka?

  1. Czasami pożądany jest kod pośredni, abyś mógł go zmodyfikować przed następnym krokiem budowania. Przyznaję, że ten punkt jest najsłabszy.
  2. Czasami dzieje się tak, ponieważ oryginalna praca nie została wykonana w żadnym języku czytelnym dla człowieka, ale w narzędziu do modelowania GUI.
  3. Czasami musisz zrobić coś bardzo powtarzalnego, a język nie powinien zaspokoić tego, co robisz, ponieważ jest to tak niszowa lub tak skomplikowana rzecz, że nie ma biznesu, który zwiększa złożoność lub gramatykę języka programowania, aby go dostosować ty.
  4. Czasami musisz zrobić coś bardzo powtarzalnego i nie ma możliwości uzyskania tego, co chcesz, w ogólny sposób; nie może być reprezentowane przez gramatykę języka lub jest w konflikcie z nią.
  5. Jednym z celów komputerów jest zmniejszenie wysiłku ludzkiego, a czasem kod, który prawdopodobnie nie zostanie ponownie dotknięty (niskie prawdopodobieństwo konserwacji), może mieć meta-kod napisany w celu wygenerowania dłuższego kodu w dziesiątej części czasu; czy mogę to zrobić w 1 dzień zamiast 2 tygodni i nie jest prawdopodobne, aby być utrzymywane w ogóle, to ja lepiej wygenerować go - i na off szansa, że ktoś 5 lat od teraz jest zirytowany, bo rzeczywiście nie trzeba utrzymywać go, a następnie mogą spędzić 2 tygodnie na napisaniu go w pełni, jeśli chcą, lub mogą być zirytowani przez 1 tydzień utrzymywania niezręcznego kodu (ale w tym momencie wciąż mamy 1 tydzień do przodu), i to w ogóle , jeśli ta konserwacja musi być w ogóle wykonana .
  6. Jestem pewien, że przeoczyłem więcej powodów.

Przykład

Wcześniej pracowałem nad projektami, w których należy generować kod na podstawie danych lub informacji zawartych w innym dokumencie. Na przykład jeden projekt miał wszystkie swoje wiadomości sieciowe i stałe dane zdefiniowane w arkuszu kalkulacyjnym oraz narzędzie, które przechodzi przez arkusz kalkulacyjny i generuje dużo kodu C ++ i Java, które pozwalają nam pracować z tymi wiadomościami.

Nie twierdzę, że był to najlepszy sposób na skonfigurowanie tego projektu (nie byłem częścią jego uruchomienia), ale to właśnie mieliśmy i były to setki (może nawet tysiące, nie jestem pewien) struktur i obiektów oraz stałych które były generowane; w tym momencie prawdopodobnie jest już za późno, aby spróbować przerobić go na coś w rodzaju Rhapsody. Ale nawet jeśli zostałyby przerobione w coś takiego jak Rhapsody, to i tak mamy kod wygenerowany z Rhapsody .

Ponadto posiadanie wszystkich tych danych w arkuszu kalkulacyjnym było dobre pod jednym względem: pozwoliło nam reprezentować dane w sposób, którego nie moglibyśmy uzyskać, gdyby były to tylko pliki kodu źródłowego.

Przykład 2

Kiedy pracowałem przy budowie kompilatora, użyłem narzędzia Antlr do wykonania leksykalizacji i parsowania. Podałem gramatykę języka, następnie użyłem narzędzia do wyplucia ton kodu w C ++ lub Javie, a następnie użyłem wygenerowanego kodu obok mojego własnego kodu i umieściłem go w kompilacji.

Jak inaczej należy to zrobić? Być może mógłbyś wymyślić inny sposób; prawdopodobnie są inne sposoby. Ale w przypadku tej pracy inne sposoby nie byłyby lepsze niż wygenerowany kod leksykalny / parsujący, który miałem.

Aaron
źródło
Użyłem kodu pośredniego jako swego rodzaju formatu pliku i śladu debugowania, gdy dwa systemy były niekompatybilne, ale miały jakiś stabilny interfejs API, w bardzo ezoterycznym języku skryptowym. Nie miał być czytany ręcznie, ale mógł być w taki sam sposób, jak xml. Jest to jednak częstsze niż myślisz po tym, jak wszystkie strony działają w ten sposób, jak ktoś zauważył.
joojaa
7

To, czego brakuje, to ponowne użycie .

Mamy niesamowite narzędzie do przekształcania kodu źródłowego w binarny, zwane kompilatorem. Jego dane wejściowe są dobrze zdefiniowane (zwykle!), A wiele pracy wymagało dopracowania optymalizacji. Jeśli chcesz używać kompilatora do wykonywania niektórych operacji, chcesz użyć istniejącego kompilatora, a nie pisać własnego.

Wiele osób wymyśla nowe języki programowania i pisze własne kompilatory. Prawie bez wyjątku wszyscy to robią, ponieważ lubią wyzwania, a nie dlatego, że potrzebują funkcji zapewnianych przez ten język. Wszystko, co robią, można zrobić w innym języku; po prostu tworzą nowy język, ponieważ lubią te funkcje. Tym, co ich nie dostanie, jest dobrze dostrojony, szybki, wydajny, optymalizujący kompilator. To da im coś, co może zamienić tekst na binarny, jasne, ale nie będzie tak dobre, jak wszystkie istniejące kompilatory .

Tekst to nie tylko coś, co ludzie czytają i piszą. Komputery są również w domu z tekstem. W rzeczywistości formaty takie jak XML (i inne powiązane formaty) są skuteczne, ponieważ używają zwykłego tekstu. Formaty plików binarnych są często niejasne i źle udokumentowane, a czytelnik nie może łatwo dowiedzieć się, jak działają. XML jest stosunkowo samo-dokumentujący, co ułatwia ludziom pisanie kodu korzystającego z plików w formacie XML. Wszystkie języki programowania są skonfigurowane do odczytu i zapisu plików tekstowych.

Załóżmy, że chcesz dodać nowy obiekt, aby ułatwić Ci życie. Być może jest to narzędzie do układania GUI. Być może jest to interfejs sygnałów i gniazd, który zapewnia Qt . Być może jest to sposób, w jaki TI Code Composer Studio pozwala skonfigurować urządzenie, z którym pracujesz i pobrać odpowiednie biblioteki do kompilacji. Być może zajmuje to słownik danych i automatyczne generowanie typów i definicji zmiennych globalnych (tak, nadal jest to bardzo ważne w oprogramowaniu wbudowanym). Cokolwiek to jest, najskuteczniejszym sposobem na wykorzystanie istniejącego kompilatora jest stworzenie narzędzia, które weźmie twoją konfigurację, czymkolwiek jest i automatycznie wygeneruje kod w wybranym języku.

Jest łatwy do opracowania i przetestowania, ponieważ wiesz, co się dzieje i możesz odczytać wyrzucony przez niego kod źródłowy. Nie musisz tracić lat na budowanie kompilatora, który może konkurować z GCC. Nie musisz uczyć się zupełnie nowego języka ani wymagać od innych osób. Wszystko, co musisz zrobić, to zautomatyzować ten jeden mały obszar, a wszystko inne pozostanie takie samo. Zadanie wykonane.

Graham
źródło
Zaletą bazowania na tekście XML jest to, że w razie potrzeby może on być czytany i zapisywany przez ludzi (zwykle nie zawracają sobie głowy, gdy to działa, ale na pewno robią to podczas programowania). Pod względem wydajności i wydajności przestrzennej formaty binarne są na ogół znacznie lepsze (co jednak bardzo często nie ma znaczenia, ponieważ wąskie gardło występuje gdzie indziej).
leftaroundabout
@leftaroundabout Jeśli potrzebujesz tej wydajności i oszczędności miejsca, na pewno. Powodem, dla którego wiele aplikacji przeszło obecnie na formaty XML, jest to, że wydajność i oszczędność miejsca nie są najważniejszymi kryteriami, jak kiedyś, a historia pokazała, jak źle utrzymywane są binarne formaty plików. (Stare dokumenty MS Word dla klasycznego przykładu!) Pozostaje jednak kwestia - tekst nadaje się tak samo do odczytania przez komputery jak ludzie.
Graham
Jasne, źle zaprojektowany format binarny może w rzeczywistości działać gorzej niż poprawnie przemyślany format tekstowy, a nawet przyzwoity format binarny często nie jest dużo bardziej zwarty niż XML wypełniony jakimś algorytmem kompresji ogólnego przeznaczenia. IMO najlepszym z obu światów jest stosowanie specyfikacji czytelnej dla człowieka poprzez algebraiczne typy danych i automatyczne generowanie wydajnej reprezentacji binarnej z AST tych typów. Zobacz np. Płaska biblioteka .
lewo około
7

Trochę bardziej pragmatyczna odpowiedź, koncentrująca się na tym, dlaczego, a nie na tym, co jest i nie jest kodem źródłowym. Zauważ, że generowanie kodu źródłowego jest częścią procesu kompilacji we wszystkich tych przypadkach - więc wygenerowane pliki nie powinny znaleźć się w kontroli źródła.

Interoperacyjność / prostota

Weźmy na przykład Bufory protokołów Google, doskonały przykład: piszesz pojedynczy opis protokołu wysokiego poziomu, który można następnie wykorzystać do wygenerowania implementacji w wielu językach - często różne części systemu są napisane w różnych językach.

Realizacja / powody techniczne

Weź TypeScript - przeglądarki nie mogą go interpretować, więc proces kompilacji używa transpilatora ( translatora kodu na kod) do generowania JavaScript. W rzeczywistości wiele nowych lub ezoterycznych skompilowanych języków zaczyna się od transpozycji do C, zanim otrzyma odpowiedni kompilator.

Łatwość użycia

W przypadku projektów osadzonych (myśl IoT) napisanych w C i wykorzystujących tylko jeden plik binarny (RTOS lub brak systemu operacyjnego) dość łatwo jest wygenerować tablicę C z danymi do kompilacji, jak w przypadku normalnego kodu źródłowego, zamiast łączenia ich bezpośrednio jako zasoby.

Edytować

Rozszerzanie protokołu protobuf: generowanie kodu pozwala generować obiekty jako klasy pierwszej klasy w dowolnym języku. W języku skompilowanym ogólny parser z konieczności zwróciłby strukturę klucz-wartość - co oznacza, że ​​nie masz dużo kodu generatora, brakuje niektórych kontroli czasu kompilacji (w szczególności kluczy i typów wartości), uzyskujesz gorszą wydajność i bez uzupełniania kodu. Wyobraź sobie wszystkie te void*w C lub tak duże std::variantw C ++ (jeśli masz C ++ 17), niektóre języki mogą w ogóle nie mieć takiej funkcji.

Jan Dorniak
źródło
Z pierwszego powodu myślę, że pomysł OP powinien mieć ogólną implementację w każdym języku (który pobiera opis buforów protokołu, a następnie analizuje / używa formatu on-the-wire). Dlaczego miałoby to być gorsze niż generowanie kodu?
Paŭlo Ebermann
@ PaŭloEbermann oprócz zwykłego argumentu perfromance taka ogólna interpretacja uniemożliwiłaby użycie tych komunikatów jako obiektów najwyższej klasy w skompilowanych (i prawdopodobnie zinterpretowanych) językach - na przykład w C ++ taki interpreter z konieczności zwróciłby strukturę klucz-wartość . Oczywiście możesz następnie umieścić ten kv w swoich klasach, ale może on przekształcić się w wiele kodów typu „plateplate”. Istnieje również uzupełnianie kodu. I sprawdzanie czasu kompilacji - twój kompilator nie sprawdzi, czy twoje literały nie mają literówek.
Jan Dorniak
Zgadzam się ... czy możesz dodać to do odpowiedzi?
Paŭlo Ebermann
@ PaŭloEbermann gotowe
Jan Dorniak
6

Czy generowanie kodu źródłowego jest anty-wzorcem?

Jest to obejście dla niewystarczająco ekspresyjnego języka programowania. Nie ma potrzeby generowania kodu w języku zawierającym odpowiednie wbudowane metaprogramowanie.

Kevin Cline
źródło
3
Jest to również obejście dla konieczności napisania pełnego kompilatora kodu obiektowego w dół do natywnego dla bardziej wyrazistego języka. Wygeneruj C, niech kompilator z dobrym optymalizatorem zajmie się resztą.
Blrfl
Nie zawsze. Czasami masz jedną lub więcej baz danych zawierających pewne definicje np. Sygnałów w magistrali. Następnie chcesz zebrać te informacje razem, być może wykonać pewne kontrole spójności, a następnie napisać kod, który będzie interfejs między sygnałami pochodzącymi z magistrali a zmiennymi, których spodziewasz się w kodzie. Jeśli możesz pokazać mi język, który ma metaprogramowanie, które ułatwia korzystanie z niektórych arkuszy Excela, bazy danych i innych źródeł danych dostarczonych przez klienta i tworzy kod, którego potrzebuję, z pewnymi niezbędnymi sprawdzeniami ważności i spójności danych, to przez wszystko znaczy pokaż mi.
CodeMonkey
@CodeMonkey: przychodzi mi na myśl implementacja ActiveRecord Ruby on Rails. Nie ma potrzeby kopiowania schematu tabeli bazy danych w kodzie. Wystarczy odwzorować klasę na tabelę i napisać logikę biznesową, używając nazw kolumn jako właściwości. Nie wyobrażam sobie żadnego wzorca, który mógłby zostać wygenerowany przez generator kodu, którym nie mógłby zarządzać także meta-programowanie Ruby. Szablony C ++ są również niezwykle wydajne, choć nieco tajemnicze. Makra Lisp to kolejny potężny system metaprogramowania w języku.
kevin cline,
@kevincline miałem na myśli kod, który był oparty na niektórych danych z bazy danych (można z niej zbudować), ale nie na samej bazie danych. Tzn. Mam informacje o tym, które sygnały otrzymuję w Tabeli Excel A. Mam bazę danych B z informacjami o tych sygnałach itp. Teraz chcę mieć klasę, która uzyskuje dostęp do tych sygnałów. Brak połączenia z bazą danych lub arkuszem Excela na komputerze, na którym działa kod. Używanie naprawdę skomplikowanego szablonów C ++ do generowania tego kodu w czasie kompilacji, zamiast prostego generatora kodu. Wybiorę codegen.
CodeMonkey
6

Generowanie kodu źródłowego nie zawsze jest anty-wzorcem. Na przykład piszę obecnie środowisko, które na podstawie podanej specyfikacji generuje kod w dwóch różnych językach (JavaScript i Java). Środowisko wykorzystuje wygenerowany Javascript do rejestrowania działań przeglądarki użytkownika, a kod Java w Selenium faktycznie wykonuje akcję, gdy środowisko jest w trybie odtwarzania. Gdybym nie używał generowania kodu, musiałbym ręcznie upewnić się, że oba są zawsze zsynchronizowane, co jest uciążliwe, a także logiczne.

Jeśli jednak używa się generowania kodu źródłowego do zastępowania funkcji takich jak generyczne, to jest to anty-wzorzec.

Hristo Vrigazov
źródło
Możesz oczywiście napisać kod raz w ECMAScript i uruchomić go w Nashorn lub Rhino na JVM. Możesz też napisać JVM w ECMAScript (lub spróbować skompilować Avian do WebAssembly za pomocą Emscripten) i uruchomić kod Java w przeglądarce. Nie twierdzę, że są to świetne pomysły (cóż, prawdopodobnie są to okropne pomysły :-D), ale przynajmniej są możliwe, jeśli nie wykonalne.
Jörg W Mittag
Teoretycznie jest to możliwe, ale nie jest to ogólne rozwiązanie. Co się stanie, jeśli nie będę mógł uruchomić jednego z języków w innym? Na przykład, pne dodatkowe rzeczy: Właśnie stworzyłem prosty model Netlogo przy użyciu generowania kodu i mam interaktywną dokumentację systemu, która jest zawsze zsynchronizowana z rejestratorem i odtwarzaczem. Ogólnie rzecz biorąc, utworzenie wymagania, a następnie wygenerowanie kodu utrzymuje synchronizację elementów, które działają semantycznie razem.
Hristo Vrigazov
6

Czy coś mi umyka?

Może dobry przykład, w którym kod pośredni okazał się przyczyną sukcesu? Mogę zaoferować Ci HTML.

Uważam, że HTML był prosty i statyczny - ułatwiał tworzenie przeglądarek, umożliwiał wczesne uruchamianie przeglądarek mobilnych itp. Jak pokazały dalsze eksperymenty (aplety Java, Flash) - bardziej złożone i wydajne języki prowadzą do większej liczby problemów . Okazuje się, że użytkownicy są w rzeczywistości zagrożeni przez aplety Java, a odwiedzanie takich stron było równie bezpieczne, jak wypróbowanie pęknięć gier pobranych przez DC ++. Z drugiej strony zwykły HTML jest na tyle nieszkodliwy, że pozwala nam sprawdzić każdą witrynę z uzasadnioną wiarą w bezpieczeństwo naszego urządzenia.

Jednak HTML nie byłby w pobliżu, gdzie jest teraz, gdyby nie był generowany komputerowo. Moja odpowiedź nie pojawiłaby się nawet na tej stronie, dopóki ktoś ręcznie nie przepisał jej z bazy danych do pliku HTML. Na szczęście możesz zrobić użyteczny HTML w prawie każdym języku programowania :)

To znaczy, jeśli istnieje jakiś generator kodu, to dlaczego nie uczynić tego czymś właściwym, które może otrzymać wymagane parametry i wykonać właściwe działanie, które wykonałby kod „wygenerowany”?

Czy możesz sobie wyobrazić lepszy sposób wyświetlania pytania oraz wszystkich odpowiedzi i komentarzy dla użytkownika niż użycie HTML jako generowanego kodu pośredniego?

Džuris
źródło
Tak, mogę sobie wyobrazić lepszy sposób. HTML jest dziedzictwem decyzji Tima Bernersa-Lee o umożliwieniu szybkiego utworzenia przeglądarki internetowej z tekstem. W tamtym czasie było to zupełnie w porządku, ale nie zrobilibyśmy tego samego z korzyścią z perspektywy czasu. CSS sprawił, że wszystkie różne typy elementów prezentacji (DIV, SPAN, TABLE, UL itp.) Stały się niepotrzebne.
kevin cline,
@kevincline Nie twierdzę, że HTML jako taki nie ma wad, zwracałem uwagę, że wprowadzenie języka znaczników (który może być wygenerowany przez program) działało bardzo dobrze w tym przypadku.
Džuris,
Tak więc HTML + CSS jest lepszy niż tylko HTML. Napisałem nawet wewnętrzną dokumentację dla niektórych projektów, nad którymi pracowałem bezpośrednio w HTML + CSS + MathJax. Ale większość stron, które odwiedzam, wydają się być stworzone przez generatory kodu.
David K,
3

po co generować kod źródłowy?

Ponieważ jest to szybsze i łatwiejsze (i mniej podatne na błędy) niż ręczne pisanie kodu, szczególnie w przypadku żmudnych, powtarzalnych zadań. Możesz także użyć narzędzia wysokiego poziomu, aby zweryfikować i zweryfikować swój projekt przed napisaniem pojedynczego wiersza kodu.

Typowe przypadki użycia:

  • Narzędzia do modelowania, takie jak Rose lub Visual Paradigm;
  • High- er języki, takie jak poziom Embedded SQL lub język definicji interfejsu, które muszą być wstępnie przetworzonej w coś compilable;
  • Generatory Lexera i parsera, takie jak flex / bison;

Jeśli chodzi o „dlaczego nie uczynić go funkcją i bezpośrednio przekazywać parametry”, należy zauważyć, że żadne z powyższych nie jest środowiskiem wykonawczym samym w sobie. Nie ma możliwości powiązania kodu z nimi.

John Bode
źródło
2

Czasami twój język programowania po prostu nie ma potrzebnych udogodnień, co uniemożliwia pisanie funkcji lub makr, aby robić to, co chcesz. A może mógłby zrobić to, co chcesz, ale kod napisać byłoby brzydkie. Prosty skrypt Pythona (lub podobny) może następnie wygenerować wymagany kod w ramach procesu kompilacji, który następnie zostanie #includeumieszczony w rzeczywistym pliku źródłowym.

Skąd to wiem? Ponieważ jest to rozwiązanie, do którego wielokrotnie sięgałem podczas pracy z różnymi różnymi systemami, ostatnio SourcePawn. Prosty skrypt Pythona, który analizuje prosty wiersz kodu źródłowego i generuje dwa lub trzy wiersze wygenerowanego kodu, jest znacznie lepszy niż ręczne tworzenie wygenerowanego kodu, gdy skończysz z dwoma tuzinami takich wierszy (tworząc wszystkie moje cvary).

Przykładowy / przykładowy kod źródłowy dostępny, jeśli ludzie tego chcą.

rosuav
źródło
1

Formularz tekstowy jest wymagany do łatwego spożycia przez ludzi. Komputery dość łatwo przetwarzają kod w formie tekstowej. Dlatego generowany kod powinien być generowany w formie, która jest najłatwiejsza do wygenerowania i najłatwiejsza do wykorzystania przez komputery i która jest bardzo często czytelnym tekstem.

A kiedy generujesz kod, sam proces generowania kodu często wymaga debugowania - przez ludzi. Jest to bardzo, bardzo przydatne, jeśli wygenerowany kod jest czytelny dla człowieka, dzięki czemu ludzie mogą wykryć problemy w procesie generowania kodu. W końcu ktoś musi napisać kod, aby wygenerować kod. Nie dzieje się to z powietrza.

gnasher729
źródło
1

Generowanie kodu, tylko raz

Nie każde generowanie kodu źródłowego polega na wygenerowaniu jakiegoś kodu, a następnie nigdy go nie dotykaniu; a następnie generowanie go z oryginalnego źródła, gdy wymaga aktualizacji.

Czasami generujesz kod tylko raz, a następnie odrzucasz oryginalne źródło i przechodząc dalej utrzymuj nowe źródło.

Zdarza się to czasami podczas przenoszenia kodu z jednego języka na inny. W szczególności, jeśli nie oczekuje się późniejszego przeniesienia nowych zmian w oryginale (np. Stary kod języka nie zostanie zachowany lub jest faktycznie kompletny (np. W przypadku niektórych funkcji matematycznych)).

Jednym z powszechnych przypadków jest to, że napisanie generatora kodu, aby to zrobić, może właściwie tylko poprawnie przetłumaczyć 90% kodu. a następnie te ostatnie 10% należy naprawić ręcznie. Co jest znacznie szybsze niż ręczne tłumaczenie 100%.

Takie generatory kodu często bardzo różnią się od generatorów kodu, które f2cprodukują tłumacze w pełnym języku (jak Cython lub ). Ponieważ celem jest jednokrotne utrzymanie kodu. Często są one wykonywane jako 1 off, aby robić dokładnie to, co muszą. Pod wieloma względami jest to wersja następnego poziomu użycia regex / find-replace do kodu portu. „Portowanie wspomagane przez narzędzie” można powiedzieć.

Generowanie kodu, tylko raz, na przykład ze strony internetowej.

Ściśle powiązane jest to, jeśli generujesz kod z jakiegoś źródła, do którego nie chcesz ponownie uzyskiwać dostępu. Np. Jeśli działania potrzebne do wygenerowania kodu nie są powtarzalne, spójne lub ich wykonanie jest kosztowne. Obecnie pracuję nad parą projektów: DataDeps.jl i DataDepsGenerators.jl .

DataDeps.jl pomaga użytkownikom pobierać dane (takie jak standardowe zestawy danych ML). Aby to zrobić, potrzebuje czegoś, co nazywamy RegistrationBlock. Jest to kod określający niektóre metadane, na przykład skąd pobierać pliki, sumę kontrolną oraz komunikat wyjaśniający użytkownikowi wszelkie warunki / kodeksy / status licencji na dane.

Pisanie tych bloków może być denerwujące. Informacje te są często dostępne w (ustrukturyzowanych lub nieustrukturyzowanych) na stronach internetowych, na których przechowywane są dane. Tak więc DataDepsGenerators.jl używa skrobaka do generowania kodu RegistrationBlockCode dla niektórych witryn, które przechowują wiele danych.

Może to nie wygenerować ich poprawnie. Tak więc deweloper korzystający z wygenerowanego kodu może i powinien to sprawdzić i poprawić. Są szanse, że chcą się upewnić, że nie zepsuły na przykład informacji licencyjnych.

Co ważne, użytkownicy / deweloperzy pracujący z DataDeps.jl nie muszą instalować ani używać skryptu do korzystania z wygenerowanego kodu RegistrationBlock. (Nie trzeba pobierać i instalować skrobaka internetowego, który pozwala zaoszczędzić sporo czasu. Szczególnie w przypadku CI)

Jednorazowe wygenerowanie kodu źródłowego nie jest antypatternem. i zwykle nie można go zastąpić metaprogramowaniem.

Lyndon White
źródło
„raport” to angielskie słowo, które oznacza coś innego niż „ponownie port”. Spróbuj „przeportuj ponownie”, aby to zdanie było jaśniejsze. (Komentowanie jest zbyt małe, by sugerować edycję).
Peter Cordes
Dobry haczyk @PeterCordes Przeredagowałem.
Lyndon White,
Szybszy, ale potencjalnie o wiele mniejszy w utrzymaniu, w zależności od tego, jak okropny jest wygenerowany kod. Fortran do C był czymś w przeszłości (kompilatory C były szerzej dostępne, więc ludzie używali f2c+ cc), ale wynikowy kod nie był tak naprawdę dobrym punktem wyjścia dla wersji C programu, AFAIK.
Peter Cordes
1
Potencjalnie, potencjalnie nie. Koncepcja generatorów kodu nie polega na tym, że niektóre generatory kodu tworzą kod, którego nie można utrzymać. W szczególności ręcznie wykonane narzędzie, które nie musi wychwytywać wszystkich przypadków, może często tworzyć całkiem niezły kod. Jeśli na przykład 90% kodu to tylko lista stałych tablic, wówczas generowanie tych konstruktorów tablic jako jednego może być trywialnie wykonane bardzo przyjemnie i bez wysiłku. (Z drugiej strony, kod C napisany przez Cython nie może być utrzymywany przez ludzi. Ponieważ tak nie jest. Tak jak mówiłeś za f2cdnia)
Lyndon White
1
Duży stół był najprostszym, najbardziej zredukowanym argumentem. Podobnie można powiedzieć na przykład konwersję pętli lub warunków. Rzeczywiście sedprzechodzi długą drogę, ale czasami potrzeba nieco bardziej wyrazistej mocy. Granica między logiką programu a danymi jest często dobra. Czasami rozróżnienie nie jest przydatne. JSON to (/ was) kod konstruktora obiektów javascript. W moim przykładzie generuję również kod konstruktora obiektów (czy to dane? Może (może nie, ponieważ czasami ma wywołania funkcji). Czy lepiej jest traktować go jako kod? Tak.)
Lyndon White
1

Generowanie kodu „źródłowego” wskazuje na niedociągnięcie generowanego języka. Czy używanie narzędzi w celu przezwyciężenia tego problemu jest anty-wzorem? Absolutnie nie - wyjaśnię.

Zazwyczaj stosuje się generowanie kodu, ponieważ istnieje definicja wyższego poziomu, która może opisać wynikowy kod znacznie mniej szczegółowy niż język niższego poziomu. Tak więc generowanie kodu ułatwia wydajność i zwięzłość.

Kiedy piszę c ++, robię to, ponieważ pozwala mi to pisać kod wydajniej niż przy użyciu asemblera lub kodu maszynowego. Nadal kod maszynowy jest generowany przez kompilator. Na początku c ++ był po prostu preprocesorem, który generował kod C. Języki ogólnego przeznaczenia doskonale nadają się do generowania zachowań ogólnego przeznaczenia.

W ten sam sposób, używając DSL (języka specyficznego dla domeny), można pisać zwięzłe, ale być może kod ograniczony do konkretnego zadania. To sprawi, że wygenerowanie poprawnego działania kodu będzie mniej skomplikowane. Pamiętaj, że kod oznacza koniec i koniec . Deweloper szuka efektywnego sposobu generowania zachowań.

Idealnie generator może tworzyć szybki kod na podstawie danych wejściowych, które są łatwiejsze w obsłudze i zrozumieniu. Jeśli zostanie to spełnione, nie użycie generatora jest anty-wzorcem . Ten anty-wzór zazwyczaj wynika z faktu, że „czysty” kod jest „czystszy”, podobnie jak pracownik drewna lub inny rzemieślnik może patrzeć na użycie elektronarzędzi lub użycie CNC do „generowania” detali (myśl złota młot ).

Z drugiej strony, jeśli źródło wygenerowanego kodu jest trudniejsze do utrzymania lub wygenerowania kodu, który nie jest wystarczająco wydajny, użytkownik wpada w pułapkę używania niewłaściwych narzędzi (czasami z powodu tego samego złotego młota ).

daramarak
źródło
0

Generowanie kodu źródłowego absolutnie oznacza, że ​​generowany kod to dane. Ale są to dane pierwszej klasy, dane, którymi reszta programu może manipulować.

Dwa najczęstsze typy danych, o których wiem, że są zintegrowane z kodem źródłowym, to graficzne informacje o oknach (liczba i rozmieszczenie różnych kontrolek) oraz ORM. W obu przypadkach integracja poprzez generowanie kodu ułatwia manipulowanie danymi, ponieważ nie trzeba wykonywać dodatkowych „specjalnych” kroków, aby z nich skorzystać.

Podczas pracy z oryginalnymi komputerami Mac (1984) definicje okien i okien zostały utworzone za pomocą edytora zasobów, który utrzymywał dane w formacie binarnym. Korzystanie z tych zasobów w aplikacji było trudniejsze niż byłoby, gdyby „format binarny” to Pascal.

Tak więc nie, generowanie kodu źródłowego nie jest anty-wzorcem, pozwala na włączenie danych do aplikacji, co ułatwia korzystanie z nich.

jmoreno
źródło
0

Generowanie kodu jest anty-wzorcem, gdy kosztuje więcej niż osiąga. Taka sytuacja ma miejsce, gdy generowanie odbywa się z A do B, gdzie A jest prawie tym samym językiem co B, ale z pewnymi drobnymi rozszerzeniami, które można wykonać po prostu kodując w A przy mniejszym wysiłku niż wszystkie niestandardowe narzędzia i tworzenie scen dla A do B .

Kompromis jest bardziej zapobiegający generowaniu kodu w językach, które nie mają funkcji metaprogramowania (makra strukturalne) ze względu na komplikacje i niedociągnięcia związane z uzyskaniem metaprogramowania poprzez etapowanie zewnętrznego przetwarzania tekstu.

Słaby kompromis może mieć również związek z ilością zastosowań. Język A może znacznie różnić się od B, ale cały projekt z niestandardowym generatorem kodu wykorzystuje A tylko w jednym lub dwóch małych miejscach, dzięki czemu całkowita złożoność (małe bity A oraz generator kodu A -> B, plus etapowanie kompilacji otaczającej) przekracza złożoność rozwiązania wykonanego właśnie w B.

Zasadniczo, jeśli zaangażujemy się w generowanie kodu, powinniśmy prawdopodobnie „pójść na całość lub wrócić do domu”: sprawić, by miał znaczną semantykę i często go używać, albo nie zawracaj sobie tym głowy.

Kaz
źródło
Dlaczego usunąłeś akapit „Kiedy Bjarne Stroustrup po raz pierwszy zaimplementował C ++ ...”? Myślę, że to było interesujące.
Utku
@Utku Inne odpowiedzi obejmują to z punktu widzenia kompilacji całego, wyrafinowanego języka, w którym reszta projektu jest w całości napisana. Nie sądzę, aby reprezentowała większość tego, co nazywa się „generowaniem kodu”.
Kaz
0

Nie widziałem tego wyraźnie (zauważyłem, że dotknęło go jedno lub dwie odpowiedzi, ale nie wydawało się to zbyt jasne)

Generowanie kodu (jak powiedziałeś, jakby to były dane) nie stanowi problemu - jest to sposób na ponowne użycie kompilatora w celu dodatkowym.

Edytowanie wygenerowanego kodu jest jednym z najbardziej podstępnych, złych, przerażających anty-wzorów, jakie kiedykolwiek spotkałeś. Nie rób tego.

W najlepszym wypadku edycja wygenerowanego kodu powoduje wciągnięcie do twojego projektu szeregu słabych kodów (CAŁY zestaw kodów jest teraz naprawdę KODEM ŹRÓDŁA - nie ma już danych). W najgorszym wypadku kod ściągnięty do twojego programu jest wysoce redundantny, źle nazwany śmieci, który jest prawie całkowicie nie do utrzymania.

Podejrzewam, że trzecią kategorią jest kod, którego używasz raz (generator GUI?), A następnie edytuj, aby pomóc Ci zacząć / uczyć się. To jest po trochu każdy - może to być dobry sposób na rozpoczęcie, ale twój generator GUI będzie ukierunkowany na użycie kodu „Generowalnego”, który nie będzie świetnym początkiem dla Ciebie jako programisty - Ponadto możesz być kusiło mnie, aby użyć go ponownie dla drugiego GUI, co oznacza ściągnięcie nadmiarowego kodu SOURCE do twojego systemu.

Jeśli Twoje narzędzie jest wystarczająco inteligentne, aby uniemożliwić jakiekolwiek edycje generowanego kodu, idź do niego. Jeśli nie, nazwałbym to jednym z najgorszych anty-wzorów.

Bill K.
źródło
0

Kod i dane to: Informacje.

Dane to informacje dokładnie w takiej formie, w jakiej potrzebujesz (i wartości). Kod jest również informacją, ale w formie pośredniej lub pośredniej. Zasadniczo kod jest również formą danych.

Mówiąc dokładniej, kod jest informacją dla maszyn, która zwalnia ludzi z samodzielnego przetwarzania informacji.

Najważniejszym motywem jest odciążenie ludzi od przetwarzania informacji. Kroki pośrednie są dopuszczalne, o ile ułatwiają życie. Dlatego istnieją pośrednie narzędzia do mapowania informacji. Jak generatory kodów, kompilatory, transpilatory itp.

po co generować kod źródłowy? Dlaczego nie przekształcić go w funkcję, która akceptuje parametry i działa na nie?

Powiedzmy, że ktoś oferuje taką funkcję mapowania, której implementacja jest dla Ciebie niejasna. Jeśli funkcja działa zgodnie z obietnicą, czy obchodzi Cię, czy wewnętrznie generuje kod źródłowy, czy nie?

SD
źródło
0

Jeśli coś można wygenerować, to chodzi o dane, a nie kod.

Ponieważ później określasz, że kodem są dane, twoja propozycja ogranicza się do „Jeśli coś można wygenerować, to nie jest to kod”. Czy powiedziałbyś zatem, że kod asemblera wygenerowany przez kompilator C nie jest kodem? Co się stanie, jeśli zdarzy się, że dokładnie pokrywa się z kodem asemblera, który piszę ręcznie? Możesz tam pójść, jeśli chcesz, ale nie przyjdę z tobą.

Zamiast tego zacznijmy od definicji „kodu”. Nie wymagając zbyt technicznej wiedzy, całkiem dobrą definicją do celów tej dyskusji byłyby „instrukcje uruchamiane maszynowo do wykonywania obliczeń”.

Biorąc to pod uwagę, czy ten cały pomysł generowania kodu źródłowego nie jest nieporozumieniem?

Cóż, twoja początkowa propozycja jest taka, że ​​kodu nie można wygenerować, ale odrzucam tę propozycję. Jeśli zaakceptujesz moją definicję „kodu”, nie powinno być problemu koncepcyjnego z generowaniem kodu w ogóle.

To znaczy, jeśli istnieje jakiś generator kodu, to dlaczego nie uczynić tego czymś właściwym, które może otrzymać wymagane parametry i wykonać właściwe działanie, które wykonałby kod „wygenerowany”?

Cóż, to zupełnie inne pytanie o powód zastosowania generowania kodu, a nie o jego naturze. Proponujesz alternatywę, w której zamiast pisać lub używać generatora kodu, pisze się funkcję, która oblicza wynik bezpośrednio. Ale w jakim języku? Dawno minęły czasy, kiedy ktoś pisał bezpośrednio w kodzie maszynowym, a jeśli piszesz swój kod w innym języku, polegasz na generatorze kodu w postaci kompilatora i / lub asemblera, aby stworzyć program, który faktycznie działa.

Dlaczego więc wolisz pisać w Javie, C, Lisp lub czymkolwiek innym? Nawet asembler? Twierdzę, że przynajmniej częściowo dlatego, że te języki zapewniają abstrakcje danych i operacji, które ułatwiają wyrażenie szczegółów obliczeń, które chcesz wykonać.

To samo dotyczy większości generatorów kodu wyższego poziomu. Prototypowe przypadki to prawdopodobnie generatory skanerów i analizatorów składni, takie jak lexi yacc. Tak, możesz napisać skaner i analizator składni bezpośrednio w C lub w innym wybranym przez siebie języku programowania (nawet w surowym kodzie maszynowym), a czasem tak jest. Jednak w przypadku problemu o znacznej złożoności użycie języka specjalnego, wyższego poziomu, takiego jak lex lub yacc, ułatwia pisanie, czytanie i utrzymywanie odręcznego kodu. Zwykle też znacznie mniejszy.

Powinieneś także rozważyć, co dokładnie rozumiesz przez „generator kodu”. Rozważałbym wstępne przetwarzanie C i tworzenie szablonów C ++ jako ćwiczenia w generowaniu kodu; sprzeciwiasz się tym? Jeśli nie, to myślę, że musisz wykonać gimnastykę umysłową, aby zracjonalizować akceptację tych, ale odrzucając inne smaki generowania kodu.

Jeśli robi się to ze względu na wydajność, brzmi to jak wada kompilatora.

Dlaczego? Zasadniczo zakładasz, że należy mieć uniwersalny program, do którego użytkownik podaje dane, niektóre sklasyfikowane jako „instrukcje”, a inne jako „dane wejściowe”, i które kontynuują obliczenia i emitują więcej danych, które nazywamy „danymi wyjściowymi”. (Z pewnego punktu widzenia taki uniwersalny program można nazwać „systemem operacyjnym”.) Ale dlaczego przypuszczasz, że kompilator powinien być tak samo skuteczny w optymalizacji takiego programu ogólnego, jak w optymalizacji bardziej wyspecjalizowanego programu? program? Oba programy mają różne cechy i różne możliwości.

Jeśli robi się to w celu połączenia dwóch języków, brzmi to jak brak biblioteki interfejsów.

Mówisz, że tak jakby posiadanie uniwersalnej do pewnego stopnia biblioteki interfejsu byłoby z pewnością dobrą rzeczą. Być może tak, ale w wielu przypadkach taka biblioteka byłaby duża i trudna do napisania i utrzymania, a może nawet powolna. A jeśli taka bestia w rzeczywistości nie istnieje, aby obsłużyć konkretny problem, to kim jesteś, aby nalegać, aby została stworzona, gdy podejście do generowania kodu może rozwiązać problem znacznie szybciej i łatwiej?

Czy coś mi umyka?

Myślę, że kilka rzeczy.

Wiem, że kod to także dane. Nie rozumiem tylko, po co generować kod źródłowy? Dlaczego nie przekształcić go w funkcję, która akceptuje parametry i działa na nie?

Generatory kodu przekształcają kod napisany w jednym języku na kod w innym, zwykle niższym poziomie. Pytasz zatem, dlaczego ludzie chcieliby pisać programy w wielu językach, a zwłaszcza dlaczego mogliby mieszać języki subiektywnie różnych poziomów.

Ale już tego dotknąłem. Wybiera się język dla konkretnego zadania, częściowo po to, by był on klarowny i wyrazisty dla tego zadania. Ponieważ mniejszy kod ma średnio mniej błędów i jest łatwiejszy w utrzymaniu, istnieje również tendencja do języków wyższego poziomu, przynajmniej w przypadku pracy na dużą skalę. Ale złożony program obejmuje wiele zadań i często niektóre z nich można skuteczniej rozwiązać w jednym języku, podczas gdy inne można skuteczniej lub bardziej zwięźle rozwiązać w innym języku. Użycie odpowiedniego narzędzia do pracy czasami oznacza zastosowanie generowania kodu.

John Bollinger
źródło
0

Odpowiedź na pytanie w kontekście komentarza:

Obowiązkiem kompilatora jest pobranie kodu zapisanego w formie czytelnej dla człowieka i przekonwertowanie go do postaci do odczytu maszynowego. Dlatego jeśli kompilator nie może utworzyć wydajnego kodu, kompilator nie wykonuje poprawnie swojego zadania. Czy to źle?

Kompilator nigdy nie będzie zoptymalizowany do twojego zadania. Powód tego jest prosty: jest zoptymalizowany do wykonywania wielu zadań. To narzędzie ogólnego zastosowania używane przez wiele osób do wielu różnych zadań. Kiedy już wiesz, jakie jest Twoje zadanie, możesz podejść do kodu w sposób specyficzny dla domeny, dokonując kompromisów, których kompilatory nie mogłyby.

Jako przykład pracowałem nad oprogramowaniem, w którym analityk może potrzebować napisać kod. Mogliby napisać swój algorytm w C ++ i dodać wszystkie sprawdzone granice i sztuczki zapamiętywania, od których zależą, ale to wymaga dużej wiedzy na temat wewnętrznego działania kodu. Wolą napisać coś prostego i pozwolić mi rzucić na to algorytm, aby wygenerować końcowy kod C ++. Następnie mogę wykonywać egzotyczne sztuczki, aby zmaksymalizować wydajność, na przykład analizę statyczną, której nigdy nie spodziewałbym się, że moi analitycy wytrzymają. Generowanie kodu pozwala im pisać w sposób specyficzny dla domeny, co pozwala im wydostać się z produktu łatwiej niż jakiekolwiek narzędzie ogólnego zastosowania.

Zrobiłem też dokładnie odwrotnie. Mam kolejną pracę, którą wykonałem, która miała mandat „bez generowania kodu”. Nadal chcieliśmy ułatwić życie osobom korzystającym z oprogramowania, dlatego wykorzystaliśmy ogromne ilości metaprogramowania szablonów, aby kompilator generował kod w locie. Tak więc potrzebowałem tylko języka C ++ ogólnego przeznaczenia, aby wykonać swoją pracę.

Jest jednak pewien haczyk. To było niezwykle trudne, aby zagwarantować, że błędy były czytelne. Jeśli kiedykolwiek używałeś wcześniej metaprogramowanego kodu szablonu, wiesz, że pojedynczy niewinny błąd może wygenerować błąd, który wymaga 100 wierszy niezrozumiałych nazw klas i argumentów szablonu, aby zrozumieć, co poszło nie tak. Ten efekt był tak wyraźny, że zalecanym procesem debugowania dla błędów składniowych było: „Przewiń dziennik błędów, aż zobaczysz błąd, w którym po raz pierwszy wystąpił błąd. Idź do tego wiersza i po prostu zezuj, aż zdasz sobie sprawę źle zrobiłem ”.

Gdybyśmy użyli generowania kodu, moglibyśmy mieć znacznie potężniejsze możliwości obsługi błędów, z błędami czytelnymi dla człowieka. C'est la vie.

Cort Ammon
źródło
0

Istnieje kilka różnych sposobów korzystania z generowania kodu. Można je podzielić na trzy główne grupy:

  • Generowanie kodu w innym języku jako danych wyjściowych z kroku w procesie kompilacji. W przypadku typowego kompilatora byłby to język niższego poziomu, ale mógłby to być inny język wysokiego poziomu, tak jak w przypadku języków kompilujących się w JavaScript.
  • Generowanie lub przekształcanie kodu w języku kodu źródłowego jako krok w procesie kompilacji. Tak właśnie działają makra .
  • Generowanie kodu za pomocą narzędzia niezależnie od zwykłego procesu kompilacji. Wynikiem tego jest kod, który żyje jako pliki razem ze zwykłym kodem źródłowym i jest kompilowany wraz z nim. Na przykład klasy jednostek dla ORM mogą być generowane automatycznie ze schematu bazy danych lub obiekty przesyłania danych i interfejsy usług mogą być generowane ze specyfikacji interfejsu, takiej jak plik WSDL dla SOAP.

Sądzę, że mówisz o trzecim rodzaju generowanego kodu, ponieważ jest to najbardziej kontrowersyjna forma. W pierwszych dwóch formach wygenerowany kod jest etapem pośrednim, który jest bardzo czysto oddzielony od kodu źródłowego. Ale w trzeciej formie nie ma formalnej separacji między kodem źródłowym a wygenerowanym kodem, z tym wyjątkiem, że wygenerowany kod prawdopodobnie zawiera komentarz, który mówi „nie edytuj tego kodu”. To nadal stwarza ryzyko edytowania wygenerowanego kodu przez programistów, co byłoby naprawdę brzydkie. Z punktu widzenia kompilatora generowany kod to kod źródłowy.

Niemniej jednak takie formy generowanego kodu mogą być naprawdę przydatne w języku o typie statycznym. Na przykład przy integracji z jednostkami ORM bardzo przydatne jest stosowanie silnie typowanych opakowań dla tabel bazy danych. Pewnie, że mógłby obsłużyć integrację dynamicznie w czasie wykonywania, ale można stracić typu bezpieczeństwa i wsparcie narzędzi (zakończenie Code). Główną zaletą statycznego języka typów jest obsługa systemu typów w typie pisania, a nie tylko w czasie wykonywania. (I odwrotnie, ten typ generowania kodu nie jest bardzo rozpowszechniony w językach dynamicznie typowanych, ponieważ w takim języku nie zapewnia żadnych korzyści w porównaniu z konwersjami środowiska wykonawczego.)

To znaczy, jeśli istnieje jakiś generator kodu, to dlaczego nie uczynić tego czymś właściwym, które może otrzymać wymagane parametry i wykonać właściwe działanie, które wykonałby kod „wygenerowany”?

Ponieważ bezpieczeństwo typu i uzupełnianie kodu są funkcjami, które chcesz w czasie kompilacji (i podczas pisania kodu w IDE), ale zwykłe funkcje są wykonywane tylko w czasie wykonywania.

Może być jednak środek: F # obsługuje koncepcję dostawców typów, która jest zasadniczo silnie typowanymi interfejsami generowanymi programowo w czasie kompilacji. Ta koncepcja prawdopodobnie zastąpiłaby wiele zastosowań generowania kodu i zapewniła czystsze rozdzielenie problemów.

JacquesB
źródło
0

Zestawy instrukcji procesora są zasadniczo niezbędne , ale języki programowania mogą być deklaratywne . Uruchomienie programu napisanego w języku deklaratywnym nieuchronnie wymaga pewnego rodzaju generowania kodu. Jak wspomniano w tej odpowiedzi i innych, głównym powodem generowania kodu źródłowego w języku czytelnym dla człowieka jest wykorzystanie wyrafinowanych optymalizacji przeprowadzanych przez kompilatory.

Kevin Krumwiede
źródło
-3

Jeśli coś można wygenerować, to chodzi o dane, a nie kod.

Źle to zrozumiałeś. Powinien przeczytać

Jeśli coś można wprowadzić do generatora interpretowalnych elementów , to chodzi tu o kod, a nie dane.

Jest to format źródłowy dla tego etapu kompilacji, a format ujścia jest nadal kodem.

Bergi
źródło
1
Błędna definicja kodu źródłowego . Kod źródłowy jest przeznaczony głównie dla ludzi, którzy nad nim pracują (i sam ten fakt go definiuje, zobacz także, co to jest wolne oprogramowanie FSF). Generowany za pomocą kodu asemblera gcc -fverbose-asm -O -Snie jest kodem źródłowym (i to nie tylko lub głównie dane), nawet jeśli jest to jakaś forma tekstowa zawsze podawana do GNU asi czasami czytana przez ludzi.
Basile Starynkevitch
Ponadto wiele implementacji języków kompiluje się do kodu C , ale wygenerowane C nie jest oryginalnym kodem źródłowym (np. Ludzie nie mogą z łatwością nad nim pracować).
Basile Starynkevitch
W końcu twój sprzęt (np. Układ AMD, Intel lub płyta główna komputera) interpretuje kod maszynowy (który oczywiście nie jest kodem źródłowym). BTW IBM1620 miał klawiaturę oznaczalnych izolatów (BCD) kod maszynowy, ale fakt ten nie sprawiają, że „kod źródłowy”. Cały kod nie jest źródłowy.
Basile Starynkevitch
@BasileStarynkevitch Ah, masz mnie tam. Nie powinienem zbyt mocno kompresować mojego dowcipnego stwierdzenia, bo inaczej zmienią ich znaczenie. Tak, kod źródłowy powinien być najbardziej oryginalnym kodem, który jest wprowadzany do pierwszego etapu kompilacji.
Bergi
Żaden kod źródłowy nie jest kodem dla ludzi. Jest tak trudne i subiektywne zdefiniowanie jak muzyka (vs. dźwięk). Nie chodzi o to, aby znaleźć oprogramowanie, które go używa.
Basile Starynkevitch