Registering a C++ class for use in Lua 5.2 - c++

So I was reading up on how to create and register a c++ class for Lua via THIS little blog tutorial.
But as simple, informative and legible as it is, it appears to be for an older version of Lua.
So some of the functions/macros are either missing or just have different signatures.
What would the following code look like in Lua C version 5.2?
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <stringstream>
#include <string>
using namespace std;
// Just a useless test class
class MyClass
{
private:
int _X;
public:
MyClass(int x) : _X(x) {};
~MyClass() { Release() };
// Displays your number in a welcoming message box
void Hello()
{
stringstream ss;
ss << "Hello!" << endl << "Your number is: " << _X;
string s(ss.str());
MessageBoxA(NULL, s.c_str(), "MyClass", MB_ICONINFORMATION);
}
void Release() {
//release code goes here
}
};
// Functions that will create/destroy MyClass instances
static int newMyClass(lua_State* L)
{
int n = lua_gettop(L); // Number of arguments
if (n != 2)
return luaL_error(L, "Got %d arguments, expected 2 (class, x)", n);
// First argument is now a table that represent the class to instantiate
luaL_checktype(L, 1, LUA_TTABLE);
lua_newtable(L); // Create table to represent instance
// Set first argument of new to metatable of instance
lua_pushvalue(L, 1);
lua_setmetatable(L, -2);
// Do function lookups in metatable
lua_pushvalue(L, 1);
lua_setfield(L, 1, "__index");
// Allocate memory for a pointer to to object
MyClass** s = (MyClass**)lua_newuserdata(L, sizeof(MyClass*));
int x = luaL_checknumber(L, 2);
*s = new MyClass(x);
luaL_getmetatable(L, "Lua.MyClass");
lua_setmetatable(L, -2);
lua_setfield(L, -2, "__self");
return 1;
}
static int doSomethingMyClass(lua_State* L)
{
MyClass* c = nullptr;
checkUserData(L, "Lua.MyClass", c);
c->Hello();
return 0;
}
static int destroyMyClass(lua_State* L)
{
MyClass* c = nullptr;
checkUserData(L, "Lua.MyClass", c);
c->Release();
return 0;
}
// Functions that will show up in our Lua environment
static const luaL_Reg gMyClassFuncs[] = {
// Creation
{ "new", newMyClass) },
{ "hello", helloMyClass },
{ NULL, NULL }
};
static const luaL_Reg gDestroyMyClassFuncs[] = {
{"__gc", destroyMyClass},
{NULL, NULL}
};
// Registers the class for use in Lua
void registerMyClass(lua_State *L)
{
// Register metatable for user data in registry
luaL_newmetatable(L, "Lua.MyClass");
luaL_register(L, 0, gDestroyMyClassFuncs);
luaL_register(L, 0, gMyClassFuncs);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
// Register the base class for instances of Sprite
luaL_register(L, "MyClass", gSpriteFuncs);
}
Basically, the goal here is to be able to write the following in Lua:
-- Create a new MyClass instance
local c = MyClass:new(5)
-- Show message
c:Hello() -- Should say something like "Hello! Your number is: 5"
What would I need to change in order to make this work for 5.2?

The only function in that code that is not part of Lua 5.2 is luaL_register. You should use luaL_setfuncs instead.
You should also set the global MyClass by hand or use local MyClass=require"MyClass" in your Lua code because require no longer sets globals.
If you are embedding Lua, you can just compile it and your code with -DLUA_COMPAT_MODULE and get the 5.1 functions. But consider that now is a good time to move your code to Lua 5.2, if you plan to use this version.

Related

Handling binary data between C++ and Lua

