Home

Awesome

Lutok2

C++/Lua binding helper for Lua 5.1 and LuaJIT 2.x+.

Dependencies

To use Lutok2 successfully you'll need:

Usage

First of all, you'll need to add lutok2 include directory into list of include directories. To use it, add following include line and you're good to go.

#include <lutok2/lutok2.hpp>

All Lutok2 functions and classes are stored in lutok2 namespace so you might actually want to use following two lines instead:

#include <lutok2/lutok2.hpp>
using namespace lutok2;

API

API of Lutok2 library is divided into two sections:

State

Stack

Stack manipulation

Table manipulation

Pushing values into stack

Getting values from stack

Value manipulation

Meta tables

Functions

Lua registry

Examples

Lutok2 usage in a library

#include <lutok2/lutok2.hpp>
using namespace lutok2;

#if (BUILDING_MyLibrary || MyLibrary_EXPORTS) && HAVE_VISIBILITY
#define MyLibrary_DLL_EXPORTED __attribute__((visibility("default")))
#elif (BUILDING_MyLibrary || MyLibrary_EXPORTS) && defined _MSC_VER
#define MyLibrary_DLL_EXPORTED __declspec(dllexport)
#elif defined _MSC_VER
#define MyLibrary_DLL_EXPORTED __declspec(dllimport)
#else
#define MyLibrary_DLL_EXPORTED
#endif

namespace MyLibrary {
    class MyClass {
    public:
        std::string value;
        int method(int param1){
            return param1 + 1;
        };
    };
    
	class LuaMyClass : public Object<MyClass> {
	public:
	    // Define all properties and methods here
		explicit LuaMyClass(State * state) : Object<MyClass>(state){
			LUTOK_PROPERTY("value", &LuaMyClass::getValue, &LuaMyClass::setValue);
			LUTOK_PROPERTY("valueGetterOnly", &LuaMyClass::getValue, &LuaMyClass::nullMethod);
			LUTOK_PROPERTY("valueSetterOnly", &LuaMyClass::nullMethod, &LuaMyClass::setValue);
			LUTOK_METHOD("method", &LuaMyClass::method);
		}

		MyClass * constructor(State & state, bool & managed);
		void destructor(State & state, MyClass * object);

	    int method(State & state, MyClass * object);
	    int getValue(State & state, MyClass * object);
	    int setValue(State & state, MyClass * object);
	};

    int LuaMyClass::method(State & state, MyClass * object){
        Stack * stack = state.stack; //a shortcut
        if (stack->is<LUA_TNUMBER>(1)){
            int param1 = stack->to<int>(1);
            int result = object->method(param1);
            stack->push<int>(result);
            return 1;
        }else{
            state.error("Expects a number");
            return 0;
        }
    }
    
    // Initialize object
    MyClass * LuaMyClass::constructor(State & state, bool & managed){
        MyClass * object = new MyClass;
        object->value = "Unspecified value";
        return object;
    }
    
    // Don't forget to release alloced memory inside of object
    void LuaMyClass::destructor(State & state, MyClass * object){
        delete object;
    }
    
    int LuaMyClass::getValue(State & state, MyClass * object){
        Stack * stack = state.stack; //a shortcut
        stack->push<const std::string &>(object->value);
        return 1;
    }
    
    int LuaMyClass::setValue(State & state, MyClass * object){
        Stack * stack = state.stack; //a shortcut
        if (stack->is<LUA_TSTRING>(1)){
            const std::string inputValue = stack->to<const std::string>(1);
            object->value = inputValue;
        }
        return 0;
    }

	// Must be called with a table value on stack - because of setField
	void initLuaMyClass(State* state, Module& module){
	    Stack * stack = state->stack; //a shortcut
	    state->registerInterface<LuaMyClass>("MyLibrary_LuaMyClass");
	    // Register LuaMyClass constructor in current table interface
	    stack->setField("LuaMyClass")
	}

	int init(State & state){
		return 0;
	}
	
	int processObject(State & state){
        LuaMyClass * interfaceLuaMyClass = state.getInterface<LuaMyClass>("MyLibrary_LuaMyClass");
        // Retrieve MyClass object from Lua - in the 1st argument
        MyClass * object = interfaceLuaMyClass->get(1);
        if (object){
            MyClass * newObject = new MyClass;
            newObject->value = object->value;
            /*  !!! IMPORTANT !!!
                1. You need to set second argument (luaManaged value) to true to activate automatic object cleanup
                which will call destructor on GC. Otherwise you risk memory leaks!
                
                2. If you're pushing objects that are managed by other classes, set this parameter to false.
                GC will not call destructor when the Lua object is collected.
                You can think of this as a light object reference.
            */
            bool luaManaged = true;
            interfaceLuaMyClass->push(newObject, luaManaged);
            return 1;
        }else{
            state.error("Expects a LuaMyClass object");
            return 0;
        }
	}
};

extern "C" MyLibrary_DLL_EXPORTED int luaopen_MyLibrary(lua_State * L){
	State * state = new State(L);
	Stack * stack = state->stack;
	Module MyLibrary_module;

	stack->newTable();
	
	initLuaMyClass(state, MyLibrary_module);
	
	MyLibrary_module["init"] = MyLibrary::init;
	MyLibrary_module["processObject"] = MyLibrary::processObject;

	state->registerLib(MyLibrary_module);
	return 1;
}

Lutok2 usage in application

#include "lutok2/lutok2.hpp"

using namespace lutok2;

int testFun(State & state){
	state.loadString(
		"print(\"Hello world from lambda function!\") \
						"
						);
	state.stack->call(0, 0);
	return 0;
}

class TestObj {
private:
	std::string value;
public:
	TestObj(const std::string & value){
		this->value = value;
	}
	std::string getValue(){
		return value;
	}
	void setValue(const std::string & value){
		this->value = value;
	}
};

class LTestObj : public Object < TestObj > {
public:
	explicit LTestObj(State * state) : Object<TestObj>(state){
		LUTOK_PROPERTY("value", &LTestObj::getValue, &LTestObj::setValue);
		LUTOK_METHOD("method", &LTestObj::method);
	}
	TestObj * constructor(State & state, bool & managed){
		TestObj * obj = nullptr;
		Stack * stack = state.stack;
		if (stack->is<LUA_TSTRING>(1)){
			const std::string value = stack->to<const std::string>(1);
			obj = new TestObj(value);
		}
		managed = true;
		return obj;
	}
	void destructor(State & state, TestObj * object){
		delete object;
	}
	int operator_concat(State & state, TestObj * a, TestObj * b){
		push(new TestObj(a->getValue() + b->getValue()), true);
		return 1;
	}

	int getValue(State & state, TestObj * object){
		state.stack->push<const std::string &>(object->getValue());
		return 1;
	}
	int setValue(State & state, TestObj * object){
		const std::string value = state.stack->to<const std::string>(1);
		object->setValue(value);
		return 0;
	}
	int method(State & state, TestObj * object){
		state.stack->push<const std::string &>("Hello");
		return 1;
	}
};


int main(char ** argv, int argc){

	State state;
	state.openLibs();

	state.stack->push<Function>([](State & state) -> int{
		state.loadString(
			"print(\"Hello world from lambda function!\") \
									"
									);
		state.stack->call(0, 0);
		return 0;
	});
	state.stack->setGlobal("testing");

	state.registerInterface<LTestObj>("testObj");
	state.stack->setGlobal("testObj");

	try {
		state.loadFile("test/test.lua");
		state.stack->call(0, 0);
	}
	catch (std::exception & e){
		printf("Can't load test file: %s", e.what());
	}
	return 0;
}

Authors