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].
Related
I'm trying to use function as metatable __index to achieve more flexibiilty, but it crashed when the returned value is not assigned to some variable in Lua side.
This is the C++ part, it just creates a table variable whose __index is pointing to a C function, the function just returns a fixed number value:
int meta_index( lua_State* lua )
{
juce::Logger::writeToLog( "meta_index with key " + juce::String( lua_tostring( lua, 2 ) ) );
lua_pushnumber( lua, 123.456 );
return 1;
}
int main()
{
lua_State* lua = luaL_newstate();
luaL_openlibs( lua );
// create a table with __index slot
lua_createtable( lua, 0, 0 );
lua_pushstring( lua, "__index" );
lua_pushcfunction( lua, meta_index );
lua_settable( lua, -3 );
lua_setglobal( lua, "test_meta" );
// run lua code
luaL_loadstring( lua, lua_src );
lua_call( lua, 0, 0 );
lua_close( lua );
}
And this is the Lua code that causes crash:
const char* lua_src =
"a = {}\n"
"setmetatable(a, test_meta)\n"
"a.foo\n";
The code crashed before C function meta_index is actually called, and it claims a error message attempt to call a string value.
However, if I put the expression a.foo who triggers meta indexing on the right of assignment, the code finishes with no error and produces expected result:
const char* lua_src =
"a = {}\n"
"setmetatable(a, test_meta)\n"
"bar = a.foo\n"
"print(bar)\n";
It seems Lua treats the foo in a.foo and bar = a.foo as different things, what is the actual rule for it?
The code below does not compile because expressions like a.foo are not statements in Lua:
a = {}
setmetatable(a, test_meta)
a.foo
The error message is
4: syntax error near <eof>
This string is left on the top of the stack after luaL_loadstring. Hence the error attempt to call a string value when you call lua_call.
Bottom line: always check the return code of luaL_loadstring and print any error messages.
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.
Hey,everyone! I've a C++ app embedded Lua as script. A non-programmer edits the Lua script, then the C++ app invoke the Lua script and the Lua script also invokes C++ registered function.
I use Luaplus to do the above job. My question is: when the script-editor makes mistakes such as misspelling the the parameter, the C++ app crashes! What can I do to prevent this happening? thanks
Look at lua_cpcall and lua_pcall. They both allow protected function calls of lua in c. If they return a non-negative number then the call failed and the lua stack contains only the error string. In cpcalls case the stack is otherwise unmodified. For pcall you'll need to look at lua_pushcclosure to invoke a cfunction safely.
What you do is this: you create a c function with all of the lua_* calls you want, such as loadfile and dofile. You call this function using lua_cpcall or lua_pushcclosure amd lua_pcall. This allows you to detect if an error occured in t
he function you passed to cpcall.
Examples:
function hello() {
string hello_ = "Hello Lua!";
struct C {
static int call(lua_State* L) {
C *p = static_cast<C*>(lua_touserdata(L,-1));
lua_pushstring(L, p->str.c_str() );
lua_getglobal(L, "print");
lua_call(L, 1, 0); //ok
lua_pushstring(L, p->str.c_str() );
lua_getglobal(L, "notprint");
lua_call(L, 1, 0); //error -> longjmps
return 0; //Number of values on stack to 'return' to lua
}
const string& str;
} p = { hello_ };
//protected call of C::call() above
//with &p as 1st/only element on Lua stack
//any errors encountered will trigger a longjmp out of lua and
//return a non-0 error code and a string on the stack
//A return of 0 indicates success and the stack is unmodified
//to invoke LUA functions safely use the lua_pcall function
int res = lua_cpcall(L, &C::call, &p);
if( res ) {
string err = lua_tostring(L, -1);
lua_pop(L, 1);
//Error hanlder here
}
//load a .lua file
if( (res=luaL_loadfile(L, "myLuaFile.lua")) ) {
string err = lua_tostring(L, -1);
lua_pop(L, 1);
//res is one of
//LUA_ERRSYNTAX - Lua syntax error
//LUA_ERRMEM - Out of memory error
//LUE_ERRFILE - File not found/accessible error
}
//execute it
if( (res=lua_pcall(L,0,0,0)) ) {
string err = lua_tostring(L, -1);
lua_pop(L, 1);
// res is one of
// LUA_ERRRUN: a runtime error.
// LUA_ERRMEM: memory allocation error.
// LUA_ERRERR: error while running the error handler function (NULL in this case).
}
// try to call [a_int,b_str] = Foo(1,2,"3")
lua_getglobal(L,"Foo");
if( lua_isfunction(L,lua_gettop(L)) ) { //Foo exists
lua_pushnumber(L,1);
lua_pushnumber(L,2);
lua_pushstring(L,"3");
lua_pushvalue(L, -4); //copy of foo()
if( (res = lua_pcall(L, 3, 2, 0/*default error func*/)) ) {
string err = lua_tostring(L, -1);
lua_pop(L, 1);
//error: see above
}
int a_int = (int)lua_tointeger(L,-2);
string b_str = lua_tostring(L,-1);
lua_pop(L,2+1); //2 returns, + extra copy of Foo()
}
}