Sometimes we import C + + classes into lua, which makes it easy to get to C + + in Lua class, which is often used in game programming, this article simply introduces an implementation.
1. Lua an object-oriented implementation inside
in lua , we can do this by manipulating the table as follows:
account = {balance = 0}
function Account:withdraw (v)
self.balance = self.balance-v
End
Here, we define a table and have a withdraw function that we can use
A = account
A:withdraw (100)
This is the same as the C + + class form. But there is no concept of a class in Lua, but each object has a prototype(prototype) that, when invoked in some operations that are not part of the object, will first come to the Find these operations in prototype. So if we have two objects a and b, if we take b as a of the prototype , we have achieved the most basic inheritance. The following implementations are required in Lua
setmetatable (A, {__index = b})
Such a call to any nonexistent member will be found in object b . Here we use the __index metamethod, as to what is metatable and Metamethod Lua the relevant documentation is described in detail. (In addition special recommendation Programming in Lua this book, which almost all introduced these knowledge)
2. C + + class in Lua forms of expression in
in lua , we assume that the C + + class operates in the following form, assuming that a class a
obj = a:new ()
obj:dosomething ()
Obj:delete ()
Here we do not consider the operation of the member variable, because it is only for us to manipulate the member function, and considering that the encapsulation principle does not recommend direct manipulation of class member variables, if we want to operate we will define the relevant member functions in the class.
3. Design Ideas
Referring to the above representations, we will set the a to a metatable , Then the correlation function is associated with it, is the new Functions such as are changed lua_pushfunction A function that metatable the . For obj , he is an instance of a a , and we will pass its pointer to the The lua for lua is used, and this pointer value is in the lua inside is used userdata to represent. Since only the operation of the function is involved, we will put the metatable __index The is associated with a related handler function.
In addition, suppose a inherits from class b , and obj to a An instance of , then obj 's metatable to a 's metatable , and a 's metatable 's metatable to b 's metatable , which guarantees inheritance. We invoke the function through the obj obj first in a Find, if a inside, a looks in b .
based on the above idea, we first define the function:
int reg_type (lua_state *l, const char *name);
This function registers our class in Lua , which is to take our class as a metatable, the code is as follows :
//registry type to lua
int reg_type (lua_state *l, const char *name)
{
/ / Create metatable, if successful return 1, failure return 0
int r = lual_newmetatable (L, name);
if ( R)
{
/ / Register the metatable as a global
lua_pushstring (L, name );
Lua_pushvalue (L, 1 );
lua_settable (L, Lua_globalsindex );
}
if ( R)
{
classevent (L );
}
Lua_pop (L, 1 );
return R;
}
for classevent (), we implement the following :
void classevent (lua_state *l)
{
lua_pushstring ( L,"__index" );
lua_pushcfunction (l,class_index_event );
Lua_rawset (l,-3 );
}
This is the __index domain of the associated metatable , and all of our operations are converted to function class_index_event .
int class_index_event (lua_state *l)
{
int t = lua_type (l,1);
if (t = = lua_tuserdata)
{
// Here we only consider the function type, so there are no many other treatments for simplicity
/ / we will get metatable, and then find the corresponding function of the metatable , make a call
lua_getmetatable (L, 1 );
Lua_pushvalue (L, 2 );
Lua_rawget (L,-2 );
if (!lua_isnil (L,-1))
{
return 1;
}
}
return 1;
}
We also provide a function to set the inheritance relationship.
int reg_class (lua_state *l, const char *name, const char< /c16> *base)
{
lual_getmetatable (L, name ); //STACK:MT
/ / If there is a base class, we will use the base class as the metatable of the class metatable
if (base && *base)
{
lual_getmetatable (L, base ); //stack:mt basemt
lua_setmetatable (L,-2 ); //STACK:MT
}
Lua_pop (L, 1 );
return 0;
}
A function that registers a function to associate a function with a specific metatable:
int reg_function (lua_state* L, const char * type, const Char* n Ame, lua_cfunction func)
{
lual_getmetatable (L, type );
lua_pushstring (L, name );
lua_pushcfunction (L, func );
Lua_rawset (L,-3 );
Lua_pop (L, 1 );
return 0;
}
RegisteredUserDatafunction, where we have to be clear, for examples of our custom types, such asobj, we pass the pointer to theLua, this is actually aLight UserData, while inLuaInsideLight UserDatadoes not haveMetaMachine , but we have to set the type ofMetaFor classAOfMetaSo that we're going to implement the package, and we put thisobj First assign a value to aFull UserDataInLuaInsideFull UserDataHasMetaProperty and then store it in a registeredTableInside, so we're not actually returning this.objThe pointer, instead of wrapping up thisobjof aFull userdata Pointer. Inlua5.0The inside definesLua_unboxpointerAndLua_boxpointermacros, but somehow5.1There are no, the two macros are as follows:
#define Lua_boxpointer (l,u)
(* (void * *) (Lua_newuserdata (L, sizeof (void *)) = (U))
#define Lua_unboxpointer (l,i) (* (void * *) (Lua_touserdata (L, i))
When we need to remove the obj pointer, we get it through the (* * * *) .
int reg_userdata (lua_state* L, void* value, const char* type)
{
lual_getmetatable (L, type );
lua_pushstring ( L, "Script_ubox" );
lua_rawget (L, Lua_registryindex );
lua_pushlightuserdata (l,value );
lua_rawget (l,-2 ); / * stack:mt ubox ubox[u] * *
if (Lua_isnil (l,-1))
{
Lua_pop (l,1); / * STACK:MT ubox * *
Lua_pushlightuserdata (l,value);
* (void * * *) lua_newuserdata (L, sizeof (void *)) = Valu E / * stack:mt ubox u newud * *
Lua_pushvalue (l,-1); / * stack:mt ubox u newud newud * *
Lua_insert (l,-4); / * STACK:MT newud ubox u newud * *
Lua_rawset (l,-3); / * stack:mt newud ubox * *
Lua_pop (l,1); / * STACK:MT newud * *
Lua_pushvalue (L,-2 ); / * STACK:MT newud MT * *
lua_setmetatable (l,-2); / * STACK:MT newud * *
}
Lua_remove (L,-2 );
return 1;
}
Then we define:
This function simply registers a table for us to operate.
int script_open (lua_state *l)
{
lua_pushstring ( L,"Script_ubox" );
lua_newtable (L );
Lua_rawset (l,lua_registryindex );
return 0;
}
The next step is our implementation :
First, define the registration function for the class and the class member function:
class test
{
Public :
test () {}
~test () {}
void Myprint () {printf ("Test::hello worldn");}
};
int test_new (lua_state *l)
{
Test *t = new test ();
Reg_userdata (L, (void*) T, "test");
return 1;
}
int Test_delete (lua_state *l)
{
Test *t = (test*) (* (void * *) lua_touserdata (L, 1));
delete t;
lua_settop (L, 0 );
return 0;
}
int test_myprint (lua_state *l)
{
Test *t = (test*) (* (void * *) lua_touserdata (L, 1));
t->myprint ();
return 0;
}
and then implement :
Script_open (L);
Reg_type (L, "test");
Reg_class (L, "test", NULL);
reg_function (L, "test", "new", test_new);
reg_function (L, "test", "Delete", test_delete);
reg_function (L, "test", "Myprint", Test_myprint);
So in Lua , we can call this:
obj = test:new ()
Obj:myprint ()
Obj:delete ()
Note that this is just a very simple implementation, with many flaws
First, it registers the class directly into the Globalsindex, which may lead to naming conflicts, followed by a single registration function for each class member function, which is very heavy Although I have written about the method of registering different C types with class member functions in Lua through an agent method, this method does not apply to this implementation. There are many aspects of this example is not considered completely, but simply to import member functions, the function is very limited. See if you can write a better set of binding class things.