Metaprogramování

Technology
12 hours ago
8
4
2
Avatar
Author
Albert Flores

Metaprogramování je programovací technika, založená na vytváření programů, které nakládají s jinými počítačovými programy jako se svými daty. Mohou je tedy modifikovat nebo vytvářet, případně i modifikovat samy sebe. To mimo jiné může umožnit již při kompilaci provádět činnosti, které by jinak musely proběhnout až za běhu programu.

V mnoha případech tato technika umožňuje programátorovi napsat program úsporněji (zmenšit počet jeho řádků, zjednodušit jeho vnitřní strukturu). Může také pomoci s odstraněním duplikovaného kódu v rámci programu. +more Jazyk, ve kterém je metaprogram napsán, se nazývá metajazykem. Jazyk programu, se kterým metaprogram manipuluje, se nazývá předmětným jazykem. Schopnost programovacího jazyka být svým vlastním metajazykem se často nazývá reflexí. Reflexe je schopnost programovacího jazyka, která programu v něm napsaném umožňuje zjistit informace o sobě samém.

Způsoby metaprogramování

Prvním způsob je odhalení vnitřní stavby programu za běhu pomocí API (aplikačního programovacího rozhraní). Program psaný v jazyce, který umožňuje objektově orientované programování, tak za běhu může například zjistit, jaké třídy je daný objekt, jaké metody tato třída definuje, jaké mají tyto metody parametry atd. +more Může také s těmito strukturami manipulovat - například za běhu programu vytvářet nové třídy, měnit implementace již definovaných metod, apod. Vždy záleží na konkrétním programovacím jazyku, jeho vlastnostech a dynamičnosti.

Druhým způsobem je dynamický překlad a spuštění zdrojového kódu z výrazu, který je dynamicky generován za běhu programu. Často se jedná o řetězce, které jsou za běhu programem dynamicky sestaveny a interpretovány jako zdrojový kód. +more Programátor může kromě podoby interpretovaného řetězce také ovlivnit kontext, ve kterém je výsledný zdrojový kód interpretován. Díky tomu tak doslova "programy mohou vytvářet programy".

Třetím způsobem je úplně vystoupit z předmětného jazyka a použít pouze metajazyk. Univerzální systémy pro transformaci programů, které jako svůj vstup přijímají popisy jazyků a poté mohou nad tímto jazykem provádět libovolné transformace, jsou přímou implementací metaprogramování. +more Tento přístup umožňuje použít techniky metaprogramování i ve spojení s jazyky, které nemají žádnou vlastní podporu pro metaprogramování.

Statické jazyky

Statické jazyky většinou neumožňují přímou interpretaci zdrojového kódu za běhu programu. Zdrojový kód musí být nejdříve zkompilován do strojového kódu, nebo bytekódu, než může být počítačem interpretován. +more Je však možné metaprogramování využít před tím, než je program zkompilován. Před finální kompilací programu rozhodneme, které jeho části využijeme (například pomocí podmínek v makrech) a budeme kompilovat a které části během kompilace "zahodíme". Příkladem takové techniky mohou být makra v C nebo šablony v C++.

Dynamické jazyky

Některé dynamické jazyky dovolují programátorovi manipulovat s jejich strukturou za běhu. Příkladem takových jazyků může být například Smalltalk, Lisp, Javascript, Ruby.

Příklady

Generativní programování

Následující skript v bashi je příkladem generativního programování:

#!/usr/bin/env bash # metaprogram echo '#!/usr/bin/env bash' >program for ((I=1; I>program done chmod +x program

Tento program (skript) vygeneruje nový program, který vytiskne na standardní výstup čísla 1 až 100. Slouží pouze pro ilustraci toho, jak jeden program může vytvořit jiný program. +more Výsledný program samozřejmě není nejefektivnější způsob, jak vypsat seznam čísel od 1 do 100.

Dynamické vytvoření třídy za běhu

classes = ["Foo", "Bar", "Baz"] classes. each_with_index do |class_name, i| klass = Class. +morenew do define_method :pozdrav do puts "Já jsem #{self. class}, #{i+1}. dynamicky definovaná třída" end end eval("#{class_name} = klass") end.

Baz.new.pozdrav # => Já jsem Baz, 3. dynamicky definovaná třída

Vysvětlení: Tento skript uloží do pole řetězce Foo, Bar a Baz. Poté toto pole projde a dynamicky definuje 3 třídy. +more Každá z těchto tříd má definovanou svou vlastní instanční metodu pozdrav, která vypíše jméno třídy, jejíž je instancí, a svoje pořadové číslo, které je v definici funkce přístupné díky uzávěře, která vzniká díky využití bloku pro definování metody.

Poté je vytvořen řetězec, který je ihned interpretován pomocí metody eval. Tento řetězec obsahuje jednoduché vytvoření nové konstanty, do které je přiřazena nově vytvořená třída.

V tomto příkladě se tedy jedná o kombinaci prvního a druhého výše zmíněného způsobu metaprogramování.

Dynamické přepsání metody

Příklad dynamického přidání funkcionality již definované metodě v programovacím jazyce Ruby:

class Foo def print_bar puts "bar" end end

foo = Foo.new foo.print_bar # => bar

Foo.class_eval do alias :print_bar_without_baz :print_bar

def print_bar_with_baz print "baz " print_bar_without_baz end

alias :print_bar :print_bar_with_baz end

foo.print_bar # => baz bar

Vysvětlení: Nejdříve definuje třídu Foo. V této třídě definujeme jednoduchou metodu, která vytiskne na STDOUT (standardní výstup) text "bar". +more Vytvoříme novou instanci třídy Foo a zavoláme na ní metodu print_bar. Na výstupu bude po spuštění programu "bar".

Poté v kontextu třídy Foo (Foo. class_eval) spustíme blok kódu, ve kterém můžeme s již předtím definovanou třídou manipulovat. +more Vytvoříme alias (alternativní jméno) print_bar_without_baz metody print_bar. Můžeme tak stejnou metodu volat pod dvěma jmény print_bar i print_bar_without_baz. Vždy se stejným výsledkem.

Dále definujeme novou metodu print_with_baz. Tato metoda nejdříve vypíše baz a následně volá již dříve definovanou metodu print_bar pod jejím novým alternativním jménem print_without_baz.

Na dalším řádku vytvoříme alias print_bar nově definované metody print_bar_with_baz. Tento nový alias přepíše původně definovanou metodu "print_bar".

Znovu zavoláme metodu print_bar na instanci třídy Foo (uložené v proměnné foo). Výsledkem je tentokrát text "baz bar".

Podařilo se nám tedy "předefinovat" již dříve vytvořenou metodu, kterou jsme uložili pod jiným jménem. Pokud po tomto předefinování metody voláme na instancích pozměněné třídy tuto metodu, dostaneme jiný výsledek. +more Tento přístup programátorům umožňuje jednoduše přidávat funkcionalitu do knihoven třetích stran, bez toho, aby museli upravovat zdrojový kód těchto knihoven. Pokud programátorovi nevyhovuje konkrétní chování některé metody z knihovny, může ji předefinovat a využívat svoji pozměněnou implementaci, avšak se zachováním možnosti volání původní metody knihovny, která je poté dostupná pod jiným jménem.

5 min read
Share this post:
Like it 8

Leave a Comment

Please, enter your name.
Please, provide a valid email address.
Please, enter your comment.
Enjoy this post? Join Cesko.wiki
Don’t forget to share it
Top