call Lua function from C++ conditionally - c++

I am using Lua to write scipts and embed them in C++. I use LuaBridge in this process.
In my Lua script, I have some variables which need to be retrieved at first for usage in C++, besides, I have a very simple function:
run = function()
print ("state is on!")
end
This function, however, is only called under a specific condition. i.e. only called when a "true" is obtained from C++ codes after a series of complicated calculations.
Limited by my Lua and LuaBridge knowledge, what I know is: after I do
loadscript(L, "script.lua")
lua_pcall(L,0,0,0)
I can read out variables and functions from Lua script by using
LuaRef blabla = getGlobal(L, "blabla")
But now, I need to read out variables and use them at first, then in a member function
LuaRunner::LuaRun(){}
defined in a separate C++ class
class LuaRunner
the condition will be obatined and this run() function will be called if the condition is "true". It would be best to call this run() function right in the C++ member function
LuaRunner::LuaRun(){}
due to restriction of further processing.
Therefore, I was wondering whether this would be possible:
Read out the function using
LuaRef run = getGlobal(L, "run")
together with other variables at the beginning and "save" this run() function somewhere in C++ codes (perhaps as a class member function), and then the run() function can be called later on by a pointer or an object in the same class. Would this be possible? If possible, how to do it? Or any other good ideas?

It's possible to store luabridge::LuaRef's in C++ to call them later just as you normally call any other function. Though sometimes there's no need to store LuaRef's anywhere. Once you load the script, all functions stay in your lua_State, unless you set them to nil or override them by loading another script which uses the same names for functions. You can get them by using getGlobal function. If your Lua function takes arguments, you can pass them, using LuaRef's operator() like this:
if(...) { // your call condition
LuaRef f = getGlobal(L, "someFunction");
f(arg1, arg2, ...); // if your function takes no arguments, just use f();
}

Related

Call C++ Functions in an active program from Lua

I'm not sure if my question makes any sense, but I certainly know it is near impossible to get the results from Google. Firstly, what I do not want. I don't want to call some function that prints "hello world" or adds two numbers together, I want to load a Lua script from a C++ program, but allow the script to modify variables of the C++ program, using functions. For example, imagine I have a C++ program like this:
class Foo
{
private:
int number = 0;
public:
void setNumber(const int& newNumber) {number = newNumber;}
}
int main()
{
Foo foo;
//Load and execute Lua script, with foo object
return 0;
}
How could I allow the Lua script to do foo.setNumber() (preferably without foo.)? This may be a very simple question, but as mentioned above, almost all information on Google when searching "Call C++ Function from Lua" assume there is no program, but just a .cpp/hpp file with some functions that you want to call.
I'm on Linux (Ubuntu), but the program needs to compile on all platforms (Windows and Mac)
This is asked here fairly regularly.
To roll your own binding you should:
Master Lua metatables completely.
Read the Programming in Lua stuff on the C API, particularly the part on classes. Alternatively you can read the manual, read the source (API headers especially), and do some googling, but the book will probably save you some time.
Broadly, you expose a C++ class instance to Lua by creating a Lua "userdata" containing a pointer to the class instance and passing this to the Lua script. A userdata is an opaque type; the Lua script can't actually do anything with it (other than pass it around) unless you give it a metatable. At the very least you must implement the __index metamethod on the userdata, which allows your C++ code to intercept attempts to index the userdata and return something meaningful, and the __gc metamethod, which allows your C++ code to delete the exposed C++ object when the corresponding Lua userdata is garbage collected.
For instance, you create a function called createFoo which creates a Foo instance, wraps the pointer as a userdata, applies a metatable implementing __index to it, and returns it to the Lua script.
When the user runs foo.setNumber, your C++ __index metamethod is called with the userdata and the string "setNumber". It's up to you what you return and this determines what foo.setNumber evaluates to in the Lua script. You want foo.setNumber to evaluate to a lua_CFunction which expects a Foo userdata as its first parameter, so that your class methods can be called idiomatically from Lua (i.e. foo:setNumber(12), which is syntax sugar for foo.setNumber(foo, 12)).
It's a very low level and manual process, and once you get the hang of it you're going to end up create a library/templates/macros whatever to do the boilerplate for you. At that point you may want to evaluate the myriad C++ binding libraries that exist. However, thanks to the Law of Leaky Abstractions it's a very good idea to learn to do this manually first.

