Zum Inhalt springen

Irrlicht - from Noob to pro: C++ Code von Lua aufrufen

Aus Wikibooks


Header einbinden

[Bearbeiten]

Wie im vorherigen Kapitel erläutert kann man von Lua auf C++ Code, genauer gesagt auf Funktionen, die in C++ geschrieben sind zugreifen. Dabei kann man sogar Rückgabewerte wieder zurück in das Lua Skript schreiben um mit diesen weiterzuarbeiten. Wir werden hier um die Vorgehensweise zu erklären zwei Funktionen von Lua aus ansteuern. Einerseits wollen wir zwei Werte addieren können und andererseits wollen wir die Werte auch darstellen können. Als erstes inkludieren wir die benötigten Headerdatein:

#include <iostream>
using namespace std;

extern "C"
{
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}

#pragma comment(lib, "lua5.1.lib")

Die Lua-Header werden über dieses "C-artige" Konstrukt eingebunden. Die untere Zeile bindet nur die statische Bibliothek in das Programm und ist nur unter Windows und dann auch nur bei dem Visual Studio zu verwenden. Bei allem anderen müssen die abhängigen Bibliotheken dem Projekt über die Einstellungen hinzugefügt werden.

Die C++ Funktionen erstellen

[Bearbeiten]

Nun erstellen wir uns unsere beiden benötigten Funktionen.

int displayResult(lua_State* lua);
int addValues(lua_State* lua);

Diese Signatur ist bindend! Die Funktion muss zwingend vom Typ int sein. Der Rückgabetyp teilt dem Lua skript mit, wie viele Rückgabewerte auf den Returnstack gepusht wurden. Als Übergabeparameter wurde ein Zeiger auf lua_State verwendet. Dieser Zeiger gibt uns alle Informationen über das aufrufende Lua Skript und somit auch die Anzahl und die tatsächlichen Parameter.

Als nächstes definieren wir unsere Funktionen und lernen dabei den Lua Stack kennen. Lua speichert alle Informationen, die vom Aurufendem Skript kommen in einer Stackarchtitektur. So kann der Programmierer sich Stück für Stück die Information holen, die er braucht. Am wichtigsten ist die Anzahl der Parameter, die vom Lua Skript übergeben wurden.

int displayResult(lua_State* lua)
{
        //paramCount = 1
	int paramCount = lua_gettop(lua);

	if(paramCount != 1)
	{
		return -1;
	}

	cout << "Result was " << lua_tonumber(lua, paramCount) << endl;

	return 0;
}

In diesem Fall würden wir paramCount eine 1 annehmen, da wir vereinbaren, dass die aufrufende Funktion genau 1 Parameter haben muss. Man kann es auch so gestalten, dass die Funktion beispielsweise bis zu 1 (also genau 0 oder 1) Parameter enthalten darf. Sollte diese Konvention nicht erfüllt sein, bricht das Lua Skript mit dem Fehler -1 ab. Falls die Parametervorgabe erfüllt ist konvertieren wir den ersten Parameter in einen Integer und geben diesen aus. Dabei hilft uns die Funktion lua_tonumber(lua_State*, int). Der erste Parameter ist einleuchtend, der zweite dafür wichtig. Denn diese gibt die Position im Stack an, auf der der gewünschte Parameter liegt. Dabei geht Lua wie folgt vor. Ganz oben liegt ein Wert, der angibt, wie viele Parameter der Stack enthält. Und darunter liegt der erste Parameter, darunter der zweite und somit liegt der n-te Parameter auf der n-te Stelle:

n paramCount
n-1 firstParam
n-2 secondParam
...
n-m (n-m)thParam

Die zweite Funktion soll dies nun verdeutlichen.

int addValues(lua_State* lua)
{
        //paramCount = 2
	int paramCount = lua_gettop(lua);

	if(paramCount != 2)
	{
		return -1;
	}

	//Lua-Stack:
	//3 paramCount
	//2 firstParam
	//1 secondParam

        //erster Parameter liegt direkt unter dem paramCount -> 2
	int first = lua_tonumber(lua, paramCount);
        //zweiter Parameter liegt direkt unter dem ersten-> 1
	int second = lua_tonumber(lua, paramCount - 1);

	lua_pushnumber(lua, first + second);
	return 1;
}