i need to make a program that will handle binary data, lots of it.
in short, the C++ program will load a binary file (some of them exceed 20mb) into a buffer.
then it will run a Lua script and pass all this loaded data to the script, which will do some manipulation and return the result to the C++ program.
I need to do this as quickly as possible, perform at the best and get the job done faster.
A while ago I already made this program using the conventional methods of Lua, but it was extremely slow.
So I lost the files, and now I want to redo it in a better, faster way that doesn't compromise performance.
Searching a bit, I found this.
I had to make some small changes to adapt to the new version of Lua, but I can't get it to work.
Can you help me with this?
And if there's a better way to do the job I said, what would it be?
#include "stdafx.h"
// metatable method for handling "array[index]"
static int array_index(lua_State* L) {
int** parray = (int**)luaL_checkudata(L, 1, "array");
int index = luaL_checkinteger(L, 2);
lua_pushnumber(L, (*parray)[index - 1]);
return 1;
}
// metatable method for handle "array[index] = value"
static int array_newindex(lua_State* L) {
int** parray = (int**)luaL_checkudata(L, 1, "array");
int index = luaL_checkinteger(L, 2);
int value = luaL_checkinteger(L, 3);
(*parray)[index - 1] = value;
return 0;
}
// create a metatable for our array type
static void create_array_type(lua_State* L) {
static const struct luaL_Reg array[] = {
{ "__index", array_index },
{ "__newindex", array_newindex },
NULL, NULL
};
luaL_newmetatable(L, "array");
luaL_setfuncs(L, array, 0);
}
// expose an array to lua, by storing it in a userdata with the array metatable
static int expose_array(lua_State* L, int array[]) {
int** parray = (int**)lua_newuserdata(L, sizeof(int**));
*parray = array;
luaL_getmetatable(L, "array");
lua_setmetatable(L, -2);
return 1;
}
// test data
int mydata[] = { 1, 2, 3, 4 };
// test routine which exposes our test array to Lua
static int getarray(lua_State* L) {
return expose_array(L, mydata);
}
int __declspec(dllexport) __cdecl luaopen_array(lua_State* L) {
create_array_type(L);
// make our test routine available to Lua
lua_register(L, "array", getarray);
return 0;
}
int main()
{
lua_State* L = luaL_newstate();
luaL_dostring(L, "require 'array'");
luaL_dostring(L, "foo = array()");
luaL_dostring(L, "x = foo[1]");
lua_getglobal(L, "x");
lua_Number x = lua_tonumber(L, 1);
printf("foo[1] = %d\n", (int)x);
}
Consider using lightuserdata to avoid copying file contents excessively.
lightuserdata is just a pointer, so you need to define some methods to work with it as well.
The idea looks like this:
#include <lauxlib.h>
#include <lualib.h>
#define BIN_DATA_MT_ID "bin data"
int get_byte(lua_State *L) {
const char *file_contents = luaL_checkudata(L, 1, BIN_DATA_MT_ID);
size_t byte_index = luaL_checknumber(L, 2);
// checking OOB is your custody here, omitted for simplicity
lua_pushlstring(L, file_contents + byte_index, 1);
return 1;
}
static const luaL_Reg __index[] = {
{"get_byte", get_byte},
{NULL, NULL}
};
int main() {
const char file_contents[4] = { 0x25, 0xAA, 0xBB, 0xCC };
lua_State *L = luaL_newstate();
luaopen_base(L); // adds "print" function
lua_pushlightuserdata(L, (void *)file_contents);
luaL_newmetatable(L, BIN_DATA_MT_ID);
luaL_newlib(L, __index);
lua_setfield(L, -2, "__index");
lua_setmetatable(L, -2);
lua_setglobal(L, "mybindata");
luaL_dostring(L, "print(mybindata:get_byte(0))");
lua_close(L);
return 0;
}
The fastest way for Lua to access the bytes of a large byte array is to expose that array directly as a string within Lua. Now because Lua does reference counting for strings, this means that Lua will always allocate its own storage for the string. So to efficiently load the string into Lua (ie: avoiding a 20+MiB copy), you need to use the lua_Buffer-based API to load it directly into Lua's storage.
But outside of that quirk, it will certainly be faster inside Lua to use array accesses of a string to access bytes compared to doing a function call for each byte accessed from the buffer.

