I know the basics of interacting with lua and C, and I am currently trying to perform the following line of lua in c++
Func1():Func2().Table1.value1
I am trying to get the value of "value2" and use it in my C program. The following is the code I wrote to attempt to get this value in C.
int GetNumber()
{
int retn = 0;
g_clientlua.lua_getfield(LUA_REGISTRYINDEX, "Player");
g_clientlua.lua_getfield(-1, "Func2");
g_clientlua.lua_getfield(LUA_GLOBALSINDEX, "Func1");
g_clientlua.lua_call(0, 1);
g_clientlua.lua_call(1, 1);
if (g_clientlua.lua_isnil(-1))
return retn;
g_clientlua.lua_getfield(-1, "Table1");
if (g_clientlua.lua_isnil(-1))
return retn;
g_clientlua.lua_getfield(-1, "value1");
if (g_clientlua.lua_isnil(-1))
return retn;
retn = (int)g_clientlua.lua_tointeger(-1);
}
The clientlua thing is an object that basically just allows me to call a method which calls it's lua_* function equivalent and fills the lua_state pointer parameter with a member variable that is a pointer to the lua state.
Every time I call this, it complains about me causing a lua stack leak. To solve this, I tried adding a lua_pop(3) to the end, but then it just crashes my program without reporting an error, so I assume I am doing something wrong.
Anyone have any words of wisdom for me? Kinda lost here. I doubt the above code is even written properly, how would I write the above lua call in C?
You need to call Func1 before you try to get Func2 as Func2 comes from the table that Func1 returns (and not from the global table).
Then you need to call Func2 and look up Table1 in that returned value, etc.
What "stack leak" complaint are you getting? If you are calling this function from C directly then yes, you need to be sure that anything you put on the lua stack (that isn't for consumption by the caller, etc.) is popped from the lua stack before you return.
The GetNumber function isn't doing exactly the same as the lua snippet you're going for. Specifically GetNumber is getting the value of "Func2" from the registry while your lua snippet is getting the value of "Func2" from the table returned by Func1(). Unless you're certain that registry.Player.Func2 == Func1().Func2 is always true, your C++ version will not have the same behavior.
Let's break down Func1():Func2().Table1.value1 into more explicit steps to help with the C translation:
Get function associated with _G.Func1
Call that function and get a table back
Get function associated with "Func2" from the returned table in step 2
Call that function and pass as argument the table from step 2. Get another table back as result
I found it helpful to track what the stack contains as a side-comment as the operations are performed:
int GetNumber()
{
// Func1()
gclientlua.lua_getfield(LUA_GLOBALSINDEX, "Func1"); // Func1
g_clientlua.lua_call(0, 1); // {}
// Func2( {} )
g_clientlua.lua_getfield(-1, "Func2"); // {}, Func2
g_clientlua.lua_insert(-2); // Func2, {}
g_clientlua.lua_call(1, 1); // {}
if( g_clientlua.lua_type(-1) != LUA_TTABLE )
{
g_clientlua.lua_pop(1);
return 0;
}
// {}.Table1
g_clientlua.lua_getfield(-1, "Table1"); // {}, {}(Table1)
if( g_clientlua.lua_type(-1) != LUA_TTABLE )
{
g_clientlua.lua_pop(2);
return 0;
}
// tonumber( Table1.value1 )
g_clientlua.lua_getfield(-1, "value1"); // {}, {}(Table1), value1
int retn = g_clientlua.lua_tointeger(-1);
g_clientlua.lua_pop(3);
return retn;
}
Notice that GetNumber pops off all the arguments it places on the stack before returning. This ensures that GetNumber leaves the lua stack the way it was found. This can probably be automated with RAII if you're using C++.
Related
I have a LUA callback in C/++ and I'm trying to use lua_gettop() to detect an optional first parameter. The problem is that lua_gettop(L) == 1 is always true, even when I don't provide any parameters in LUA.
Here's the basics of my callback function in C/++:
int LuaFile::l_cpp_my_callback(lua_State *L) {
LuaFilePtr my_this = lua_map_[L];
if ( lua_gettop(L) == 0 ) {
// I never get here
} else if(lua_gettop(L) == 1) {
// Always gets here
} else {
return 1;
}
return 1;
}
This is the context of the call in the LUA code:
function my_function()
if ... then
if not ... then
if ... then
someOtherCallback(param, param, param)
someOtherCallback(param)
end
if ... then
someOtherCallback(param)
end
end
else
if not ... then
if ... then
someOtherCallback(param, param, param)
someOtherCallback(param)
end
if ... then
someOtherCallback(param)
end
end
var = my_callback()
...
end
end
I used lua_gettype(L, 1) to debug and it returns '5', which lua_typename(L, 5) says is a 'table'. I'm guessing this is a metatable of the stack?
I understand that 0 isn't a valid index in the LUA stack but then what's the proper way to determine optional first parameters?
As #nate mentioned in his comment, any "object-based" function call, that is, calling a member function--or method, if you prefer--results in the object being the first thing on the Lua stack. lua_gettop would return 1, assuming nothing else was passed to the member function as an argument.
As for checking an optional first argument to the member function, which would be located at index 2 on the Lua stack if you were using an object-based call, functions like luaL_optstring are very helpful. (Reference page here. In case you are unaware, functions prefixed with luaL are located in lauxlib.h.) There are also equivalent functions for other types. It allows one to provide an index and a default argument: if a value exists at the given index on the Lua stack, it is returned; otherwise, the default value supplied to the function is returned.
I've been to the end of Google and back trying to solve this problem.
I have a few userdata objects that I push from C++ to Lua.
I have a function that should get the X value of either a 2D or 3D object.
When I try to get the userdata object, taking into consideration that it could be either a 2D element or 3D object, I need to be able to get the X for whichever the user chooses.
Here is what I tried:
int getX(lua_State* L)
{
Object3D* a = static_cast<Object3D*>(luaL_checkudata(L, 1, "Object3D"));
if (!a)
{
Object2D* b = static_cast<Object2D*>(luaL_checkudata(L, 1, "Object2D"));
if (b)
{
lua_pushnumber(L, b:getX());
}
else
{
lua_pushnil(L);
}
}
else
{
lua_pushnumber(L, a:getX());
}
return 1;
}
Unfortunately if the userdata type is not Object3D, it fails and exits on an lua error without continuing to try Object2D.
Therefore, it will only work in the above code if the object being passed is of type Object3D.
luaL_testudata
void *luaL_testudata (lua_State *L, int arg, const char *tname);
This function works like luaL_checkudata, except that, when the test fails, it returns NULL instead of raising an error.
The lua(L)_check* functions throw Lua errors on failure, the lua(L)_to* functions return NULL. For whatever reason, this one deviates from the naming convention and is named lua(L)_test* instead, which makes it a bit harder to find.
Your code is incomplete and doesn't compile as-is so I can't be bothered to check, but if I'm not mistaken, just replacing luaL_checkudata with luaL_testudata should make it work as intended.
Solved by using rawequal to see which class the registry matches.
I want to link a Lua table function to a C++ variable using LUAPlus. So far using what information I can find on the web, I've managed to call a table function using DoString("tableName:functionName"), but I want to be able to call the function through a variable. I know its possible but I cant figure out what I'm doing wrong.
In the code I've added, pLuaState->DoString("table1:prints()") works fine and prints everything as needed.
When I try to get the function and call it using the func variable, my program crashes with
error message
Assertion failed: 0, file .../luafunciton.h, line 41
If I remove all the prints in the prints() function in Lua except "print("in prints")", everything works fine. Based on this I assume that GetByName("functionName") returns a function that doesn't contain a reference to its parent table. And this is where I get stuck.
main.cpp
pLuaState = LuaPlus::LuaState::Create(true);
pLuaState->DoFile("test.lua");
LuaObject globals = pLuaState->GetGlobals();
LuaObject metaTableObj = pLuaState->GetGlobals().Lookup("RandomMove");
if (metaTableObj.IsNil())
{
metaTableObj = pLuaState->GetGlobals().CreateTable("RandomMove");
}
metaTableObj.SetObject("__index", metaTableObj);
metaTableObj.RegisterObjectDirect("GetX", (TestLua*)0, &TestLua::GetX);
metaTableObj.RegisterObjectDirect("SetX", (TestLua*)0, &TestLua::SetX);
TestLua obj1(6);
LuaObject table1Obj = pLuaState->GetGlobals().CreateTable("table1");
table1Obj.SetLightUserData("__object", &obj1);
table1Obj.SetObject("__index", metaTableObj);
table1Obj.SetMetaTable(metaTableObj);
pLuaState->DoString("table1:prints()");
auto prints = table1Obj.GetByName("prints");
if (!prints.IsFunction())
cout << "nil function" << endl;
else
{
LuaPlus::LuaFunction<void> func(prints);
func();
}
test.lua
print("test.lua")
RandomMove =
{
_thing = 1
}
function RandomMove:SetThing()
print("I am "..tostring(self.__object))
end
function RandomMove:prints()
print("in prints")
print("__object is: " .. tostring(self.__object))
print("_thing is: ".. tostring(self._thing))
print(self:GetX())
end
Any help would be appreciated.
You are correct. Functions do not know anything about their "parents" or their "objects" or anything. That's why the : calling syntax exists. The call table1:prints() is identical to table1.prints(table1) (only lua ensures that table1 is evaluated only once.
So if you grab the function from the table/etc. directly when you call it directly you need to ensure that you pass the correct table/etc. as the first argument (before any other arguments the function expects).
I'm implementing Lua as a script language into a Windows application. Due to the application's structure, printout isn't using streamed io, such as stdout and stderror.
I have managed to override the Lua print to fit into my structure...
lua_register(L,"print", cs_print);
...but how do I override all error an debug printouts without using streams? I need to handle this in a function (similar to print).
The only place where Lua writes to stderr is in the panic function that luaL_newstate installs. If you're embedding Lua into your application, make sure you start Lua from a protected call and no panic will ever occur. See http://www.lua.org/source/5.2/lua.c.html#main for ideas.
After a lot of Google I came up with this solution to fetch compiler and runtime error messages as well to redirect the standar Lua print function.
I use C++ Builder but I think it can be useful for anyone looking for the same answer.
The script is running in a TScriptLua object instance and to map a Lua state against the correct script instance, I use a std::map list to look up the object pointer.
// list for mapping Lua state with object pointers.
static std::map<lua_State*,TScriptLua*> LuaObjMap;
Next is an inline function to get the object pointer from a Lua state pointer.
extern "C" {
// Inline to map Lua state pointer to object pointer
static inline TScriptLua* GetScriptObject(lua_State* L) {
return LuaObjMap.find(L)->second;
}
This function will replace the standard Lua print function. The object pointer (f) has a member function in its base class called Out() that will output a char buffer in the associated window control.
// New Lua print function
static int cs_print (lua_State *L) {
// Map Lua state to object
TScriptLua* f = GetScriptObject(L);
if (f) {
int count = lua_gettop(L);
for (int i=1; i <= count; ++i) {
const char *str = lua_tostring(L, i); // Get string
size_t len = lua_rawlen(L,i); // Get string length
// Output string.
f->Out(str,len);
}
}
return 0;
}
This is my error print routine that will display the compiler/runtime error. As for the Lua print function, the f->Out is used again to print the error message.
// Error print routine
static int cs_error(lua_State *L, char *msg) {
// Map Lua state to object
TScriptLua* f = GetScriptObject(L);
// Get error message
AnsiString m = String(msg) + " " + String(lua_tostring(L, -1));
// "print" error message
f->Out(m.c_str(),m.Length());
// Clenaup Lua stack
lua_pop(L, 1);
return 0;
}
} // <--- End extern C
Here is the actual load and run member. After the new Lua state has been created it is associated with this object in the mapping list. Then it loads the script from a Rich edit control with luaL_loadbuffer(), checks for compiler errors and runs the compiled script with lua_pcall().
void __fastcall TScriptLua::Compile(void) {
// Create new Lua state
lua_State *L = luaL_newstate();
// Store mapping Lua state --> object
LuaObjMap.insert( std::pair<lua_State*,TScriptLua*>(L,this) );
// Override Lua Print
lua_register(L,"print", cs_print);
// Load and compile script
AnsiString script(Frame->Script_RichEdit->Text);
if (luaL_loadbuffer(L,script.c_str(),script.Length(),AnsiString(Name).c_str()) == 0) {
if (lua_pcall(L, 0, 0, 0)) // Run loaded Lua script
cs_error(L, "Runtime error: "); // Print runtime error
} else {
cs_error(L, "Compiler error: "); // Print compiler error
}
// Close Lua state
lua_close(L);
// Remove Lua --> object mapping
LuaObjMap.erase( LuaObjMap.find(L) );
}
This isn't my final solution, but it does the trick so far. I think the best thing is to write stream handling into the TScriptLua object so I can register it directly into Lua.
I'm attempting to call a function inside of a lua file called test2.lua
This is the contents of test2.lua:
function abc(path)
t = {}
table.insert(t, "a")
return t
end
As you can see it takes a single input and returns a string.
Here is my C code. It's pretty simple. However my call getglobal in order to call that function does not work... lua_getglobal says it isn't a function when I test it... Any reason why this is? Shouldn't abc be a global function returnable inside of the source file? Why then does it only find nil for this global?
L = lua_open();
luaL_openlibs(L);
luaL_loadfile(L, "src/test2.lua");
lua_getglobal(L, "abc");
lua_pushstring(L, "coollll");
int error = 0;
if ((error = lua_pcall(L, 1, 1, 0)) == 0)
{
std::cout << "cool";
}
EDIT:
calling lua_getglobal is causing my program to break control regardless of using loadfile or dofile... any idea why?
lua_getglobal crashing program
The function luaL_loadfile() reads, parses, and compiles the named Lua file. It does not execute any of its content. This is important in your case because the statement function abc(path)...end has no visible effect until it is executed. The function keyword as you've used it is equivalent to writing
abc = function(path)
t = {}
table.insert(t, "a")
return t
end
In this form, it is clearer that the variable named abc is not actually assigned a value until the code executes.
When luaL_loadfile() returns, it has pushed an anonymous function on the top of the Lua stack that is the result of compiling your file. You need to call it, and lua_pcall() will do the trick. Replace your reference to luaL_loadfile() with this:
if (luaL_loadfile(L, "src/test2.lua") || lua_pcall(L, 0, LUA_MULTRET, 0)) {
// do something with the error
}
At this point, test2.lua has been executed and any functions it defined or other global variables it modified are available.
This is a common enough idiom, that the function luaL_dofile() is provided to load and call a file by name.
There is a second, more subtle issue in your code as presented. The function abc() uses a variable named t, but you should be aware that t as used is a global variable. You probably meant to write local t = {} at the top of abc().
It's not enough to call luaL_loadfile: this puts a chunk onto the stack. Either follow up with luaL_[p]call to execute the chunk (thus making the function available), or use luaL_dofile.