I am trying to get values from a Lua table. This is what I have written in Program.cpp:
lua_State* lua = luaL_newstate();
luaL_openlibs(lua);
luaL_dofile(program->getLuaState(), "Script.lua");
lua_getglobal(lua, "table");
lua_pushstring(lua, "x");
lua_gettable(lua, -2);
printf("%i", lua_tonumber(lua, -1));
And I wrote this in Script.lua:
table = {x = 12, y = 32}
The problem is that this only writes 0 in the console. I have checked that the lua file is loading correctly. What am I doing wrong?
Change %i to %g. lua_tonumber returns a float or double, not an int.
Related
I learned that package.preload can be used to expose a script to other scripts.
Here's my example code.
lua_State *L = luaL_newstate();
luaL_openlibs(L);
lua_settop(L, 0);
//Script A
luaL_dostring(L, "local A = {} package.preload['A'] = function () return A end A.num = 3");
//Script B
luaL_dostring(L, "local A = require 'A' print(A.num)");
lua_close(L);
The result: 3
Although this works fine, I wonder if Script A's code can be more simplified or if there's other alternative solution to expose a script to other scripts.
ADDED: The main reason I'm asking this is because I think package.preload['A'] = function () return A end is quite long and boring to write.
In this case, where you have some set of in-C strings that represent Lua modules, package.preload is exactly the tool to use. Though your specific use of it leaves something to be desired.
Generally speaking, the modules themselves do not define their names. So hard-coding the module's name into the string is not the correct move. Similarly, modules do not register themselves; they should be registered by the environment around the module.
What you really want is to take an array of name+Lua code strings and register them as module preloads in a loop. So you'd have something like this. I'll be using Lua 5.3; you can translate it to older version of Lua pretty easily.
Also, be warned: this code is untested.
const char *lua_preloads[] =
{
"A", "local A = {}\n"
"A.num = 3\n"
"return A)\n", //Modules are usually tables, not functions.
...
NULL //Null-terminated list.
};
//Loader function
int lua_preloader_func(lua_State *L)
{
int nargs = lua_gettop(L);
int lua_func_ix = lua_upvalueindex(1);
lua_pushvalue(L, lua_func_ix);
//Move the function to the bottom of the stack
lua_insert(lua_func_ix, 1);
//Call with all of the given arguments.
lua_call(L, nargs, LUA_MULTRET);
return lua_gettop(L);
}
int top = lua_gettop(L);
//Get the package.preload table.
lua_getglobal(L, "package");
lua_getfield(L, -1, "preload");
int preload_ix = lua_gettop();
for(const char **position = lua_preloads;
*position;
position += 2)
{
const char *module_name = position[0];
const char *module = position[1];
//Compile the preload script into a Lua function.
int err = luaL_loadbufferx(L, module, strlen(module), module_name, "t");
//Check for errors in `err`.
//Create a Lua C-function with the script as an upvalue.
lua_pushcclosure(L, lua_preloader_func, 1);
//Stick that Lua C-function inside of package.preload[preload.first].
lua_setfield(L, preload_ix, module_name);
}
lua_settop(L, top);
It seems as if you want to prefix local A = {} package.preload['A'] = function () return A end to every chunk defining a module (where A is the module name). I think it would be much easier to just use string concatenation for that.
#include <string>
#include <lua.hpp>
int preload(lua_State *L, std::string const &modname,
std::string const &modcode) {
std::string code = "package.preload['" + modname + "'] = function()\n" +
"local " + modname + " = {}\n" + modcode + "\n"
"return " + modname + " end";
return luaL_dostring(L, code.c_str());
}
int main() {
lua_State *L = luaL_newstate();
luaL_openlibs(L);
// Script A
preload(L, "A", "A.num = 3");
// Script B
luaL_dostring(L, "local A = require 'A' print(A.num)");
lua_close(L);
}
I have a c function, used to return a table to lua, but the table is created from a string, not lua_newtable, how to do this?
int GetTable(lua_State* L)
{
//this string is generated at runtime, so i can not use lua_newtable
const char* TableFromStr = "{a = 123, b = 456, d = {x = 1, y = 9} }";
//i want to push the table to the top stack
luaL_loadstring(L, TableFromStr);
//return 1 table, lua code can get the table
return 1;
}
You have to use luaL_dostring
https://www.lua.org/manual/5.1/manual.html#luaL_dostring
If you're using Lua 5.1 make sure luaL_dostring is correctly defined. See: http://lua-users.org/lists/lua-l/2006-04/msg00218.html
In Lua 5.1, luaL_dostring is defined as luaL_loadstring(L, s) || lua_pcall(L, 0, 0, 0)
and so it ignores returns.
Try this:
#undef luaL_dostring
#define luaL_dostring(L,s) \ (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
It's possible that you will also have to prepend your string with return
I have the following simple code in C++ where Object is a std container:
static int create_an_object(lua_State* L) {
auto obj = static_cast<Object*>(lua_newuserdata(L, sizeof(Object*)));
*obj = another_valid_obj;
luaL_newmetatable(L, "object_metatable");
lua_pushcfunction(L, object_metatable_function);
lua_setfield(L, -2, "__index");
lua_pop(L, 1);
return 1;
}
static int object_metatable_function(lua_State* L) {
string index = luaL_checkstring(L, -1);
if (index == "foo") {
lua_pushnumber(L, 123);
}
// Handles other indices, or throws error.
}
lua_pushcfunction(L, create_an_object);
lua_setglobal(L, "create_an_object");
With the FFI above, I can achieve indexing of Object in Lua such as:
local obj = create_an_object()
print(obj.foo) -- 123
Meanwhile print(obj) shows that obj is userdata: 0x12345678.
Is it possible to use some metamethod magic so that
obj could be used as a table, while print(obj.foo) still prints 123? I am running my code in Lua 5.1.
I'm not exactly sure what you mean by "could be used as a table", but if you want to print something different from the default from print(obj), then you'll need to assign __tostring metamethod and return some string from it. This string may look like "userdata: 0x12345678 = {foo = 123}" if you want (or simply "{foo = 123}").
If you mean making it work as a table when assigning a new index to it, then __newindex metamethod should be used.
I have a global table in Lua I am trying to access from C++.
Here is essentially what I am trying to do:
Lua:
myTable = {}
myTable[1] = 1
C++:
lua_State* L = luaL_newstate();
luaL_openlibs(L);
lua_pcall(L, 0, 0, 0);
lua_State* L2 = luaL_newstate();
luaL_dofile(L, "luaScript.lua");
LuaRef myTable= getGlobal(L, "myTable");
cout << myTable[0];
I get an error on my cout, saying:
Error C2593 'operator <<' is ambiguous ConsoleApplication2" & "more than one operator "<<" matches these operands:
However I do not think these errors are the issue.
How can I access this value?
You have to explicitely convert your myTable[] to something << can handle.
And your Lua array starts at 1, but you access [0].
im trying to call a lua function from C++ where function is in subtable of global table. Im using lua version 5.2.* compiled from source.
Lua function
function globaltable.subtable.hello()
-- do stuff here
end
C++ code
lua_getglobal(L, "globaltable");
lua_getfield(L, -1, "subtable");
lua_getfield(L, -1, "hello");
if(!lua_isfunction(L,-1)) return;
lua_pushnumber(L, x);
lua_pushnumber(L, y);
lua_call(L, 2, 0);
However im unable to call it, i always get an error
PANIC: unprotected error in call to Lua API (attempt to index a nil value)
On line #3: lua_getfield(L, -1, "hello");
What am I missing?
Side question: I would love to know also how to call function deeper than this - like globaltable.subtable.subsubtable.hello() etc.
Thank you!
This is what im using to create the globaltable:
int lib_id;
lua_createtable(L, 0, 0);
lib_id = lua_gettop(L);
luaL_newmetatable(L, "globaltable");
lua_setmetatable(L, lib_id);
lua_setglobal(L, "globaltable");
how do i create the globaltable.subtable?
function is a keyword in Lua, I am guessing on how did you manage to compile the code:
-- test.lua
globaltable = { subtable = {} }
function globaltable.subtable.function()
end
When this is run:
$ lua test.lua
lua: test.lua:2: '<name>' expected near 'function'
Maybe you changed the identifiers for this online presentation, but check that on line 2 "subtable" really exists in the globaltable, because on line 3, the top of stack is already nil.
Update:
To create multiple levels of tables, you can use this approach:
lua_createtable(L,0,0); // the globaltable
lua_createtable(L,0,0); // the subtable
lua_pushcfunction(L, somefunction);
lua_setfield(L, -2, "somefunction"); // set subtable.somefunction
lua_setfield(L, -2, "subtable"); // set globaltable.subtable
lua_newtable(L);
luaL_newmetatable(L, "globaltable");
lua_newtable(L); //Create table
lua_setfield(L, -2, "subtable"); //Set table as field of "globaltable"
lua_setglobal(L, "globaltable");
This is what i was looking for.