How do you bind C++ member methods and member variables with the Lua C API?

All the googling I've done so far has turned up things that are very close but just aren't quite cutting it for what I'm trying to do.
Let me describe this in the most basic way possible:
Imagine you have a C++ class
class A
{
public:
int Method();
int Variable;
};
Now imagine you instantiate A* Foo;
Now imagine you have a .lua file with this 3 line function:
function Test()
local n = Foo:Method();
Foo.Variable = 0;
local m = Foo.Variable;
end
How can you bind the object A* to lua such that all those things are doable?
Pseudocode-wise, my first attempt went like this, partly from copy pasting examples:
In a function only called once, regardless of the number of instances of A:
create newmetatable( MT )
pushvalue( -1 ) (i dont really get this)
setfield( -2, "__index" )
pushcfunction with a static version of Method that unpacks the A* pointer from checkudata
setfield( -2, "Method" )
In an init function called for each instance, e.g. Foo:
create a pointer to Foo with newuserdata
setmetatable( MT )
setglobal with Foo to make the name available to lua
In a test function in main:
pcall a test function with the 3 lines of .lua mentioned above, by global name
When doing this, Foo:Hide(); successfully called my static function, which successfully unpacked the pointer and called its member Hide().
So far so good for :Method().
Then I tried to support the .Variable access. Everyone seemed to be saying to use metatables again this time overriding __index and __newindex and make them a sort of generic Get/Set, where you support certain keys as valid variable links e.g. if( key == "Variable" ) Variable = val;
This also worked fine.
The problem is trying to put those two things together. As soon as you override __index/__newindex with a getter/setter that works on Variable, the Method() call no longer calls the Method() static function, but goes into the __index function you bound instead.
All of that said, how does one support this seemingly basic combination of use cases?
Actual code snippets would be much more appreciated than purely theoretical chatter.
Reminder: please respond using the basic C API only, not third party stuff.
the Method() call no longer calls the Method() static function, but
goes into the __index function you bound instead.
So program it so that if the key exists in the table, return that first, else go for getter/setter.

Custom bindings with boost::python [duplicate]

I am trying to achieve call Python functions from C++. I thought it could be achieved through function pointers, but it does not seem to be possible. I have been using boost.python to accomplish this.
Say there is a function defined in Python:
def callback(arg1, arg2):
#do something
return something
Now I need to pass this function to C++, so that it can be called from there. How do I write the code on C++ side using boost.python to achieve this?
If it might have any name:
Pass it to a function that takes a boost::python::object.
bp::object pycb; //global variable. could also store it in a map, etc
void register_callback(bp::object cb)
{
pycb = cb;
}
If it is in a single known namespace with a consistent name:
bp::object pycb = bp::scope("namespace").attr("callback");
bp::object has operator() defined, so you call it just like any function
ret = pycb()
Not a clue. But you can use PyObject_Call() to call it once you have the function object.
I've not used it before, but the reference manual has a section called Calling Python Functions and Methods which seems to show how to do this.
I used PyRun_SimpleString("myFunction()") as quick hack, as my function's name was known, took no args and lived in the __main__ namespace. Note you additionally need to get lock GIL if you are multi-threaded.

LuaBind: How to bind specific instance of class to Lua?

