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;
}
Related
I have a script to C++ callback/functor system that can call any "registered" C++ function using strings and/or variants.
//REMOVED ERROR CHECKS AND ERRONEOUS STUFF FOR THIS POST
int LuaGameObject::LuaCallFunction( lua_State *luaState )
{
if ( lua_isuserdata( luaState, 1 ) == 1 )
{
int nArgs = lua_gettop( luaState );
//Get GameObject
OGameObject* pGameObject = static_cast<OGameObject*>(lua_touserdata( luaState, 1 ));
if ( pGameObject )
{
//Get FunctionName
const char* functionNameString = lua_tostring( luaState, 2 );
//Get Args
std::vector<OVariant> args;
for ( int i = 3; i <= nArgs; ++i )
{
OVariant variant;
variant.SetFromLua( luaState, i );
args.push_back( variant );
}
//Call it!
CallGameObjectFunction( luaState, pGameObject, functionNameString, args );
return 1;
}
}
return 0;
}
OVariant LuaGameObject::ExecuteLua()
{
lua_State *lState = luaL_newstate();
luaL_openlibs( lState );
lua_register( lState, "Call", LuaCallFunction );
luaL_loadstring( lState, m_pScript );
//now run it
lua_pcall( lState, 0, 1, 0 );
//process return values
OVariant result;
result.SetFromLua( lState, -1 );
lua_close( lState );
return result;
}
In lua I can do something like this...
local king = Call("EmpireManager","GetKing")
Call("MapCamera","ZoomToActor",king)
However, I am feeling that I can use the __index metamethod to simplify the lua...
local king = EmpireManager:GetKing()
MapCamera:ZoomToActor(king)
I was hoping to achieve the simplified lua by using the following implemenation of the __index metamethod
Here is how I register the __index metafunction... (mostly copied from online examples)
void LuaGameObject::Register( lua_State * l )
{
luaL_Reg sRegs[] =
{
{ "__index", &LuaGameObject::LuaCallFunction },
{ NULL, NULL }
};
luaL_newmetatable( l, "luaL_EmpireManager" );
// Register the C functions into the metatable we just created.
luaL_setfuncs( l, sRegs, 0 );
lua_pushvalue( l, -1 );
// Set the "__index" field of the metatable to point to itself
// This pops the stack
lua_setfield( l, -1, "__index" );
// Now we use setglobal to officially expose the luaL_EmpireManager metatable
// to Lua. And we use the name "EmpireManager".
lua_setglobal( l, "EmpireManager" );
}
Unfortunately, I cant seem to get the callback setup right. Lua correctly calls my LuaGameObject::LuaCallFunction, but the stack does not contain what I would like. From within the LuaGameObject::LuaCallFunction, I can find the function name and EmpireManager object on the stack. But, I cant find the args on the stack. What is the proper way to set this up? Or is it not possible?
It is definitely possible to add methods to a userdata type in Lua, as explained in the Programming in Lua guide from the official website.
When you type the following Lua code:
myUserdata:someMethod(arg1,arg2,arg3)
Assuming myUserdata is a "userdata" object, the interpreter will do the following.
Call getmetatable(myUserdata).__index(myUserdata,"someMethod") to get the value of someMethod.
Call someMethod(myUserdata,arg1,arg2,arg3). someMethod can be anything callable from Lua. Examples: a Lua or C function, or a table/userdata with a __call metamethod.
Your __index metamethod should just return a function (or another object callable from Lua) implementing the method. Something like this:
// IMO, quite a misleading name for the __index metamethod (there is a __call metamethod)
int LuaGameObject::LuaCallFunction( lua_State *l)
{
// todo: error checking
OGameObject* pGameObject = static_cast<OGameObject*>(lua_touserdata( luaState, 1 ));
std::string memberName = lua_tostring( luaState, 2 );
int result = 1;
if (memberName == "method1") {
lua_pushcfunction(l,LuaGameObject::luaMethod1);
} else if (memberName == "method2") {
lua_pushcfunction(l,LuaGameObject::luaMethod2);
} else {
result = 0;
}
return result;
}
Basic skeleton of the functions returned by the __index metamethod:
int LuaGameObject::luaMethod1(lua_State* l) {
// todo: error checking.
OGameObject* pGameObject = static_cast<OGameObject*>(lua_touserdata(l, 1));
float arg1 = lua_tonumber(l, 2);
// get other args
pGameObject->method1(arg1 /*, more args if any.*/);
// optionally push return values on the stack.
return 0; // <-- number of return values.
}
Ok so after more research, I now believe that I cannot use __index metafunction to call a c functor with arguments. It only passes the table name and the key to the callback.
However, for anyone interested, it can be used for table-like objects, but not functions (as arguments are not pushed onto the stack). I will it for my "property" objects. They have no arguments and can be used in lua as follows...
local king = EmpireManager:king
king:name = "Arthur"
local name = king:name
These properly link to and call the appropriate C++ objects.functions
Actor::SetName(std::string name)
std::string Actor::GetName()
I had the same problem to call a method from my object and have used this post to develop the solution.
I hope that the example below can be useful to you.
#include <iostream>
#include <string>
#include <map>
#include <functional>
extern "C" {
#include "lua/lua.h"
#include "lua/lauxlib.h"
#include "lua/lualib.h"
}
//template<class UserdataType> // if will be work with lua garbage collector, use a function like that to delete the this_ptr (1st param)
//int DeletePtr(lua_State *lua_state) { // It's necessary register the metatable.__gc and to trust in gc (create just pointer of LuaObjects
// UserdataType** this_ptr = reinterpret_cast<UserdataType**>(lua_touserdata(lua_state, 1));
// delete (*this_ptr);
// return 0;
//}
template<class UserdataType>
int Closure(lua_State *lua_state) {
UserdataType** ptr = reinterpret_cast<UserdataType**>(lua_touserdata(lua_state, 1)); // This closure is being called by call operator ()
return (*ptr)->CallFunction(lua_state); // To access the function name called use lua stack index with lua_upvalueindex(-1)
} // Call the object method to resolve this called there
template<class UserdataType>
int ReturnClosure(lua_State *lua_state) { // This function is called as a lookup of metatable.__index
lua_pushcclosure(lua_state, Closure<UserdataType>, 1); // then we will return a closure to be called through call operator ()
return 1; // The 1st param (the only one) is the action name of function
} // Then a closure will grant access to ReturnClosure params as upvalues (lua_upvalueindex)
class LuaObject {
public:
LuaObject() : userdata_name("userdata1") {
}
void CreateNewUserData(lua_State* lua_ptr, const std::string& global_name) {
RegisterUserData(lua_ptr);
LuaObject** this_ptr = reinterpret_cast<LuaObject**>(lua_newuserdata(lua_ptr, sizeof(LuaObject*)));
*this_ptr = this;
luaL_getmetatable(lua_ptr, userdata_name.c_str());
lua_setmetatable(lua_ptr, -2); // setmetatable(this_ptr, userdata_name)
lua_setglobal(lua_ptr, global_name.c_str()); // store to global scope
}
int CallFunction(lua_State* lua_state) const {
std::string name = lua_tostring(lua_state, lua_upvalueindex(1)); // userdata:<function>(param2, param3)
auto it = functions.find(name); // <function> lua_tostring(lua_state, lua_upvalueindex(1))
if (it != functions.end()) { // <implicit this> lua_touserdata(l, 1)
return it->second(lua_state); // <param #1> lua_touserdata(l, 2)
} // <param #2> lua_touserdata(l, 3)
return 0; // <param #n> lua_touserdata(l, n+1)
}
void NewFunction(const std::string& name, std::function<int(lua_State*)> func) {
functions[name] = func;
}
private:
void RegisterUserData(lua_State* lua_ptr) {
luaL_getmetatable(lua_ptr, userdata_name.c_str());
if (lua_type(lua_ptr, -1) == LUA_TNIL) {
/* create metatable for userdata_name */
luaL_newmetatable(lua_ptr, userdata_name.c_str());
lua_pushvalue(lua_ptr, -1); /* push metatable */
/* metatable.__gc = DeletePtr<LuaObject> */
//lua_pushcfunction(lua_ptr, DeletePtr<LuaObject>);
//lua_setfield(lua_ptr, -2, "__gc");
/* metatable.__index = ReturnClosure<LuaObject> */
lua_pushcfunction(lua_ptr, ReturnClosure<LuaObject>);
lua_setfield(lua_ptr, -2, "__index");
}
}
std::map<std::string, std::function<int(lua_State*)>> functions;
std::string userdata_name;
};
int main(int argc, char* argv[]) {
lua_State* lua_state = luaL_newstate();
luaL_openlibs(lua_state);
LuaObject luaobj;
luaobj.CreateNewUserData(lua_state, "test_obj");
luaobj.NewFunction("action", [](lua_State* l)->int {
std::string result = "action has been executed";
LuaObject** ptr = reinterpret_cast<LuaObject**>(lua_touserdata(l, 1));
result += "\n #1 param is user_data (self == this) value = " + std::to_string(reinterpret_cast<size_t>(*ptr));
for (int i = 2; i <= lua_gettop(l); ++i) {
result += "\n #" + std::to_string(i)+ " = " + lua_tostring(l, i);
}
result += "\n #n param is passed on call operator () #n = " + std::to_string(lua_gettop(l));
lua_pushfstring(l, result.c_str());
return 1;
});
std::string lua_code;
lua_code += "print(test_obj:unknown_function()) \n";
lua_code += "print(test_obj:action()) \n";
lua_code += "print(test_obj:action(1)) \n";
lua_code += "print(test_obj:action(1, 2)) \n";
lua_code += "print(test_obj:action(1, 2, 'abc'))\n";
if (!(luaL_loadbuffer(lua_state, lua_code.c_str(), lua_code.length(), NULL) == 0 && lua_pcall(lua_state, 0, LUA_MULTRET, 0) == 0)) {
std::cerr << "Lua Code Fail: " << lua_tostring(lua_state, -1) << std::endl;
}
lua_close(lua_state);
return 0;
}
Output:
action has been executed
#1 param is user_data (self == this) value = 13629232
#n param is passed on call operator () #n = 1
action has been executed
#1 param is user_data (self == this) value = 13629232
#2 = 1
#n param is passed on call operator () #n = 2
action has been executed
#1 param is user_data (self == this) value = 13629232
#2 = 1
#3 = 2
#n param is passed on call operator () #n = 3
action has been executed
#1 param is user_data (self == this) value = 13629232
#2 = 1
#3 = 2
#4 = abc
#n param is passed on call operator () #n = 4
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 currently using the following code to get a value from a table (cstring = const char*):
template<>
cstring luaTable::get(cstring name) {
prep_get(name); // puts table[name] at -1 in stack
cstring result;
if(!lua_isstring(L, -1)) {
report(name, "is not a string");
result = "";
}
else {
result = lua_tostring(L, -1);
}
lua_pop(L, 1);
return result;
}
void luaTable::prep_get(cstring name) {
lua_pushstring(L, name); // name at -1, table at -2
lua_gettable(L, -2);
// table[name] is now at position -1 in stack
}
This works perfectly for tables of form table = {a=10, b=2}. How can I modify it to get values from tables without keys such as table = {10, 2}?
I'm sure I'm missing something simple but can't seem to find the answer.
Thanks in advance,
Ben
Edit: added some pops
Okay sorry to answer my own question so soon - but a quick flash of inspiration lead to:
void luaTable::prep_get(cstring name) {
lua_pushstring(L, name); // name string at -1
if(lua_isnumber(L, -1)) { // call prep_get("i") for ith element etc
int key = lua_tonumber(L, -1);
lua_pop(L, 1); // remove the name string from -1
lua_pushnumber(L, key); // push name number to -1
}
lua_gettable(L, -2);
// result is now at position -1 in stack
}
which works as desired.
#user1483596 I don't think that solution would work. lua_isnumber will only return true if the value is of type number, and you just pushed a string, so it will always return false.
Instead, try something like this:
void luaTable::prep_get(cstring name) {
int num = strtol(name, 0, 0);
if (num > 0) {
lua_pushnumber(L, num);
} else {
lua_pushstring(L, name);
}
lua_gettable(L, -2);
}
Bear in mind though that it won't handle a special case. In Lua a[1] and a["1"] are different. If you use this function, you'll always treat numbers as array indices, even if they're not.
If you want to differentiate both cases, then you could overload prep_get and take a number.
How to return C++ object to lua?
My C++ code is following:
class MyClass
{
public:
void say()
{
print("Hello\r\n");
}
};
int test(lua_State* l)
{
MyClass* obj = new MyClass();
lua_pushlightuserdata(l, obj);
return 1;
}
Lua Test is following:
local a = MyClass:new()
a:say() <--- OK, beacause I set metatable!!
local b = test()
b:say() <--- ERROR: attempt to index local 'b' (a userdata value)
How to modify test() function to work fine?
obj will auto destory by lua ?
PS: I has set MyClass metatable is following
void l_registerClass()
{
lua_newtable(l);
int methods = lua_gettop(l);
luaL_newmetatable(l, "MyClass");
int metatable = lua_gettop(l);
lua_pushvalue(l, methods);
lua_setglobal(l, "MyClass");
lua_pushvalue(l, methods);
l_set(l, metatable, "__metatable");
//set metatable __index
lua_pushvalue(l, methods);
l_set(l, metatable, "__index");
//set metatable __gc
lua_pushcfunction(l, l_destructor);
l_set(l, metatable, "__gc");
//set method table
lua_newtable(l); // mt for method table
lua_pushcfunction(l, l_constructor);
lua_pushvalue(l, -1); // dup new_T function
l_set(l, methods, "new"); // add new_T to method table
l_set(l, -3, "__call"); // mt.__call = new_T
lua_setmetatable(l, methods);
// set methods metatable
lua_pushstring(l, "say");
lua_pushcclosure(l, l_proxy, 1);
lua_settable(l, methods);
lua_pop(l, 2);
}
int l_proxy(lua_State* l)
{
int i = (int)lua_tonumber(l, lua_upvalueindex(1));
lua_remove(l, 1); // remove self so member function args start at index 1
//call real function
MyClass* obj = getInstance();
obj->say();
return 1;
}
I should don't lost step ?
I don't use any Lua Binding Framework, I am using pure Lua Library.
==== update 1 ====
Thanks for user1520427's answer, but....
int test(lua_State* l)
{
MyClass** c = (MyClass**)lua_newuserdata(l, sizeof(MyClass*));
*c = new MyClass(); // we manage this
lua_getglobal(l, "MyClass");
lua_setmetatable(l, -2);
return 1;
}
and I test it in Lua
local b = test()
print( type(b) )
local meta = getmetatable(b)
for k,v in pairs(meta) do
print(" ", k, v)
end
Lua show metatable is correct.
userdata
say function: 00602860
new function: 00493665
But lua still shows the same error in
b:say() <-- attempt to index local 'b' (a userdata value)
=== update 2 ===
int test(lua_State* l)
{
MyClass** c = (MyClass**)lua_newuserdata(l, sizeof(MyClass*));
*c = new MyClass(); // we manage this
luaL_getmetatable(l, "MyClass"); //
lua_getglobal(l, "MyClass");
lua_setmetatable(l, -2);
return 1;
}
the lua test result:
b:say() <-- attempt to call method 'say' (a nil value)
=== update 3 ===
int test(lua_State* l)
{
MyClass** c = (MyClass**)lua_newuserdata(l, sizeof(MyClass*));
*c = new MyClass(); // we manage this
luaL_getmetatable(l, "MyClass");
luaL_setmetatable(l, "MyClass"); //modify
return 1;
}
Lua test result:
b:say() <-- calling 'say' on bad self
You're not associating what you return from test with the class you registered. Try something like:
int test(lua_state* l) {
MyClass** c = lua_newuserdata(l, sizeof(MyClass*)); // lua will manage the MyClass** ptr
*c = new MyClass(); // we manage this
luaL_getmetatable(l, "MyClass");
lua_setmetatable(l, -2);
return 1;
}
That's off the top of my head but you get the idea. You've already set the destructor, so when Lua garbage collects the userdata, it will call your __gc func which should then cast, dereference and delete the data.
thanks for user1520427 and lhf
correct code sould is following:
int test(lua_State* l)
{
MyClass** c = (MyClass**)lua_newuserdata(l, sizeof(MyClass*));
*c = new MyClass(); // we manage this
luaL_setmetatable(l, "MyClass"); //assign MyClass metatable
return 1;
}
Lua test code is work fine.
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)
{
SetLuaState(l);
void* lua_obj = lua_topointer(l, 1); //I hope get lua's a variable
processObj(lua_obj);
...
return 0;
}
int processObj(void *lua_obj)
{
lua_State* l = GetLuaState();
lua_pushlightuserdata(l, lua_obj); //access lua table obj
int top = lua_gettop(l);
lua_getfield(l, top, "ID"); //ERROR: attempt to index a userdata value
std::string id = lua_tostring(l, -1); //I hoe get the value "abc123"
...
return 0;
}
I get the ERROR: attempt to index a userdata value
How to access lua's object from lua_topointer() ?
Storing a lua object in C, then calling it from C.
You shouldn't use lua_topointer as you can't convert it back to lua object, store your object in the registry and pass it's registry index:
int callCfunc(lua_State* L)
{
lua_pushvalue(L, 1);//push arg #1 onto the stack
int r = luaL_ref(L, LUA_REGISTRYINDEX);//stores reference to your object(and pops it from the stask)
processObj(r);
luaL_unref(L, LUA_REGISTRYINDEX, r); // removes object reference from the registry
...
int processObj(int lua_obj_ref)
{
lua_State* L = GetLuaState();
lua_rawgeti(L, LUA_REGISTRYINDEX, lua_obj_ref);//retrieves your object from registry (to the stack top)
...
You don't want to use lua_topointer for that task. In fact, the only reasonable use of lua_topointer is for debugging purposes (like logging).
As a is a table, you need to use lua_gettable to access one of its fields, or even simpler use lua_getfield. Of course you cannot pass a void* pointer to processObj for that task, but you can use the stack index instead.