Sharing global variables between different Lua states through require

I'm trying to find a way to share global variables of a specific Lua script(test.lua in the example) between different Lua states.
Here's my simple example code:
In test.lua
num = 2
In main.cpp
#include <iostream>
#include <lua.hpp>
int main()
{
lua_State *L1 = luaL_newstate(); //script A
luaL_openlibs(L1);
lua_settop(L1, 0);
luaL_dostring(L1, "require('test') num = 5");
lua_State *L2 = luaL_newstate(); //script B
luaL_openlibs(L2);
lua_settop(L2, 0);
luaL_dostring(L2, "require('test') print(num)");
lua_close(L1);
lua_close(L2);
}
I expect to get 5 but I get 2.
Is not possible to share global variables between different lua_State* through require?
ADDED :
If it's not possible, would it be a good idea to open test.lua using luaL_loadfile and then create getter/setter methods in C++ to share variable num between script A and B?
For example like this,
Script A:
script = my.Script("test")
script:setVar("num", 5)
Script B:
script = my.Script("test")
print(script:getVar("num"))
I wonder what you think about this design as an alternative to require.
Two distinct lua_States are completely and totally independent. One cannot directly affect anything that happens in another. You can expose some C code to one that allows it to modify the other, or they could both access some external resource (a file, for example) that allows them to share data.
But outside of things like this, no, they cannot interact.
The preferred method for this is to not make them separate lua_States.
Rather than having the global value in a Lua module, you could push a pointer to a C++ value as an upvalue for a metatable to a table which contains those globals. Then you push the globals table with the same metatable to both VMs. When you now access globals.num the getglobal and setglobal metamethods are triggered (depending on whether you read or write). These will update the value on the C++ side, such that it is shared between the two VMs.
N.B.: As you can judge from the lengthy boilerplate this is not a good solution. You should avoid having multiple VMs at the same time. If you require multiple VMs for concurrency purposes, consider using a mature library like Lua Lanes rather than rolling your own (doing this right requires several thousands of lines of code).
#include <string>
#include <lua.hpp>
int setglobal(lua_State *L) {
void *p = luaL_checkudata(L, 1, "globals_meta");
luaL_argcheck(L, p != nullptr, 1, "invalid userdata");
std::string key = lua_tostring(L, 2);
luaL_argcheck(L, key == "num", 2, "unknown global");
int value = luaL_checkinteger(L, 3);
luaL_argcheck(L, lua_isnumber(L, 3), 3, "not a number");
int *num = static_cast<int *>(lua_touserdata(L, lua_upvalueindex(1)));
*num = value;
lua_pop(L, 1);
return 0;
}
int getglobal(lua_State *L) {
void *p = luaL_checkudata(L, 1, "globals_meta");
luaL_argcheck(L, p != nullptr, 1, "invalid userdata");
std::string key = lua_tostring(L, 2);
luaL_argcheck(L, key == "num", 2, "unknown global");
int num = *static_cast<int *>(lua_touserdata(L, lua_upvalueindex(1)));
lua_pop(L, 1);
lua_pushinteger(L, num);
return 1;
}
static const struct luaL_Reg globals_meta[] = {
{"__newindex", setglobal},
{"__index", getglobal},
{nullptr, nullptr} // sentinel
};
int main() {
int num = 2;
// script A
lua_State *L1 = luaL_newstate();
luaL_openlibs(L1);
luaL_newmetatable(L1, "globals_meta");
lua_pushlightuserdata(L1, &num);
luaL_setfuncs(L1, globals_meta, 1);
lua_newuserdata(L1, 0);
luaL_getmetatable(L1, "globals_meta");
lua_setmetatable(L1, -2);
lua_setglobal(L1, "globals");
luaL_dostring(L1, "print('Script A: ' .. globals.num) globals.num = 5");
// script B
lua_State *L2 = luaL_newstate();
luaL_openlibs(L2);
luaL_newmetatable(L2, "globals_meta");
lua_pushlightuserdata(L2, &num);
luaL_setfuncs(L2, globals_meta, 1);
lua_newuserdata(L2, 0);
luaL_getmetatable(L2, "globals_meta");
lua_setmetatable(L2, -2);
lua_setglobal(L2, "globals");
luaL_dostring(L2, "print('Script B: ' .. globals.num)");
lua_close(L1);
lua_close(L2);
}
As a challange to myself I implemented a complete global table which can communicate values of type nil, bool, int, double, and string between two Lua states. They can be named with everything that has a string representation.
-- To be on the safe side, just use numbers and strings as keys
globals[1] = "x"
globals.num = 5
-- Be careful when using table or function literals as keys
-- Two empty tables don't have the same representation
globals[{}] = 2 -- "table: 0x10d55a0" = 2
globals[{}] = 1 -- "table: 0x10ce2c0" = 1
I haven't checked all sorts of exceptional situations exhaustively, so no refunds!
#include <iostream>
#include <string>
#include <unordered_map>
#include <boost/variant.hpp>
#include <lua.hpp>
enum class nil {};
using Variant = boost::variant<nil, bool, int, double, std::string>;
int setglobal(lua_State *L) {
void *p = luaL_checkudata(L, 1, "globals_meta");
luaL_argcheck(L, p != nullptr, 1, "invalid userdata");
std::string key = luaL_tolstring(L, 2, nullptr);
auto &globals = *static_cast<std::unordered_map<std::string, Variant> *>(
lua_touserdata(L, lua_upvalueindex(1)));
Variant &v = globals[key];
switch (lua_type(L, 3)) {
case LUA_TNIL:
v = nil{};
break;
case LUA_TBOOLEAN:
v = static_cast<bool>(lua_toboolean(L, 3));
lua_pop(L, 1);
break;
case LUA_TNUMBER:
if (lua_isinteger(L, 3)) {
v = static_cast<int>(luaL_checkinteger(L, 3));
} else {
v = static_cast<double>(luaL_checknumber(L, 3));
}
lua_pop(L, 1);
break;
case LUA_TSTRING:
v = std::string(lua_tostring(L, 3));
lua_pop(L, 1);
break;
default:
std::string error = "Unsupported global type: ";
error.append(lua_typename(L, lua_type(L, 3)));
lua_pushstring(L, error.c_str());
lua_error(L);
break;
}
return 0;
}
int getglobal(lua_State *L) {
void *p = luaL_checkudata(L, 1, "globals_meta");
luaL_argcheck(L, p != nullptr, 1, "invalid userdata");
std::string key = luaL_tolstring(L, 2, nullptr);
auto globals = *static_cast<std::unordered_map<std::string, Variant> *>(
lua_touserdata(L, lua_upvalueindex(1)));
lua_pop(L, 1);
auto search = globals.find(key);
if (search == globals.end()) {
lua_pushstring(L, ("unknown global: " + key).c_str());
lua_error(L);
return 0;
}
Variant const &v = search->second;
switch (v.which()) {
case 0:
lua_pushnil(L);
break;
case 1:
lua_pushboolean(L, boost::get<bool>(v));
break;
case 2:
lua_pushinteger(L, boost::get<int>(v));
break;
case 3:
lua_pushnumber(L, boost::get<double>(v));
break;
case 4:
lua_pushstring(L, boost::get<std::string>(v).c_str());
break;
default: // Can't happen
std::abort();
break;
}
return 1;
}
static const struct luaL_Reg globals_meta[] = {
{"__newindex", setglobal},
{"__index", getglobal},
{nullptr, nullptr} // sentinel
};
int main() {
std::unordered_map<std::string, Variant> globals;
globals["num"] = 2;
// script A
lua_State *L1 = luaL_newstate();
luaL_openlibs(L1);
luaL_newmetatable(L1, "globals_meta");
lua_pushlightuserdata(L1, &globals);
luaL_setfuncs(L1, globals_meta, 1);
lua_newuserdata(L1, 0);
luaL_getmetatable(L1, "globals_meta");
lua_setmetatable(L1, -2);
lua_setglobal(L1, "globals");
if (luaL_dostring(L1, "print('Script A: ' .. globals.num)\n"
"globals.num = 5") != 0) {
std::cerr << "L1:" << lua_tostring(L1, -1) << '\n';
lua_pop(L1, 1);
}
// script B
lua_State *L2 = luaL_newstate();
luaL_openlibs(L2);
luaL_newmetatable(L2, "globals_meta");
lua_pushlightuserdata(L2, &globals);
luaL_setfuncs(L2, globals_meta, 1);
lua_newuserdata(L2, 0);
luaL_getmetatable(L2, "globals_meta");
lua_setmetatable(L2, -2);
lua_setglobal(L2, "globals");
if (luaL_dostring(L2, "print('Script B: ' .. globals.num)") != 0) {
std::cerr << "L1:" << lua_tostring(L2, -1) << '\n';
lua_pop(L2, 1);
}
lua_close(L1);
lua_close(L2);
}
While Lua states are separate by default, some binding libraries expose functionality to transfer information from one to the other.
For example, in sol, there are methods to serialize fairly arbitrary Lua data, including functions, to C++ data. You can then de-serialize that data into another Lua state, to effectively copy it (code link).
But you still will have two copies, in the end. You can't modify one Lua state from another directly.
Your last point, about exposing some getter/setter, is valid. You can have some data stored in C/C++ and have two different Lua states able to access it. You still have to bind that data to each VM separately.

