Różnice między wybraną wersją a wersją aktualną.
Both sides previous revision Previous revision Next revision | Previous revision | ||
c_i_lua [2010/12/28 18:42] sjablon1 |
c_i_lua [2010/12/29 12:22] (aktualna) sjablon1 |
||
---|---|---|---|
Linia 23: | Linia 23: | ||
Strona ma na celu ukazanie sposobów i możliwości integracji języków C++ oraz Lua, jednak przedstawię krótko podstawy języka Lua wraz z przykładami. W celu dogłębnego poznania składni i możliwość języka polecam dokumentacje umieszczoną na stronie internetowej. | Strona ma na celu ukazanie sposobów i możliwości integracji języków C++ oraz Lua, jednak przedstawię krótko podstawy języka Lua wraz z przykładami. W celu dogłębnego poznania składni i możliwość języka polecam dokumentacje umieszczoną na stronie internetowej. | ||
+ | |||
+ | Na początek standardowy Hello World (Listing 1) | ||
+ | |||
+ | **Listing 1** - program Hello World w Lua | ||
+ | |||
+ | <code cpp> | ||
+ | |||
+ | -- HelloWorld.lua | ||
+ | print("Hello from Lua!"); | ||
+ | --[[ | ||
+ | Przykład komentarza | ||
+ | umieszczonego w kilku liniach | ||
+ | ]] | ||
+ | |||
+ | </code> | ||
+ | |||
=== Konwencje leksykalne === | === Konwencje leksykalne === | ||
- | // wypunktować! | ||
* W języku Lua identyfikatory mogą się składać z dowolnego łańcucha znaków alfanumerycznych lub znaku _. | * W języku Lua identyfikatory mogą się składać z dowolnego łańcucha znaków alfanumerycznych lub znaku _. | ||
- | * Ograniczeniami są: zakaz rozpoczynania identyfikatora od cyfry oraz słowa zastrzeżone jak //and//, //or//, //break//, //else// itp. | + | * Ograniczeniami są: zakaz rozpoczynania identyfikatora od cyfry oraz słowa zastrzeżone jak //and//, //or//, //break//, //else// itp. |
- | | + | * Lua rozróżnia wielkość liter, więc For będzie oznaczał identyfikator zaś for będzie dotyczył pętli. |
+ | * Komentarze rozpoczyna się podwójnym myślnikiem i działa analogicznie jak w C++, ignorowane są wszystkie znaki, aż do końca linii. | ||
+ | * Istnieje również możliwość komentowania całego bloku kodu na kształt znanych z języka C, które w Lua zastąpione jest ciągiem --[[". | ||
- | Lua rozróżnia wielkość liter, więc For będzie oznaczał identyfikator zaś for będzie dotyczył pętli. Komentarze rozpoczyna się podwójnym myślnikiem -- i działa analogicznie jak // w C++, ignorowane są wszystkie znaki, aż do końca linii. Istnieje również możliwość komentowania całego bloku kodu na kształt znanych z języka C /* .. */, które w Lua zaczynamy --[[" i kończymy "]]. | ||
=== Typy danych i wyrażenia === | === Typy danych i wyrażenia === | ||
- | //wypunktować | ||
W Lua podobnie jak w innych językach skryptowych nie deklarujemy typów zmiennych. Zamiast tego każda zmienna ma dynamiczny typ określany przez jej wartość. | W Lua podobnie jak w innych językach skryptowych nie deklarujemy typów zmiennych. Zamiast tego każda zmienna ma dynamiczny typ określany przez jej wartość. | ||
Linia 41: | Linia 56: | ||
Istnieje 8 podstawowych typów danych: | Istnieje 8 podstawowych typów danych: | ||
+ | * //nil// - przyjmuje jedną wartość nil, która określa brak konkretnej wartości (często nil jest stosowany do określenia zmienną niezainicjalizowaną); | ||
+ | * //boolean// - standardowe wartości boolowskie true/false; | ||
+ | * //number// - liczba rzeczywista, zmiennoprzecinkowa; | ||
+ | * //string// - standardowy łańcuch znaków; | ||
+ | * //userdata// - typ danych pozwalajacy na przetrzymywanie dowolnych danych z języka C(np. wskaźników); | ||
+ | * //function// - ciąg wykonywalnych instrukcji, funkcje mogą być deklarowane wewnątrz innych funkcji lub być zwracane z innej funkcji | ||
+ | *// thread// - wątek w Lua; | ||
+ | * //table// - tablica asocjacyjna(więcej informacji w dalszej części strony); | ||
- | nil – przyjmuje jedną wartość nil, która | + | Ciekawym mechanizmem języka jest poleceniem //type// pozwalające na sprawdzenie typu zmiennej. Przykład pokazano na Listingu 2. |
- | określa brak konkretnej wartości (często | + | |
- | nil jest stosowany do określenia | + | |
- | zmienną niezainicjalizowaną); | + | |
- | • boolean – przyjmuje standardowe | + | |
- | wartości boolowskie, czyli true lub | + | |
- | false; | + | |
- | • number – liczba reprezentująca wartości | + | |
- | rzeczywiste (zmiennoprzecinkowe); | + | |
- | liczby możemy zapisywać na różne | + | |
- | sposoby, np.: 18, 3.141592, 1.23e12, | + | |
- | 4.18e-3; | + | |
- | • string – standardowy łańcuch znaków, | + | |
- | np. "What is your name?"; | + | |
- | • userdata – typ danych umożliwiający | + | |
- | przetrzymywanie dowolnych danych z | + | |
- | języka C (np. wskaźników) w strukturach | + | |
- | Lua; | + | |
- | • function – funkcje zdefniniowane jako | + | |
- | wykonywalny ciąg instrukcji; funkcje | + | |
- | mogą być deklarowane wewnątrz | + | |
- | innych funcji, mogą być argumentami | + | |
- | innych funkcji, lub być zwracane z innej | + | |
- | funkcji; | + | |
- | • thread – wątek w Lua – z uwagi na charakter | + | |
- | niniejszego artykułu nie będziemy | + | |
- | omawiać wątków w Lua; | + | |
- | • table – tablica asosjacyjna będąca najcięższym | + | |
- | orężem Lua; dokładny opis tablic | + | |
- | znajduje się w dalszej części. | + | |
- | W dowolnym miejscu skryptu możemy | + | |
- | sprawdzić typ danej zmiennej poleceniem | + | |
- | type (jak pokazano w Listingu 2). | + | |
- | Lua, jak przystało na porządny język programowania, | + | |
- | oferuje również różnego rodzaju | + | |
- | operatory do definiowania wyrażeń. Wyróżniamy: | + | |
- | • operatory arytmetyczne, czyli + (dodawanie), | + | |
- | - (odejmowanie), * (mnożenie), | + | |
- | / (dzielenie), % (modulo) i ^ (potęgowanie); | + | |
- | • operatory porównania, czyli == (równe), | + | |
- | ~= (różne), < (mniejsze), > (większe), | + | |
- | <= (mniejsze lub równe), >= (większe lub | + | |
- | równe); | + | |
- | • operatory logiczne, takie jak and (część | + | |
- | wspólna), or (suma logiczna), not (negacja). | + | |
- | Instrukcje i funkcje | + | **Listing 2** - przykład polecenia type w Lua. |
- | Lua obsługuje standardowy zestaw instrukcji | + | |
- | podobnych do tych stosowanych w języku | + | <code cpp> |
- | Pascal czy C. Istnieje możliwość definiowania | + | |
- | bloków instrukcji objętych słowami kluczowymi | + | print(type(a)) --> nil (zmienna 'a' nie została zainicjalizowana) |
- | do i end, które można kontrolować | + | a = 10 |
- | poprzez break czy return. Na listę pojedynczych | + | print(type(a)) --> number |
- | instrukcji składają się: | + | a = print |
- | • instrukcje przypisania =; | + | a(type(a)) --> function |
- | • instrukcje warunkowe, if/elseif/else/ | + | |
+ | </code> | ||
+ | |||
+ | |||
+ | W Lua dostępne są następujące operatory: | ||
+ | |||
+ | * Operatory arytmetyczne: +, -, *, /; | ||
+ | * Operatory porównania: ==, ~=, <, >, <=, >=- | ||
+ | * Operatory logiczne: //and//, //or//, //not// | ||
+ | |||
+ | === Instrukcje i funkcje === | ||
+ | |||
+ | Lua posiada standardowe instrukcje znane z języków Pascal/C: | ||
+ | |||
+ | * instrukcja przypisania =; | ||
+ | * instrukcje warunkowe, if/elseif/else/end; | ||
+ | * pętle, for/do/end, while,do,end, repeat/until; | ||
+ | * wywołanie funkcji; | ||
+ | * lokalna deklaracja zmiennych; | ||
+ | |||
+ | |||
+ | **Listing 3** - przykład stosowania funkcji i warunków logicznych, funkcja rekurencyjna obliczająca silnie. | ||
+ | |||
+ | <code cpp> | ||
+ | |||
+ | -- obliczenie n! | ||
+ | function factorial (n) | ||
+ | if n == 1 then | ||
+ | return 1; | ||
+ | else | ||
+ | return n * factorial(n-1); | ||
+ | end | ||
+ | end | ||
+ | -- obliczenie 5! | ||
+ | print(factorial(5)); --> 120 | ||
+ | |||
+ | </code> | ||
+ | |||
+ | === Tablice === | ||
+ | |||
+ | Najistotniejszymi strukturami danych w Lua są tablice asocjacyjne reprezentowane przez kolekcję par (klucz, wartość). Kluczem może być wszystko poza | ||
+ | wartością //nil//. Elementami tablicy mogą być wszystkie wartości podstawowych typów Lua. Oznacza to, że również mogą to być omawiane w tym miejscu Tablice. Pozwala to na tworzenie takich struktur jak lista, wektor, kolejka czy stos. | ||
+ | |||
+ | Listing 4 pokazuje przykłady wykorzystania tablic w Lua realizując proste zbiory danych, listę jednokierunkową, czy strukturę zawierającą zarówno atrybuty jak i metody. | ||
+ | |||
+ | |||
+ | **Listing 4** - przykład wykorzystania tablic do realizacji zbiorów danych. | ||
+ | |||
+ | <code cpp> | ||
+ | |||
+ | |||
+ | -- prosty wektor liczb | ||
+ | squares = {1, 4, 9, 16, 25, 36, 49, 64, 81} | ||
+ | -- zbiór danych | ||
+ | creature = { name = "Ghoul", health = 100 } | ||
+ | print(creature["name"]); --> Ghoul | ||
+ | print(creature.name); --> Ghoul | ||
+ | -- lista | ||
+ | list = nil; | ||
+ | list = {next = list, value = 2}; | ||
+ | list = {next = list, value = "test"}; | ||
+ | list = {next = list, value = math.pi}; | ||
+ | ptr = list; | ||
+ | while ptr do | ||
+ | print(ptr.value); | ||
+ | ptr = ptr.next; | ||
+ | end; --> 3.141592 test 2 | ||
+ | -- struktura/klasa | ||
+ | Vec3 = {}; | ||
+ | Vec3.new = function(x, y, z) | ||
+ | return {x=x, y=y, z=z, length=function() return math.sqrt(x*x+y*y+z*z) end;} | ||
end; | end; | ||
- | • pętle, for/do/end, while/do/end, repeat/ | + | v = Vec3.new(4, 2, 4); |
- | until/; | + | print(v:length()); --> 6 |
- | • wywołania funkcji; | + | |
- | • lokalne deklaracje zmiennych. | + | |
- | Instrukcja przypisania może dotyczyć kilku | + | |
- | zmiennych. Pojedyncze instrukcje mogą | + | |
- | opcjonalnie kończyć się średnikiem. | + | |
- | Listing 3 ilustruje wykorzystanie funkcji, | + | |
- | zastosowanie pętli oraz instrukcji warunkowych | + | |
- | na przykładzie funkcji wyznaczającej | + | |
- | wartość minimalną i maksymalną z trzech | + | |
- | liczb, oraz funkcji obliczającej pierwiastki | + | |
- | równania kwadratowego. | + | |
- | Jak widzimy w przykładzie, funkcje mogą | + | |
- | przyjmować i zwracać więcej niż jeden | + | |
- | parametr. Istnieje również możliwość deklarowania | + | |
- | funkcji rekurencyjnych (Listing | + | |
- | 4). | + | |
- | Tablice | + | </code> |
- | Tablice są najistotniejszymi strukturami | + | |
- | danych w Lua. Są one reprezentowane przez kolekcję par (klucz, wartość), gdzie | + | |
- | kluczem może być wszystko poza wartością | + | |
- | nil. Elementami tablicy mogą być | + | |
- | wszystkie wartości podstawowych typów | + | |
- | Lua, czyli także same tablice. Dzięki temu | + | |
- | mechanizm tablicy umożliwia definiowanie | + | === Biblioteka standardowa Lua === |
- | użytkownikowi dowolnych struktur | + | |
- | danych, które mogą być utożsamiane z elementami | + | Lua jak każdy porządny język, wyposażona została w bogatą standardową bibliotekę zawierającą bogaty zestaw narzędzi wyższego poziomu. Należą do niej następujące moduły: |
- | takimi jak: lista, wektor, kolejka, | + | |
- | zestaw czy stos. | + | * funkcje wejścia/wyjścia; |
- | W tradycyjnych językach programowania | + | * funkcje systemowe; |
- | jakimi są C czy Pascal w większości przypadków | + | * operacje na łańcuchach znaków; |
- | złożone struktury danych reprezentowane | + | * operacje na tablicach; |
- | są właśnie przez dynamiczne listy lub | + | * obsługa wątków; |
- | wektory prostych typów danych. Tablice w | + | * funkcje matematyczne; |
- | Lua umożliwiają implementację o wiele bardziej | + | |
- | złożonych i elastycznych struktur, stworzonych | + | Poniższy kod pokazuje kilka przykładów wykorzystania biblioteki standardowej. |
- | specjalnie na potrzeby rozwiązania | + | |
- | danego zagadnienia. Listing 5 pokazuje przykłady | + | **Listing 5** Wykorzystanie biblioteki standardowej Lua |
- | wykorzystania tablic w Lua realizując | + | |
- | proste zbiory danych, listę jednokierunkową, | + | <code cpp> |
- | czy strukturę zawierającą zarówno atrybuty | + | |
- | jak i metody. | + | -- losowanie liczb |
+ | math.randomseed(os.time()) | ||
+ | print(math.random(10)); -- wyświetlenie liczby wylosowanej z przedziału [1,10] | ||
+ | |||
+ | -- obliczenie wartości cos(pi/5) | ||
+ | print(math.cos(math.pi*0.2)); | ||
+ | |||
+ | |||
+ | -- zapis do pliku | ||
+ | f = assert(io.open("output.txt", "w")) | ||
+ | f:write("test"); | ||
+ | f:close(); | ||
+ | |||
+ | </code> | ||
==== Integracja z C++ ==== | ==== Integracja z C++ ==== | ||
+ | |||
+ | Zapoznaliśmy się już z podstawami języka Lua, część przejść do najważniejszej sprawy czyli integracji z językiem C++. | ||
=== Przygotowanie do pracy z Lua === | === Przygotowanie do pracy z Lua === | ||
- | === Wywoływanie funkcji Lua w C++ === | + | W celu rozpoczęcia pracy z Lua musimy wykonać dwie następujące operacje: |
+ | |||
+ | - Dołączenie do projektu skompilowaną bibliotekę Lua oraz dołączyć w kodzie nagłówki Lua (lua.h, lualib.h i lauxlib.h) | ||
+ | - Należy zapamiętać, że skrypty Lua wykonywane są na maszynie wirtualnej, musimy więc ją zainicjować. | ||
+ | |||
+ | Prosty przykład ukazujący wspomniane operacje i wywołujący z poziomu kodu C++ skrypt Lua pokazany jest na Listingu 6. | ||
+ | |||
+ | **Listing 6** Inicjalizacja maszyny wirtualnej Lua i wywołanie skryptu Lua z poziomu kodu C++ | ||
+ | |||
+ | <code cpp> | ||
+ | |||
+ | #include <iostream> | ||
+ | |||
+ | // dołączenie nagłówków Lua | ||
+ | extern "C" | ||
+ | { | ||
+ | #include "lua.h" | ||
+ | #include "lualib.h" | ||
+ | #include "lauxlib.h" | ||
+ | } | ||
+ | |||
+ | |||
+ | int main() | ||
+ | { | ||
+ | // utworzenie maszyny wirtualnej Lua. | ||
+ | lua_State* L = lua_open(); | ||
+ | |||
+ | // inicjalizacja standardowych bibliotek | ||
+ | luaL_openlibs(L); | ||
+ | |||
+ | // wczytanie skryptu Lua | ||
+ | if (luaL_dofile(L, "zpr.lua")) | ||
+ | { | ||
+ | // Walidacja wcztania skryptu | ||
+ | std::cout << "Error while running script: " << lua_tostring(L, -1) << std::endl; | ||
+ | return 1; | ||
+ | } | ||
+ | |||
+ | // zamknięcie maszyny wirtualnej | ||
+ | lua_close(L); | ||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | </code> | ||
+ | |||
+ | |||
+ | === Wywoływanie funkcji Lua w kodzie C++ === | ||
+ | |||
+ | Potrafimy już z poziomu kodu C++ zainicjalizować maszynę wirtualną Lua i uruchomić skrypt napisany w Lua. Przejdźmy krok dalej - spróbujmy wywołać funkcję zdefiniowaną w skrypcie Lua z poziomu kodu C++. Zacznijmy od przykładowej funkcji obliczającą różnicę dwóch liczb. | ||
+ | |||
+ | **Listing 7** Funkcja Lua obliczającą różnicę dwóch liczb. | ||
+ | |||
+ | <code cpp> | ||
+ | |||
+ | --sub.lua | ||
+ | |||
+ | function(x,y) | ||
+ | return x - y | ||
+ | end | ||
+ | |||
+ | |||
+ | </code> | ||
+ | |||
+ | Aby umożliwić wywoływanie funkcji ze skryptu Lua musimy zdefiniować kodzie C++ funkcję, w której odłożymy na stos parametry funkcji, a następnie | ||
+ | zdejmiemy ze stosu wartość zwracaną przez funkcję. Ilustruje to poniższy przykład: | ||
+ | |||
+ | **Listing 8** Przykład wywołania funkcji Lua w kodzie C++. | ||
+ | |||
+ | <code cpp> | ||
+ | |||
+ | #include <iostream> | ||
+ | |||
+ | // dołączenie nagłówków Lua | ||
+ | extern "C" | ||
+ | { | ||
+ | #include "lua.h" | ||
+ | #include "lualib.h" | ||
+ | #include "lauxlib.h" | ||
+ | } | ||
+ | |||
+ | |||
+ | // Interpretator Lua | ||
+ | lua_State* L; | ||
+ | |||
+ | |||
+ | // Funkcja wywołującą funkcję ze skryptu Lua i zwracająca otrzymany wynik | ||
+ | int luaSub(int x, int y) | ||
+ | { | ||
+ | |||
+ | int sub = 0; | ||
+ | |||
+ | // nazwa funkcji w pliku sub.lua | ||
+ | lua_getglobal(L, "sub"); | ||
+ | |||
+ | // pierwszy argument | ||
+ | lua_pushnumber(L, x); | ||
+ | |||
+ | // drugi agrument | ||
+ | lua_pushnumber(L, y); | ||
+ | |||
+ | // wywołanie funkcji z dwoma argumentami i jedną wartością zwracaną | ||
+ | lua_call(L, 2, 1); | ||
+ | |||
+ | // pobranie wyniku i zdjęcie ze stosu | ||
+ | sub = static_cast<int>(lua_tointeger(L, -1)); | ||
+ | |||
+ | lua_pop(L, 1); | ||
+ | return sub; | ||
+ | } | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | int sub = 0; | ||
+ | |||
+ | // utworzenie maszyny wirtualnej Lua. | ||
+ | L = lua_open(); | ||
+ | |||
+ | // inicjalizacja standardowych bibliotek | ||
+ | luaL_openlibs(L); | ||
+ | |||
+ | // wczytanie skryptu Lua | ||
+ | if (luaL_dofile(L, "sub.lua")) | ||
+ | { | ||
+ | // Walidacja wczytania skryptu | ||
+ | std::cout << "Error while running script: " << lua_tostring(L, -1) << std::endl; | ||
+ | return 1; | ||
+ | } | ||
+ | |||
+ | // wywołanie funkcji | ||
+ | sub = luaSub( 10, 15 ); | ||
+ | |||
+ | std::cout << "Substraction: " << sub << std::endl; | ||
+ | |||
+ | // zamknięcie maszyny wirtualnej | ||
+ | lua_close(L); | ||
+ | return 0; | ||
+ | |||
+ | } | ||
+ | |||
+ | </code> | ||
+ | |||
+ | |||
+ | === Wywoływanie funkcji C++ w kodzie Lua === | ||
+ | |||
+ | Spróbujmy zrobić wykonać to samo zadanie odwrotni. Wywołajmy funkcję napisaną w C++ w kodzie Lua. Funkcje w Lua są wywoływane używając wskaźnika na funkcję: | ||
+ | |||
+ | ''typedef int (*lua_CFunction) (lua_State *L);'' | ||
+ | |||
+ | Wynika z tego, że funkcje przyjmują jako argument interpretator Lua i zwracają wartość int. Napiszmy funkcję w C++, która będzie liczyła sumę liczb przekazanych do funkcji, których liczba będzie zmienna: | ||
+ | |||
+ | **Listing 9** Funkcja liczącą sumę podanych liczb w C++ wywoływana z poziomu kodu Lua. | ||
+ | |||
+ | <code cpp> | ||
+ | |||
+ | |||
+ | |||
+ | #include <iostream> | ||
+ | |||
+ | // dołączenie nagłówków Lua | ||
+ | extern "C" | ||
+ | { | ||
+ | #include "lua.h" | ||
+ | #include "lualib.h" | ||
+ | #include "lauxlib.h" | ||
+ | } | ||
+ | |||
+ | |||
+ | // Interpretator Lua | ||
+ | lua_State* L; | ||
+ | |||
+ | // Funkcja przygotowana do wywołania w kodzie Lua | ||
+ | static int suma(lua_State *L) | ||
+ | { | ||
+ | // pobranie liczby argumentów | ||
+ | int n = lua_gettop(L); | ||
+ | |||
+ | int suma = 0; | ||
+ | |||
+ | // sumowanie argumentów | ||
+ | for (int i = 0; i < n; ++i) | ||
+ | suma += lua_tonumber(L, i); | ||
+ | |||
+ | // zwrócenie sumy | ||
+ | lua_pushnumber(L, suma) | ||
+ | |||
+ | // zwracamy ilość wartości zwracanych przez funkcję | ||
+ | return 1; | ||
+ | } | ||
+ | |||
+ | |||
+ | int main() | ||
+ | { | ||
+ | int sub = 0; | ||
+ | |||
+ | // utworzenie maszyny wirtualnej Lua. | ||
+ | L = lua_open(); | ||
+ | |||
+ | // inicjalizacja standardowych bibliotek | ||
+ | luaL_openlibs(L); | ||
+ | |||
+ | // rejestracja funkcji suma pod nazwą f_suma | ||
+ | lua_register(L, "f_suma", suma); | ||
+ | |||
+ | // wczytanie skryptu Lua | ||
+ | if (luaL_dofile(L, "zpr.lua")) | ||
+ | { | ||
+ | // Walidacja wczytania skryptu | ||
+ | std::cout << "Error while running script: " << lua_tostring(L, -1) << std::endl; | ||
+ | return 1; | ||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | // zamknięcie maszyny wirtualnej | ||
+ | lua_close(L); | ||
+ | return 0; | ||
+ | |||
+ | } | ||
+ | |||
+ | </code> | ||
+ | |||
+ | |||
+ | **Listing 10** Przykład wywołania w skrypcie Lua funkcji napisanych w C++ | ||
+ | <code cpp> | ||
+ | |||
+ | --zpr.lua | ||
+ | suma = f_suma(10, 20, 30, 40, 50) | ||
+ | print("Suma wynosi: ", suma) | ||
+ | </code> | ||
- | === Wywoływanie funkcji C++ w Lua === | ||
- | BUUU | ||
==== Więcej informacji ==== | ==== Więcej informacji ==== |