LUA - prosty sposób na zaawansowaną konfigurację

06.09.2010 - Łukasz Milewski
TrudnośćTrudność

Wykonywanie kodu

Jeżeli udostępnimy nasz program innym, to będzie on uruchamiany na wielu różnych maszynach. Są słabsze i mocniejsze komputery. Mają różne podzespoły i różne zainstalowane oprogramowanie (np. system operacyjny).

Dzięki temu, że zdecydowaliśmy się na język skryptowy, możemy w łatwy sposób napisać plik konfiguracyjny, który dostosuje się do sprzętu i oprogramowania użytkownika. Osiągniemy to dzięki temu, że Lua daje możliwość wykonania kodu (a nie tylko przypisywania wartości do zmiennych). Możemy np. sprawdzić jaka jest rozdzielczość ekranu, czy systemem operacyjnym. Po sprawdzeniu odpowiednich warunków ustawiamy zmienne konfiguracyjne zależnie od wyników testów.

Nasz przykład będzie trochę bardziej praktyczny. Programy często wykorzystują liczby losowe, jednak podczas testów chcielibyśmy móc uruchamiać program wielokrotnie z takimi samymi danymi. Dzięki temu będziemy mogli powtórzyć błąd gdy tylko się pojawi. Powoduje to potrzebę uruchomienia programów w trybie test oraz w trybie zwykłym. Zobaczmy jak uzyskać taki efekt w praktyce. Możemy np. napisać taki plik konfiguracyjny (config_3.lua):

1
2
3
4
5
6
7
8
testing = true;
 
if testing then
   seed = 42;
else 
   math.randomseed(os.time());
   seed = math.floor(math.random() * 10000); -- początkowy seed dla random'a
end

W kodzie programu pobieramy wartość seed ze skryptu i używamy jej w funkcji srand. Zauważmy, że dzięki temu program w trybie testing zawsze będzie zachowywał się tak samo, a w trybie normalnym będzie trochę mniej przewidywalny. Kod programu w C++ nie zmienia się. Zmienną seed możemy użyć np. tak:

1
srand(luabind::object_cast<double>(luabind::globals(lua)["seed"]));

Możliwość wykonywania kodu przydaje się gdy dane wejściowe można wygenerować. W cyklu artykułów o grze 2D używamy sprite'ów. Każdy sprite ma współrzędne klatek w pliku z grafiką. Możemy użyć pętli Lua do wygenerowania pozycji wszystkich klatek sprite'a (config_4.lua):

1
2
3
4
5
player = {}
for i = 0, 5 do
   player[i].x = i * 32
   player[i].y = 64
end

Dynamiczne tworzenie kodu

Zazwyczaj pisanie programu wygląda tak, że piszemy kod, kompilujemy go i udostępniamy program użytkownikom. Po procesie kompilacji nie ma możliwości zmiany działania programu (np. dodania całkowicie nowej funkcjonalności, albo usunięcia starej). Jeżeli program zostanie uruchomiony to możliwości zmian są jeszcze mniejsze - np. zmiana stałych konfiguracyjnych często przestaje być możliwa.

Zastosowanie Lua pozwala na bardzo ciekawą sztuczkę. Jeżeli chcemy to możemy już po skompilowaniu i uruchomieniu programu dopisać kod w Lua i podać go jako wejście naszego programu. Program może następnie (cały czas działając) skompilować ten kod i uruchomić, rozszerzając w ten sposób swoje możliwości bez potrzeby wyłączenia się.

Spójrzmy na prosty przykład, w którym wykorzystujemy funkcję luaL_dostring aby skompilować i wykonać kod podany przez użytkownika. W tym przypadku pozwalamy mu na wpisanie dowolnego wyrażenia arytmetycznego (np. (2+3)*5), podczas gdy program oczekuje liczby. Jest to najprostszy sposób na obliczanie tego typu wyrażeń - pomyślmy jak trudne jest napisanie parsera wyrażeń matematycznych bez pomocy Lua.

konfiguracja (plik config_5.lua) wygląda tak:

1
liczba = 42;

Plik C++ (config_5.cpp):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
 
#include <luabind/luabind.hpp>
#include "lua.hpp"
 
int main() {
    lua_State* lua = luaL_newstate();
    luaL_openlibs(lua);
    luabind::open(lua);
 
    if (luaL_dofile(lua, "config_5.lua") != 0) {
        std::cout << lua_tostring(lua, -1) << "\n";
        lua_pop(lua, 1);
        return 0;
    }
 
    std::string wybor = "1";
    while (wybor != "3") {
        int liczba = luabind::object_cast<int>(
                             luabind::globals(lua)["liczba"]);
 
        std::cout << "1. Pokaz liczbe\n"
                  << "2. Wczytaj liczbe\n"
                  << "3. Wyjście\n";
        std::cout << "\n\tWybierz opcje: ";
        std::cin >> wybor;
        
        if (wybor == "1") {
            std::cout << "\nLiczba i jej kwadrat = " 
                      << liczba << " " << liczba * liczba << "\n\n";
        }
        else if (wybor == "2") {
            std::cout << "\nPodaj wyrazenie definiujace liczbe: ";
            std::string wyrazenie;
            std::cin.ignore();
            std::getline(std::cin, wyrazenie);
            luaL_dostring(lua, 
                         ("liczba = " + wyrazenie + "; ").c_str());
        }
    }
 
    lua_close(lua);
}

Co dalej?

Lua jest językiem bardzo często wykorzystywanym do tego, aby dać możliwość tworzenia modów. Wiele gier wykorzystuje taką sztuczkę. Np. w World of Warcraft gracze mają możliwość tworzenia skryptów, które ułatwiają grę.

Wspomniany program Adobe Photoshop Lightroom jest napisany w 40% w Lua. Program o zupełnie innym przeznaczeniu, nmap również wykorzystuje Lua. Tak samo jak edytor tekstu SciTE. Te przykłady świadczą o wszechstronności tego języka.

Więcej informacji na temat wykorzystania Lua w praktycznych projektach, bardzo rozbudowany kurs tego języka i darmowe książki o Lua można znaleźć na stronie języka.

5
Twoja ocena: Brak Ocena: 5 (2 ocen)

Copyright © 2008-2010 Wrocławski Portal Informatyczny

design: rafalpolito.com