Lua Array and Object Oriented Access for the Same Userdata

I have an CArray class written in C++ and is exposed to Lua as Array.
1) Create a new userdata:
int Array_new(lua_State* L)
{
int len = luaL_checkint(L, 1);
CArray<std::string> **Arr = (CArray<std::string>**)lua_newuserdata(L, sizeof(CArray<std::string>*));
*Arr = new CArray<std::string>(len);
luaL_getmetatable(L, "ArrayMetatable");
lua_setmetatable(L, -2);
return 1;
}
2) Access an element:
int Array_getValue(lua_State* L)
{
CArray<std::string>* arr = *(CArray<std::string>**)lua_touserdata(L, 1);
int pos=luaL_checknumber(L, 2)-1;
//Omitted for brevity
lua_pushstring(L, stdStr.c_str());
return 1;
}
3) Register to Lua:
int luaopen_Array(lua_State* L)
{
luaL_newmetatable(L, "ArrayMetatable"); // metatable1
luaL_setfuncs(L, Array_metatable, 0);
lua_pushstring(L, "__index");
lua_pushvalue(L, -2); // metatable1 __index metatable1
lua_settable(L, -3); // metatable1[__index]=metatable1
/*luaL_newmetatable(L, "ArrayMetatable_2"); // metatable1 metatable2
lua_pushstring(L, "__index"); // metatable1 metatable2 __index
lua_pushstring(L, "get"); // metatable1 metatable2 __index get
lua_gettable(L, 1); // metatable1 metatable2 __index
lua_settable(L, 2);
lua_pushvalue(L, 1); //metatable1 metatable2 metatable1*/
lua_setglobal(L, "Array");
return 0;
}
The problem is I have to prefer either Lua code to access an element in the array: a) arr:get(1) to get the first element, b) arr[1] to get the first element.
However a and b does not work at the same time, so either I have to prefer style a or style b. Is it possible to do a and b at the same time by modifying the 3rd step?
So far the code I presented gives an error at step 2 if I try a Lua expression such as arr[2] such that the variable arr's address is 0xcccccc.
You can add an __index method and a get method, just add them separately.
The registration function looks a little messed up.
Normally you would have two meta-tables - the global functions, and the member functions.
static const struct luaL_Reg Array_globals[] = {
{ "new", Array_new },
{ NULL,NULL }
};
static const struct luaL_Reg Array_members[] = {
{ "get", Array_getValue},
{ "__index", Array_getValue },
{ NULL,NULL }
};
the luaopen_ function merely needs to build the tables with the appropriate methods.
I would recommend also writing __len, and __setindex
int luaopen_Array(lua_State* L)
{
luaL_newmetatable(L, "ArrayMetatable"); // metatable1
luaL_setfuncs(L, Array_members, 0);
luaL_newlib( L, Array_globals );
return 1; // return table to caller.
}

