MetaprogramowanieMetaprogramowanie – technika umożliwiająca programom tworzenie lub modyfikację kodu innych programów (lub ich samych). Program będący w stanie modyfikować lub generować kod innego programu nazywa się metaprogramem. Wykorzystanie zasad metaprogramowania pozwala na przykład na dynamiczną modyfikację programu podczas jego kompilacji. Metaprogramy tworzy się w metajęzykach. Jeśli język jest jednocześnie swoim metajęzykiem, taką cechę nazywamy refleksyjnością (ang. reflexivity). Metaprogramowanie może polegać nie tylko na generowaniu kodu, ale również na modyfikacjach w czasie wykonania programu. Takie możliwości dają języki Javascript, C#, Lisp, Perl, PHP, Prolog, Python, Ruby, Groovy, Smalltalk, R oraz Tcl. PrzykładyPrzykładem prostego metaprogramu jest ten skrypt w bashu: #!/bin/bash
# metaprogram
echo '#!/bin/bash' >program
for ((I=1; I<=992; I++)) do
echo "echo $I" >>program
done
chmod +x program
Program ten generuje 993 linie, wypisujące liczby od 1 do 992. Nie jest to zbyt efektywny sposób na wypisanie liczb 1-992, ale ilustruje, jak w kilka minut można stworzyć program o długości 1000 linii. Dość analogiczny program w języku python wersji 3.0: from os import system
clay = open( "adam.py", "w" )
clay.write( "print( \"Madam, i'm Adam.\" )" )
clay.close()
system("python3.0 adam.py")
Jeśli umieścimy powyższy kod w pliku i uruchomimy go, to program wygeneruje plik/program adam.py i uruchomi go, a ten wypisze "Madam, i'm Adam.". Python posiada kilka funkcji ułatwiających metaprogramowanie np. Poniżej analogiczny kod w scheme: (define adam "adam.scm")
(define clay
(open-output-file adam))
(display "(display " clay)
(write "Madam, i'm Adam." clay)
(display ")" clay)
(close-output-port clay)
(load adam)
Makra lispoweDialekty języka Lisp, takie jak Scheme, Clojure czy Common Lisp, jako języki symboliczne, obsługują tzw. makra składniowe. Kod źródłowy zorganizowany jest w nich w postaci tzw. S-wyrażeń i może być po wczytaniu do pamięci zmieniany tak samo, jak inne dane, z użyciem funkcji operujących na jednokierunkowych listach, którymi reprezentowane są złożone S-wyrażenia. Przykład makra when w Scheme: (define-macro (when cond . body)
`(if ,cond
(begin
,@body)))
Przykład makra when w Clojure: (defmacro when
[test & body]
(list 'if test (cons 'do body)))
Powyższe makro działa podobnie do konstrukcji sterującej if, ale ma tylko jedną „odnogę” i można do niej wstawiać wiele wyrażeń. Zapisanie tego kodu w postaci funkcji wywołałoby zarówno warunek, jak i ciało. Warto zauważyć, że lispowe makra są specyficznymi funkcjami, jednak różnią się od zwykłych funkcji następującymi cechami:
Introspekcja i RefleksjaSą to cechy meta programowania umożliwiające sprawdzanie jak wyglądają obiekty w pamięci np. sprawdzenie listy pól i metod w obiekcie czy pobranie i wywołanie metody, na podstawie wygenerowanego ciągu znaków. Magiczne metodyJest to mechanizm występujący m.in. w JavaScript, Python czy PHP umożliwiający zastąpienie wbudowanego mechanizmu własną implementacją np. w php są funkcje takie jak __call czy __get, które wywołają się, gdy próbujemy wywołać metodę lub pobrać właściwość, która nie istnieje. Podobny mechanizm występuje w języku Python. W JavaScript mechanizm ten zaimplementowany jest za pomocą zdefiniowanych symboli oraz obiektów Proxy, które weszły do języka wraz z wersją ES6. Zobacz też |