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.
Related
---Context---
I want to have a class called "fileProcessor". This class is completely static and merely serves as a convinient namespace (within my normal library namespace) for some global function. This is a basic blueprint of the class with only the relevant stuff
class fileProcessor{
private:
lua_State* LUA_state;
public:
static std::variant<type1,type2> processFile(const char* filePath,const char* processorScript);
}
Please note again that I ommitted most of the stuff from the class so if anything seems odd ignore it.
What process file is supposed to do is:
Read the filePath file, storing all directives including it (this is my own filetype or style of syntax. This is already handeled correctly). The directives are stored with strings, one for the command and one for everything after it.
Read the script file and check if it has a commented out fileProcessor line at the top. This is to make sure that the lua script loaded is relevant and not some random behaviour script
Load and compile the lua script.
Make all read directives available (they are saved in a struct of 2 strings as mentioned before)
Run the file and recieve a object back. The object should only be of types that I listed in the return type (variant)
I am having problems with step 4 and one vital part of the scripting.
---Question---
How can I make the creation of a full new object of type1 or type2 possible within lua, write to it from within lua and then get it back from the lua stack into c++ and still know if its type1 or type2?
---No example provided since this question is more general and the only reason I provided my class is for context.---
It seems like you are trying to do it the other way around. I quote a part of this answer:
...you are expecting Lua to be the primary language, and C++ to be the client. The problem is, that the Lua C interface is not designed to work like that, Lua is meant to be the client, and all the hard work is meant to be written in C so that Lua can call it effortlessly.
If you are convinced there is no other way that doing it other way around you can follow the workaround that answer has given. Otherwise I think you can achieve what you need by using LUA as it meant to be.
LUA has 8 basic types (nil, boolean, number, string, userdata, function, thread, and table). But you can add new types as you require by creating a class as the new type in native C++ and registering it with LUA.
You can register by either:
Using some LUA helper for C++ like luna.h (as shown in this tutorial).
Pushing a new lua table with the C++ class (check this answer).
Class object instance is created in your native C++ code and passed to LUA. LUA then makes use of the methods given by the class interface.
I'm using Lua for scripts in my C++ game. I want to be able to attach scripts to entities, and based on which functions are defined in the script, register for callbacks which will run the script functions at the appropriate time.
I believe that I can encapsulate different scripts from each other, by making the "script" into a table. Basically, ... lua source code ... would become ScriptName = { ... lua source code ... }. Then instead of calling func(), I'd call ScriptName.func(), and thus two scripts defining the same function (aka registering for the same event) wouldn't trample over each other.
My problem now is in encapsulating different entities sharing the same script. Obviously I don't want them to be sharing variables, but with what I'm doing now, any variable defined by a script would be shared by every instance of that script, which is just really bad. I could maybe try something similar to my above solution on the source level, by wrapping every script with EntityID.ScriptName = { ... } before compiling it. Something tells me there's a better way, though, I just don't know it.
Another factor is that scripts need to be able to reference entities and scripts/components relative to a specific entity. If I use the above method the best solution to this would be passing entity IDs around as strings which could reference the table specific to that entity, I think? At this point I really have no idea what I'm doing.
In order for a script to interact with a C++ object, the typical approach is to have the C++ code expose the object to Lua as a userdata (a wrapper for a pointer) and provide C++ functions that the script can call, passing the userdata as parameter. In the C++ code, that userdata gives you the object that the function should to operate on. It's equivalent to a "this" pointer.
You usually do this by putting the C++ functions into a metatable associated with the userdata, so they can be called like methods in the Lua code (i.e. objectIGotFromCpp:someMethod('foo').
ScriptName.func(), and thus two scripts defining the same function (aka registering for the same event) wouldn't trample over each other.
Rather than relying on accessing globals or naming conventions, ect. it's much cleaner to simply provide a callback that Lua scripts can use to register for events.
If I use the above method the best solution to this would be passing entity IDs around as strings
No reason. An entity in your C++ code is a pointer to an object on the heap. You can pass that pointer directly to Lua as userdata. Lua can pass that back to your C++ code and give you direct access to the object, rather than going through some object-to-ID mapping.
I'm thinking about how to design my APIs, I plan to have a C++ application with a scripting layer in LUA .
For a couple of key points in my design I would like to give the ability to the user to create a function object that represents what he wants to do in LUA, than send this function object from LUA to C/C++ .
In pseudo code, in C++ I have a class T
class T {
...
int num1 = 0;
float num2 = 0.0f;
std::string str{"NONE"};
...
};
And I would like to manipulate an instance of T with a function object provided from LUA like so
void applyFunc(T t,F f){
f(t);
}
The problem is that I can't find nothing in LUA that creates a function object like a C++11 lambda or std::function or any other object that can be considered a function object.
Really my point is: how to define a C++ compatible function object in LUA ?
The problem is that I can't find nothing in LUA that creates a function object like a C++11 lambda or std::function or any other object that can be considered a function object.
That's what the function keyword does.
This is a lambda. It's easy enough to pass these to C++ and let the C++ code call them.
As for this:
void applyFunc(T t,F f){
f(t);
}
In principle it's easy: push the C++ object pointer onto the Lua stack as a userdata and call the Lua function. The problem is the Lua code can't do anything with a C++ pointer.
If you want the Lua code to be able to manipulate the object you pass it, you'll need to write manipulator methods in C++ and expose them to Lua. You usually do that by creating a metatable for the userdata.
There are libraries that this that automatically for C++. If you want to do it by hand (my preference), you should probably start here.
If you Google "C++ object Lua __index" that should net you numerous examples. I could code up an example later, but I'm at work at the moment.
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.
I have a class that is wrapped with swig, and registered with lua. I can create an instance of this class in a lua script, and it all works fine.
But say I have an instance of a class made in my c++ code with a call to new X, and I have la lua_state L with a function in it that I want to call, which accepts one argument, an instance of X... How do I call that function. Here is (some) of the code in question (I've omitted the error handling stuff):
main.cpp
class GuiInst;
extern "C"
{
int luaopen_engine (lua_State *L);
}
int main()
{
GuiInst gui=new GuiInst;
lua_State *L=luaL_newstate();
luaopen_engine(L); //this is swigs module
int error=luaL_loadfile(L,"mainmenu.lua")||
lua_pcall(L, 0, 0, 0);
lua_getglobal(L,"Init");
//Somehow push gui onto lua stack...
lua_pcall(L, 1, 0, 0));
lua_close(L);
}
mainmenu.lua
function Init(gui)
vregion=gui:CreateComponent("GuiRegionVertical");
end
At the moment all I have found that can work is to expose some functionality from the swig generated cpp file, and call that. This is bad for a few reasons... It won't work if I have multiple modulles and I had to change the default linkage specification in the swig file (using -DSWIGRUNTIME=).
I add the following to main.cpp
extern "C"
{
struct swig_module_info;
struct swig_type_info;
int luaopen_engine (lua_State *L);
swig_module_info *SWIG_Lua_GetModule(lua_State* L);
void SWIG_Lua_NewPointerObj(lua_State* L,void* ptr,swig_type_info *type, int own);
swig_type_info *SWIG_TypeQueryModule(swig_module_info *start,swig_module_info *end,const char *name);
}
//and then to push the value...
SWIG_Lua_NewPointerObj(L,gui,SWIG_TypeQueryModule(SWIG_Lua_GetModule(L),SWIG_Lua_GetModule(L),"GuiInst *"),0);
That gets a pointer to the module, then a pointer to the type, then calls swigs function to register it. It was an unreasonable thing to have to dig into a file that's not supposed to be human readable (so it says at the top of the file) and is just MESSY! (but it does work!)
Surely theres a better way to accomplish what I'm trying to do.
PS from a high level pov what I want is to have lua not refcount the Gui components which are created by the Object Factory in GuiInst, in case I'm going about this wrong. This is my first time exposing functionality to a scripting language apart from some very simple (and non-swig) python modules, so I'm prepared to take advice.
Thanks for any advice!
Response to comment by RBerteig
GuiInst's contructor is #defined to private when swig runs to prevent lua constructing instances of it, so that won't work for me. What I was trying to prevent was the following (in lua):
r=engine.GuiRegionVertical()
r:Add(engine.GuiButton())
which would call "g=new GuiButton" then register it with the GuiRegionVertical (which needs to store a pointer for various reasons), then call "delete g", and the GuiRegionVertical is left with a dangling pointer to g.
I suspect what really needs to happen is that GuiRegionVertical::Add(GuiButton*) should increment the ref count of the GuiButton*, and then GuiRegionVertical's destructor should decrement the refcounts of all of its contents, though i'm not sure how this should be done with swig.
That would remove the need for the private constructors, the Gui Object Factory and the nasty externs.
Am I going about this Wrong?
Thanks.
Better late then never, and this solution will help other people.
void handle_web_request(WebRequest *request, WebResponse *response)
{
lua_getfield(rackam->lua_state, LUA_GLOBALSINDEX, "handle_web_request");
SWIG_Lua_NewPointerObj(rackam->lua_state, request, SWIGTYPE_p_WebRequest, 0);
SWIG_Lua_NewPointerObj(rackam->lua_state, response, SWIGTYPE_p_WebResponse, 0);
lua_call(rackam->lua_state, 2, 0);
}
this code must be inside %{}% blocks in your .i file, because SWIGTYPE_p_WebRequest is
#define SWIGTYPE_p_WebResponse swig_types[6]
and swig_types[6] is
static swig_type_info *swig_types[12];
which means that swig_types is only accessable from the C++ file from which it is defined.
this particular snippet is sending in two of my wrappered pointers, so calling handle_web_request(request, response) from the C++ side of things will run the global lua function "handle_web_request" and pass it my two pointers, with the SWIG magic applied.
There is a simple and direct answer, that may not be the most efficient answer. SWIG produces wrappers for manipulating objects from the scripting language side. For objects, it also synthesizes a wrapped constructor. So, the direct solution is to just let the Lua interpreter call SWIG's constructor to create the new object.
For the wrapped engine.GuiInst class, you almost certainly can do something like:
int main()
{
lua_State *L=lua_open();
luaopen_engine(L); //this is swigs module
int error=luaL_loadfile(L,"mainmenu.lua")||
lua_pcall(L, 0, 0, 0);
luaL_dostring(L, "Init(engine.new_GuiInst())");
lua_close(L);
}
For a one-shot case like script startup, the penalty of running a string constant through luaL_dostring() is not bad at all. I'd look harder to avoid it in an event callback or an inner loop, however.
It does seem like there ought to be a way to convert a pointer directly into a wrapped object, I'm not spotting it in my own handful of SWIG generated wrappers.
Edit: Of course, the Lua fragment can be decomposed to API calls that get the engine table global on the stack, extract from it the new_GuiInst member, call it, then call the global Init, but the little bit of efficiency comes at the cost of some clarity.
As for dealing with objects that shouldn't be constructed by accident in user code, as the clarified question indicates, my first impulse would be to let SWIG generate the constructor function, keep a private reference if needed later, and remove it from the table. Even a C module is (usually) just a table whose members contain function values. Being implemented in C doesn't make them read-only unless extra effort is taken.
So, you could always retrieve the value of engine.new_GuiInst and park it in the registry (see luaL_ref() and the discussion in section 3.5 of the Lua Reference Manual of the pseudo-index LUA_REGISTRYINDEX for the details) for later use. Then, before letting any user code run, simply do the equivalent of engine.new_GuiInst = nil. I should note that for the C data types I've been playing with most recently, SWIG created two constructors for each type, named new_TYPE and TYPE. Both were visible in the module's table, and you would want to set both names to nil. If have much less experience with SWIG wrapping C++ classes, and the result may differ...
You might want to check and review the whole content of the engine table returned by SWIG, and create a proxy object that contains only the methods you want available to your users. You can also change the environment seen by the user script so that it only has the proxy available, and names the proxy engine as well. There has been a fair amount of discussion of sandboxing user scripts on the Lua list and at the lua-users wiki.