C++ - Lua: get caller table name

How can i get in a c++ function the name of the table who calls it?
Here is the c++ sourcecode, i need to store objects in a c++ map and in lua tables, the c++ map->first is the same name of the table in lua.
see the function static int move_to(lua_State* L) i need to modify the lua table who calls that function.
Test.cpp
#include <lua.hpp>
#include <lauxlib.h>
#include <iostream>
#include <map>
#include <string>
struct Point{
int x=0, y=0;
};
std::map<std::string, Point> points;
static int move_to(lua_State* L){
int num_args=lua_gettop(L);
if(num_args>=2){
int new_x=lua_tonumber(L, 1);//first argument: x.
int new_y=lua_tonumber(L, 2);//second argument: y.
std::string name=???;//i need to get the name of the lua table who calls this function.
lua_getglobal(L, name.c_str());
lua_pushnumber(L, new_x);
// modify point in the lua table.
lua_setfield(L, -2, "x");// point.x=x
lua_pushnumber(L, new_y);
lua_setfield(L, -2, "y");// point.x=x
// modify point in the c++ map.
points.find(name)->second.x=new_x;
points.find(name)->second.y=new_y;
};
return 0;
};
static int create_point(lua_State* L){
int num_args=lua_gettop(L);
if(num_args>=2){
std::string name=lua_tostring(L, 1);//first argument: name.
int x=lua_tonumber(L, 2);//second argument: x.
int y=lua_tonumber(L, 3);//third argument: y.
static const luaL_Reg functions[]={{ "move_to", move_to},{ NULL, NULL }};
lua_createtable(L, 0, 4);
luaL_setfuncs(L, functions, 0);
lua_pushnumber(L, x); lua_setfield(L, -2, "x");// point.x=x
lua_pushnumber(L, y); lua_setfield(L, -2, "y");// point.y=y
lua_setglobal(L, name.c_str());
points.insert(std::pair<std::string, Point>(name, Point()));// insert point in the c++ map.
};
return 0;
};
int main(){
lua_State * L=luaL_newstate();
luaL_openlibs(L);
luaL_loadfile(L, "script.lua");
lua_pushcfunction(L, create_point); lua_setglobal(L, "create_point");//Register create_point to L.
lua_call(L, 0, 0);
std::cout<<"c++: a.x: "<<points.find("a")->second.x<<", a.y: "<<points.find("a")->second.y<<std::endl;
return 0;
};
Here is the lua script.
Test.lua
-- name, x, y
create_point("a", 10, 11)
print("lua: a.x: " .. a.x .. ", a.y: " .. a.y)
a.move_to(1,2)
print("lua: a.x: " .. a.x .. ", a.y: " .. a.y)
How can i get in a c++ function the name of the table who calls it?
You can't get table's name, since values don't have names.
Just pass that table as first argument, the very much like this pointer in C++. Lua has special syntax to make it easy:
a:move_to(1,2)
Note the difference - colon is used instead of dot. That syntactic sugar is shorter equivalent to:
a.move_to(a,1,2)

How to return C++ object to lua 5.2?

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.