(sidenote: This is game programming)
Binding entire classes to Lua using LuaBind is easy:
class test
{
test()
{
std::cout<<"constructed!"<<std::endl;
}
void print()
{
std::cout<<"works!"<<std::endl;
}
}
//somewhere else
module[some_lua_state]
[
class_<test>("test")
.def(constructor<>())
.def("print",&test::print)
];
Now I can create instances of the class in Lua and use it:
lua_example.lua
foo = test() //will print "constructed!" on the console
foo:print() //will print "works!" on the console
However, now I'd like to bind a specific instance of test to Lua. This would enable me to pass objects down to Lua, e.g. an instance of the Player-class and do something like:
Player:SetPosition(200,300)
As opposed to going the hard way and having something like
SetPosition("Player",200,300)
where the corresponding C++ SetPosition function needs to look up a std::map to find the player.
Is this even possible and if so, how can I do it in LuaBind?
You don't bind instances of classes to Lua. Instances of classes are just data, and you can pass data to Lua via the usual means. C++ objects are special though; because they're registered through Luabind, you have to use Luabind methods to provide them to Lua scripts.
There are several ways to give data to Lua, and Luabind covers all of them. For example, if you have an object x, which is a pointer to class X that is registered with Luabind, there are several ways you can give Lua x.
You could set the value of x into a global variable. This is done through Luabind's object interface and the globals function:
luabind::globals(L)["NameOfVariable"] = x;
Obviously, you can put that in another table, which lives in another table, etc, which is accessible from the global state. But you'll need to make sure that the tables all exist.
Another way to pass this data to Lua is to call a Lua function and pass it as a parameter. You can use the luabind::object interface to get a function as an object, then use luabind::call_function to call it. You can then just pass x as a parameter to the function. Alternatively, if you prefer the lua_pcall-style syntax, you can wrap x in a luabind::object and push it into the stack with luabind::object::push.

Passing existing C++ objects to Lua and calling the passed objects' member functions

I'm working on a little simulation project which uses Lua to drive the behavior of individual units (ants) and using Luabind to glue the C++ and Lua sides together. Each individual ant (there are different types, derived from the base class Ant) has a Run() function, which calls the appropriate script; the script then carries out whatever actions need to be taken, calling the exposed class functions and possibly free functions. I've gotten the Run function (in C++) to successfully execute the matching Run function in the Lua script (which just prints some text at the moment).
void AntQueen::Run()
{
lua->GetObject("QueenRun")(GetID());
}
lua is just a manager class which retrieves the function from the script. The above is calling the following in a Lua file:
function QueenRun(ID)
print("The Queen is running!")
print(ID)
end
And Luabind registration looks like this for the AntQueen class:
void Register(lua_State *luaState)
{
using namespace luabind;
module(luaState)
[
class_<AntQueen, Ant>("AntQueen")
.def("Eat", &AntQueen::Eat)
.def("ExtractLarvae", &AntQueen::ExtractLarvae)
.def("GetMaxLarvaeProduced", &AntQueen::GetMaxLarvaeProduced)
.def("GetNumAvailLarvae", &AntQueen::GetNumAvailLarvae)
];
}
The way it's set up now, ants are created, removed, and found through a simple factory/manager. Each ant can be retrieved by calling static Ant* AntFactory::GetAntByID(const int ID) which just finds the ant in a hash map and returns a pointer to the ant. What I'm trying to do is get Lua to be able to do something like the following:
function QueenRun(ID)
ant = GetAntByID(ID)
larvae = ant:GetNumAvailLarvae()
print(larvae)
ant:Eat()
end
The above is just a made up example, but hopefully it shows what I'm trying to achieve. I don't want Lua to garbage collect the objects, because they are managed already on the C++ side. While testing everything out, any attempt to do the following:
ant = GetAntByID(ID)
in Lua resulted in abort() being called and the program crashing and burning.
R6010
-abort() has been called
I just seem to be missing something with how everything gets shuttled back and forth (this is my first foray into gluing Lua and C++ together beyond toy programs). I'm pretty sure passing a plain pointer isn't the way to do it; lightuserdata seems to be what I'm looking for, but it also has a bunch of restrictions.
So to sum up: What is going on here that causes abort to be called and how can I use Luabind/the Lua C API to get a pointer to a C++ object passed to Lua and call member functions on that pointer as if it were an object (without letting Lua garbage collect it)?
The solution to this problem seemed to be tied to the AntFactory class/member functions being static. As soon as I switched from registering and using this:
//C++
static int AntFactory::CreateAnt(foo, bar)
{}
//Lua
factory:CreateAnt(foo, bar)
to an instantiated object and regular member functions like this:
//C++
int AntFactory::CreateAnt(foo, bar)
{}
//Lua
factory:CreateAnt(foo, bar)
everything worked with no problems at all (after also pushing the factory to the global table). I think the reason for this is that trying to call static member functions on a non-instantiated C++ object fails due to Lua (or Luabind?) not being able to have an object to query for calls.