Revert existing objects from C ++ to Lua

In C ++, let's say I have a class that creates a binary tree similar to a structure, and I use it something like this:

CTreeRoot* root = new CTreeRoot(/* whatever */); CNode* leftNode = root->getLeftNode(); CNode* rightNode = root->getRightNOde(); leftNode->doSomething(); rightNode->doSomething(); // etc 

And suppose that the left and right nodes have their left and right nodes (hence the binary tree). Now I want to show this to Lua ( not using luabind) so that I can do the same:

 local root = treeroot.new(/* whatever */) local left = root:getLeftNode() local right = root:getRightNode() left:doSomething(); right:doSomething(); 

I got most of the work. However, for the getLeftNode () and getRightNode () methods, I am sure that I am doing this "wrong." This is how I implement getLeftNode () in C ++, for example:

 int MyLua::TreeRootGetLeftNode(luaState* L) { CTreeRoot* root = (CTreeRoot*)luaL_checkudata(L, 1, "MyLua.treeroot"); CNode* leftNode = root->getLeftNode(); if (leftNode != NULL) { int size = sizeof(CNode); // create a new copy of the CNode object inplace of the memory Lua // allocated for us new ((CNode*)lua_newuserdata(L,size)) CNode((const CNode&)*leftNode); lua_setmetatable(L, "MyLua.treenode"); } else { lua_pushnil(L); } return 1; } 

I threw UserData back to the CTreeRoot object, call getLeftNode (), make sure it exists, and then (here the "wrong part") create another USERDATA data object with the copy constructor copy object I want to return.

Is this "standard practice" for this type of scenario? It looks like you would like to avoid creating another copy of the object, because what you really want is just a reference to an existing object.

It seems like this would be an ideal place for lightuserdata, since I do not want to create a new object, I would be happy to return an existing object. The problem, however, is that lightuserdata does not have a meta table, so the objects will be useless to me as soon as I return them. Basically, I want to do something like:

 int MyLua::TreeRootGetLeftNode(luaState* L) { CTreeRoot* root = (CTreeRoot*)luaL_checkudata(L, 1, "MyLua.treeroot"); CNode* leftNode = root->getLeftNode(); if (leftNode != NULL) { // "WRONG" CODE BUT SHOWS WHAT I WISH I COULD DO lua_pushlightuserdata(L, (void*)leftNode); lua_setmetatable(L, "MyLua.treenode"); } else { lua_pushnil(L); } return 1; } 

Can someone tell me how I can return the MyLua::TreeRootGetLeftNode to a Lua copy of an object that already exists in such a way that I can use this object as an “object” in Lua?

+4
source share
1 answer

There are two levels of memory optimization that can be performed here. In his first functional but inefficient solution, when the user calls getLeftNode() , he must create a copy of CNode in order to store it in Lua user data. In addition, each time the user calls getLeftNode() several times on the same tree, he will continue to create new user data to represent CNode , even if it was created earlier.

At the first level of optimization, you can save this call so that every time a user requests the same subtree, you can simply return the user data that was originally created, instead of copying and building another userdata to represent the same thing. There are 3 approaches to this, but depending on whether you want to change the Lua interface, change the C ++ implementation, or just bite the bullet.

  • MyLua.treenode user data currently contains actual CNode object CNode . This is sad, however, because it means that whenever you create a CNode object, you need to use the new storage location in memory allocated by Lua immediately after creation. It is probably best to just save the pointer instead of ( CNode* ) in userdata for MyLua.treenode . This requires you to change the Lua interface for MyLua.treenode so that it now treats its data as a pointer to a CNode object.

  • If you prefer to store CNode data in CNode user data directly, then you will need to make sure that when creating CTreeRoot it will use the new location to build the CNode from the memory allocated by Lua each time (or maybe you can use the allocator pattern , used in the standard C ++ library?). This is less elegant since the CNode implementation now depends on the Lua runtime, and I do not recommend this.

  • If none of the above solutions work, you just need to make a copy whenever you return a subnode, although you can increase the efficiency of callbacks on the same node by tracking from whether you have previously created the same user data (for example, using a Lua table).

At the second level of optimization, you can additionally save memory by making the copied subtree < weak link a fragment of the source tree. Thus, whenever you copy a subtree, you simply create a pointer to a part of the source tree. Or you can use a strong link if you want your subtree to be preserved even after the original tree has been destroyed, but then you have to go to the details of link counting. In any case, this optimization is exclusively at the C ++ level and is not related to the Lua interface, and judging by your code, I assume that you are already using weak links ( CNode* ).

Note. Light user data is probably best avoided, except for use in an internal implementation. The reason is that light user data is essentially equivalent to pointers to C and thus can point to anything. If you specify bright user data in Lua code, you will not have the slightest idea where light user data may appear or what types of data it contains using it pose a security risk (as well as the possibility of segfault of your program). The right way to use light user data is to use it as an index to the Lua lookup table stored in the Lua registry, which can be used to implement the previously mentioned memoization.

+2
source

All Articles