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
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 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 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.
I am trying to use Lua for the configuration of a C++ application and am having trouble generating helpful messages when something is wrong in the configuration, not the the Lua syntax.
For example, suppose the following is a valid configuration:
foo = { a = 0, b = 'bar' }
but the user actually typed this:
foo = { a = 0, c = 'bar' }
Now, the app knows that foo can have fields a and b. It can load foo and get the value of a. It can even tell that b is not set and use a default. But I want to detect that c is present and report a warning.
Here is an extract of my attempt at that which blows up:
static void check_table(lua_State* L)
{
lua_pushnil(L);
while ( lua_next(L, -2) )
{
// key at -2 and value at -1
if ( lua_isstring(L, -2) )
{
const char* key = lua_tostring(L, -2);
// validate here; just printing key for now
cout << key << endl;
}
lua_pop(L, 1);
}
}
This works fine as long as the table is not actually an array. When I hit one of those, it dies on the second iteration with this:
...
1
PANIC: unprotected error in call to Lua API (invalid key to 'next')
which I attribute to this from the Lua reference page:
"If the value is a number, then lua_tolstring also changes the actual value in the
stack to a string. (This change confuses lua_next when lua_tolstring is applied to
keys during a table traversal.)"
Any way around this? I am open to alternate approaches. Ideally a message could be emitted like:
WARNING: conf.lua line 18: table foo does not use key 'c', ignored
(The Lua debug API doesn't give the file name and line number either, but that is a different topic.)
PS: I know, c could benign, but it could also be a typo. In a large configuration, ignoring such things could lead to hours of head scratching.
Validation will probably be much easier if written in Lua. I have something like this in mind:
local template = { a="number", b="string"}
local function validate(t)
for k,v in pairs(t) do
if template[k]==nil then
print("field "..k.." cannot be present")
elseif type(v)~=template[k] then
print("field "..k.." should be a "..template[k])
end
end
end
validate{ a = 0, b = 'bar' }
validate{ a = 0, b = 42 }
validate{ a = 0 }
validate{ a = 0, c = 'bar' }
lua_isstring is defined:
LUA_API int lua_isstring (lua_State *L, int idx) {
int t = lua_type(L, idx);
return (t == LUA_TSTRING || t == LUA_TNUMBER);
}
So instead of:
if ( lua_isstring(L, -2) )
use:
if ( lua_type(L, -2) == LUA_TSTRING )
In Lua Code
Test = {}
function Test:new()
local obj = {}
setmetatable(obj, self)
self.__index = self
return obj
end
local a = Test:new()
a.ID = "abc123"
callCfunc(a)
In C Code
int callCfunc(lua_State * l)
{
void* obj = lua_topointer(l, 1); //I hope get lua's a variable
lua_pushlightuserdata(l, obj);
lua_getfield(l, 1, "ID");
std::string id = lua_tostring(l, 1); //I hoe get the value "abc123"
...
return 0;
}
But My C result is
id = null
Why? How to modify code to work fine ?
PS: I don't hope create C Test Class mapping to lua
==== update1 ====
In addition, I have added the test code to confirm correct incoming parameters.
int callCfunc(lua_State * l)
{
std::string typeName = lua_typename(l, lua_type(l, 1)); // the typeName=="table"
void* obj = lua_topointer(l, 1); //I hope get lua's a variable
lua_pushlightuserdata(l, obj);
lua_getfield(l, 1, "ID");
std::string id = lua_tostring(l, 1); //I hoe get the value "abc123"
...
return 0;
}
the result
typeName == "table"
so incoming parameter type is Correct
I found the reason
Correct c code should is ...
In C Code
int callCfunc(lua_State * l)
{
lua_getfield(l, 1, "ID");
std::string id = lua_tostring(l, -1); //-1
...
return 0;
}
Maybe this - haven't tested sorry - don't have a compiler handy
Input is the table from lua on top of the stack, so getfield(l,1, "ID") should get the field ID from the table at the top of the stack - which in this case is your input table. It then pushes the result to the top of the stack
int callCfunc(lua_State * l)
{
lua_getfield(l, 1, "ID");
std::string id = lua_tostring(l, 1); //I hoe get the value "abc123"
...
return 0;
}