-
Notifications
You must be signed in to change notification settings - Fork 28
The LuaFunction Object
Now that we know what LuaTables are, let us turn our sights to LuaFunctions. Actually LuaFunctions in C++ are implemented as a complex system of templates and take advantage of SFINAE. But the only thing you need to know to be able to use them is that you will be passing a LuaFunction<SIG>
around.
A LuaFunction<SIG>
represents a function in Lua. How? Here's how: Let us try and extract a function that was defined in a Lua script.
#Extracting LuaFunctions
The function:
-- myscript.lua
function addThreeNumbers(a,b,c)
return a + b + c
end
is an
#include <fstream>
#include <streambuf>
#include <string>
#include <iostream>
#include <luacppinterface.h>
int main()
{
std::ifstream file("myscript.lua");
std::string script((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
Lua lua;
LuaTable global = lua.GetGlobalEnvironment();
// Extract the function into C++
auto addThreeNumbers = global.Get< LuaFunction<int(int,int,int)> >("addThreeNumbers");
// Try and call it
int result = addThreeNumbers.Invoke(1,2,3);
return 0;
}
Wait a minute, Lua functions don't have a specific signature. Why are we forcing one on it? Well, Lua knows no function signatures, but C++ does, and it is advantageous for us to "cast" a function to a particular signature when we want to use it in C++ to allow us to use it normally.
We must also be vigilant and ensure that the function that we are extracting (or providing) to C++ will work with the signature that we provide.
We can also invoke this LuaFunction object by calling Invoke on it with the parameters that are defined in its signature, and LuaCppInterface will try to convert the result into an int and return it to us.
What if it fails and the result is a string instead? Lua's rules dictate that either NULL or 0 is returned from functions that attempt to get the wrong type. This means you will get 0.
If you think this should throw an exception, let me know. Make an issue.
##Types allowed
Like the LuaTable, the function signature can consist of anything in the list of Primitive Types.
For example:
void doChristmasThings(LuaTable santa)
{
auto doSomething = santa.Get< LuaFunction<void()> >("doSomething");
auto registerBoy = santa.Get< LuaFunction<LuaTable(std::string name, int age)> >("registerBoy");
auto saySomething = santa.Get< LuaFunction<void(std::string)> >("hohoho");
auto isGoodBoy = santa.Get< LuaFunction<bool()> >("isGoodBoy");
}
An additional thing is that the return type can be void too (not in the list). Marking a function as void only means that its return value is completely ignored and thrown away.
We can also take it a step further, functions can return functions. You can also pass functions to Lua and pass functions around this way, such as for sorting or filtering, making writing code a joyous
void doContrivedThing(LuaTable santa)
{
// get a function that returns a function
LuaFunction< LuaFunction<void()>(std::string boyName) >
getSendPresent = santa.Get<
LuaFunction<
LuaFunction<void()>(std::string boyName)
>
>("getSendPresent");
// get the function that returns a function to give you a function
LuaFunction<void()> sendPresent = getSendPresent.Invoke("billy");
// and call that function
sendPresent.Invoke();
}
#Creating LuaFunctions
Obviously, this should be a two-way thing. If we can get LuaFunctions from the script, we should be able to pass functions that we create to the script as well.
LuaFunction< void() > lambdaFunc = lua.CreateFunction<void()>([&]()
{
std::cout << "called lambdaFunc" << std::endl;
});
global.Set("lambdaFunc", lambdaFunc);
After running the code above a global var called lambdaFunc will be this function. You can then call the function in a Lua script as below:
-- script.lua loaded after setting lambdaFunc
lambdaFunc()
Since the LuaFunctions that you get from Lua and those you create in C++ are the same, it is also possible to call them the same way in C++.
lambdaFunc.Invoke()
You might wonder why but this is for uniformity and in case you have an interface that needs to provide for both C++ and Lua.
#Things to watch out for
Functions are implemented in such a way that they don't have variable sizes, so variadic is out. The limit on the number of parameters a function can have is 8.
I do not believe in long function sizes and you can pass a LuaTable back as an array of things if you want a variable number of things to be worked on.
From here we have an understanding now of LuaFunction. It is very simple to use so go ahead and experiment.
As a Lua veteran you may also have heard that functions can yield. This is possible in coroutines. It is possible to start a coroutine in Lua and using LuaCppInterface, which makes it a very powerful tool in your box. Have a look at it in the next step in the tutorial:
Next: The LuaCoroutine Object