In dieser Funktion haben wir einen Aufruf von Lua mit 2 Parametern verarbeitet. Die Addition soll sofort in das Lua Skript zurückgeschrieben werden. Auch dafür sieht Lua eine Stackarchitektur vor - der Rückgabestack. Dabei pushen wir mittels lua_pushxxx(lua_State*, typedValue) den Rückgabewert auf den Stack. In unserem Fall schieben wir einen Integer auf den Stack und nutzen daher lua_pushnumber. Der Rückgabewert der C++-Funktion macht hingegen dem Lua Skript bekannt, wie viele Rückgabewerte sich auf dem Stack befinden, da es auch durchaus Funktionen gibt, in der 2 oder mehr Werte zurückgegeben werden und so das Lua Skript die Werte entsprechend auswerten muss.

Die C++ Funktionen mit Lua verbinden und aufrufen

[Bearbeiten]

Nun haben wir beide Funktionen geschrieben und Lua Ready gemacht. Nun müssen wir diese nur noch auf den lua-eigenen Funktionsstack schieben, damit diese auch von Lua verstanden werden können. Dabei machen wir nichts weiter als uns erst mal einen lua_State zu erzeugen und dann die Funktionen zu pushen.

int main(int argc, const char* argv[])
{
	lua_State* lua = lua_open();
	luaL_openlibs(lua);

	//unsere C++ Funktion heißt displayResult
	lua_pushcfunction(lua, displayResult);
	//mittels der Lua Funktion displayResult können wir diese C++ Funktion aufrufen!
	lua_setglobal(lua, "displayResult");

	lua_pushcfunction(lua, addValues);
	lua_setglobal(lua, "addValues");

Nun brauchen wir nur noch das entsprechende Lua Skript einbinden und ausführen lassen und schon werden uns die Ergebnisse angezeigt.

        luaL_dofile(lua, "myFile.lua");

	lua_close(lua);
	
	return 0;
}


Zur Grundlage liegt folgendes Lua Skript:

--[[
myFile.lua 
]]-- 
displayResult(addValues(5,4));
displayResult(addValues(2,11));

Als Ausgabe erscheint beim Kompilieren nun folgendes:

Result was 9
Result was 13

Nun können wir ganz unabhängig vom Code die zu addierenden Wertepaare verändern, da wir lediglich das Lua Skript ändern und die Implementierung lassen. Wir könnten unsere IDE schließen und nur noch die erzeugte Exe verwenden um uns die Ergebnisse der Addition zurückliefern zu lassen und haben somit den Vorteil der Skriptsprachen herausgearbeitet!

Gesamter Code:

[Bearbeiten]
#include <iostream>
using namespace std;

extern "C"
{
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}

#pragma comment(lib, "lua5.1.lib") 

int displayResult(lua_State* lua)
{
	int paramCount = lua_gettop(lua);

	if(paramCount != 1)
	{
		return -1;
	}

	cout << "Result was " << lua_tonumber(lua, paramCount) << endl;

	return 0;
}

int addValues(lua_State* lua)
{
	int paramCount = lua_gettop(lua);

	if(paramCount != 2)
	{
		return -1;
	}

	int first = lua_tonumber(lua, paramCount);
	int second = lua_tonumber(lua, paramCount - 1);

	lua_pushnumber(lua, first + second);
	return 1;
}


int main(int argc, const char* argv[])
{
	lua_State* lua = lua_open();
	luaL_openlibs(lua);

	lua_pushcfunction(lua, displayResult);
	lua_setglobal(lua, "displayResult");

	lua_pushcfunction(lua, addValues);
	lua_setglobal(lua, "addValues");

	luaL_dofile(lua, "myFile.lua");

	lua_close(lua);
	
	return 0;
}