This is Part 2 of my Integrating Lua into C++ Tutorial series.
For this part of the tutorial series, we will be discussing on how to interact with classes and objects, both in Lua and C++.
Check out these best online Lua courses and tutorials recommended by the programming community. Pick the tutorial as per your learning style: video tutorials or a book. Free course or paid. Tutorials for beginners or advanced learners. Check Lua community's reviews & comments.
Using a C++ class in Lua
First off, let’s bind a simple C++ class so that we can use the class in our lua code. You can name your class anything, but I’m going to use something simple, ‘CppObject’.
Create a class with the following interface and implementation:
CppObject.hpp
This is a simple c++ class with a function that prints out “Test1Function1 Called!” when called.
This code helps to bind the C++ object into a Lua object with the alias ‘CppObject.
Lets dissect the code line by line.
state.new_usertype<CppObject>('CppObject', 'TestFunction1', &CppObject::TestFunction1);
This line defines the new Lua Usertype and also defines the member function ‘TestFunction1’ to specify that the class contains this function.
state.do_string('cppObj = CppObject:new() cppObj:TestFunction1()');
In this line, we create a new object ‘cppObj’ of type ‘CppObject’ and invoked it’s member function.
If all goes well, you should printed:
TestFunction1 Called!
Congratulations! You now know how to play around with C++ Objects in your lua code. If you are really interested in learning more about this, The tutorials over in the Sol2 github page really covers this extensively.
Using a Lua class in Lua
Lua C Library Tutorial
Before we go over using a Lua class in C++, let’s first do it in Lua.
The Lua language does not have any implementation of classes but they do have something called metatable that allows you to do really cool stuffs. One of them is to implement a class system. For this, we will be using a library called middleclass. Middleclass is an OOP library that I really like to use. It’s very lightweight, and easy to use.
I will only be covering the basics, so head over to their github page for more info.
For starters, download the middleclass file and then add it into your scripts folder. I’ll show you how to set it up later.
For now, go to your scripts folder and create a new lua file with the following code:
TestClass.lua
The code above defines the class that we will be using, ‘TestClass’.
The initialize function will be called whenever a TestClass object is created. You can say that it’s the constructor of the class.
state['package']['path'] = (package_path + ';scripts/middleclass.lua').c_str();
Here, I initialized the middleclass library by adding it into the luastate”s package path.
state.do_file('scripts/TestClass.lua');
I then ran the ‘TestClass.lua’ file that defines my TestClass Lua class.
state.do_string('testObj = TestClass:new() testObj.TestFunctionCall()');
I then created a TestClass object and called it’s member function ‘TestFunctionCall’
If everything went well, you should be printing this on the console:
TestClass Created!
TestClass TestFunction Called!
This is just a basic functionality for using MiddleClass together with Sol2. I hope you find it useful.
Using a Lua class in C++
Now we get to the more interesting part of the tutorial, using a lua object in c++. I find that the best way for this is to create a wrapper class.
You actually do not need to create a wrapper class like ScriptObject as you can just save it into a sol::table but I would recommend making one to make your life easier.
ScriptObject.hpp
Whew this is rather long. This class omits out quite a few functions for it to be usable, but it contains the bare-bones of what we need for the tutorial. I wanted to keep it simple. Don’t worry though, I’ll share the full class implementation later.
This is the non-default constructor of the ScriptObject.
This function creates a Lua object of type ‘luaClassName’ and gives them a unique name for use in the luaState. The name is then saved in m_scriptVarName.
m_luaObjectData = (*m_pLuaState)[m_scriptVarName];
If you remember the previous tutorial, then this should be rather familiar. Here, we save the lua object into m_luaObjectData for easy access.
m_pLuaState->script(luaScript, [&isValidCreation](lua_State* state, sol::protected_function_result res) { isValidCreation = false; return res; });
The most complex part about this function would most probably be in this line. I created this lambda function to suppress an exception being thrown if the lua code called is not valid. In this case, it would usually be called if the Lua-class of the object you are trying to create is not valid.
If an exception is thrown, we will set isValidCreation to false, and then stop with the creation of the object.
If m_initialized boolean is set to true at the end of the function, it would mean that your object is successfully created.
This helper function helps us call the lua object’s member functions.
In the destructor, we set the object to nil and then let the garbage collection do all the work.
Alright, so we have already defined our ScriptObject wrapper class. So now let’s try using it.
So here we create our ScriptObject object and then call the “TestFunctionCall” function. The behaviour is totally the same as the previous example.
If everything went well, same as before, you should be printing this on the console:
TestClass Created!
TestClass TestFunction Called!
Summary
This part of the tutorial is where you start having the basic knowledge on OOP on Lua, as well as handling classes in both Lua and C++ codes.
Codes
CppObject.hpp
ScriptObject.hpp
ScriptObject.cpp
Main.cpp
Previous (Part 1b: The Basics) | Next(Part 2.5: Adding Templates to ScriptObject Class)
In this short tutorial I'll show how to run Lua programs from C and C++ and howto expose functions to them. It's easy!
Update: The code in this post has been updated for Lua 5.2.4. I haven'tchecked if the Lua 5.3 C API is backwards-compatible with 5.2. All the codehere is available on GitHub.
The first program will just create a Lua state object and exit. It will be ahybrid between C and C++. Since the two languages must include different files,we need to discern between them by checking for the existence of the__cplusplus
macro.
Notice that I'm being explicit about which version of Lua I'm using in thecode. If you trust that the Lua developers care about compatibility, you canjust #include <lua.hpp>
and so on directly.
The purpose of the program is just to make sure that we can compile, link andrun it without errors.
You need to let the compiler know where it can find the include files and theLua shared library. The include files are usually located in/usr/local/include
and the library files in /usr/local/lib
. Search yoursystem directories if needed. To compile the above program, pass thedirectories with -I
and -L
, respectively.
You may swap out g++
with llvm-g++
, or just c++
, depending on yourcompiler. If you're using a C compiler, use gcc
or llvm-gcc
— butremember to rename the file to first.c
.
Now try to run the program to make sure it doesn't segfault:
This one worked just fine.
Executing Lua programs from a host
The next step is to execute Lua programs from your C or C++ code. We'll createthe Lua state object as above, load a file from disk and execute it.
Put this into runlua.cpp
or runlua.c
:
You can reuse the compilation arguments from above:
or
Running Lua programs
Lua C Api Tutorial
Let's test this with some Lua programs. The first one prints the Lua versionand exits.
You may want to double-check that it works by running lua hello.lua
. It maynot be important for this trivial program, but can become important when youtry more advanced ones.
Now try it with runlua
:
You can even run bytecode-compiled programs:
We should also check that the error handling works. Put some garbage in a filecalled error.lua
, for example
Running it produces
Calling C functions from Lua
It gets very interesting when Lua programs call back to your C or C++functions. We'll create a function called howdy
that prints its inputarguments and returns the integer 123.
To be on the safe side, we'll declare C linkage for the function in the C++version of the program. This has to do with name mangling,but in this case, it really doesn't matter: Lua just receives a pointer to afunction, and that's that. But if you start using dynamic loading of sharedlibraries through dlopen
and dlsym
, this will be an issue. So let's do itcorrect from the start.
Copy the above program into a file called callback.cpp
and add the howdy
function.
We have to pass the address of this function to Lua along with a name. Put thefollowing line somewhere between the call to lua_newstate
andluaL_loadfile
:
Create a test program called callback.lua
Compile and test it
I told you it was easy!
What next?
Read the Lua C APIReference. You've learned enough now to get going with it. Did you see mynote about clearing the stack in howdy
? You may want to investigate that.
Find out how to integrate Lua closures with your C functions.
If you want to hide or catch console output from Lua, you need to figure thatout as well. I once did it by trapping io.write()
; I copied its code fromlualib.c
and changed io_write
to point to my own function. There isprobably a better way to do it, though. Doing so is useful for things like gameprogramming.
Use RAIIor smart pointers to manage resources like lua_State
.
I also strongly recommend to try out LuaJIT.Calling into your functions there is even easier, using LuaJIT's foreignfunction library. I'll write a blog post on how todo that as well. In short, just create ordinary C functions, compile as ashared library, copy their signatures into pure Lua source code and hook themup with LuaJIT's FFIlibrary.
Embed Lua In C Tutorial
LuaJIT runs between 10-20 and up to 135 times faster than interpreted Lua, soit's definitely worth it.