Lua 与C/C++ 交互系列:动态注册枚举enum到Lua Code中,在运行时在Lua Code中获取内省信息

来源:互联网 发布:抢车位怎么恢复数据 编辑:程序博客网 时间:2024/06/05 17:25



在Lua 5.1 Reference Manual  对于Lua 值和类型的介绍。Lua是一个动态语言,在Lua中变量仅仅有值而没有类型。所以在Lua中的变量不需要声明。所以的值本身包含类型。
其实Lua 包含一种运行时类型识别,通过type()函数,可以在运行时获取值的类型。

信息来自: Lua 5.1 Reference Manual  Values and Types

Lua is a dynamically typed language. This means that variables do not have types; only values do. There are no type definitions in the language. All values carry their own type. 

type (variables)
Returns the type of its only argument, coded as a string. The possible results of this function are "nil" (a string, not the value nil), "number", "string", "boolean", "table", "function", "thread", and "userdata". 

在C语言特征本身,不提供运行时信息。C语言的拓展集,C++语言特征本身对运行时提供支持。在C++语言中通过typeid(),dynamic_case()等函数可以获取类型的内省信息。

在Java语言中,对内省信息支持强大,spring 等库就是通过内省信息来实现的强大库。在actionscript3.0中也提供了对类的内省信息。在游戏开发中,可以利用内省信息反射出类对象,包括游戏UI编辑器都是通过内省类信息来实现的。C# 是在C++,Java语言发展而来,同时也对运行时内省提供强大支持。对于这些语言的内省信息不详细赘述。


在Python脚本语言中,PyObject 类 实现机制与Lua lua_TValue 实现机制类型,都是通过类型值int 与值相绑定. 虽然,在python 和lua语言中,只存在值,不存在类型,但是通过语言提供的函数,可以在运行时获取到值的类型。

在Lua源代码中,节选出部分与运行时信息相关的代码:

在Lua源代码中提供了8中基本内置类型,随着Lua演化,Lua的基本内置类型变化不大。以后的文章中,就详细介绍Lua类型的演化历史。

/*** basic types*/#define LUA_TNONE(-1)#define LUA_TNIL0#define LUA_TBOOLEAN1#define LUA_TLIGHTUSERDATA2#define LUA_TNUMBER3#define LUA_TSTRING4#define LUA_TTABLE5#define LUA_TFUNCTION6#define LUA_TUSERDATA7#define LUA_TTHREAD8

内置类型的实现,是通过值与类型相绑定来实现的。值是如何表示的,在Lua 作者的5.0实现介绍中有详细解释。

typedef struct lua_TValue {  Value value_; //用来标示Lua的值类型,在lua作者5.0实现中有详细的介绍  int tt_; //用来标示类型,-1 -8 由上面宏定义} TValue;union Value {  GCObject *gc;    /* collectable objects */  void *p;         /* light userdata */  int b;           /* booleans */  lua_CFunction f; /* light C functions */  numfield         /* numbers */};
在Lua Code 运行时中,可以通过type()来获取值的类型:

type (v)
Returns the type of its only argument, coded as a string. 
The possible results of this function are "nil" (a string, not the value nil), "number", "string", "boolean", "table", "function", "thread", and "userdata". 

注意:lightuserdata 和 full userdata的类型都是 userdata.

type(v)在lua中关键源代码如下:

static int luaB_type (lua_State *L) {  luaL_checkany(L, 1);  lua_pushstring(L, luaL_typename(L, 1));  return 1;}/* Macros to access values */ //获取与值绑定的类型#define ttype(o)((o)->tt)//通过数组来返回对应的字符串类型#define ttypename(x)luaT_typenames_[(x) + 1]LUAI_DDEF const char *const luaT_typenames_[LUA_TOTALTAGS] = {  "no value",  "nil", "boolean", udatatypename, "number",  "string", "table", "function", udatatypename, "thread",  "proto", "upval"  /* these last two cases are used for tests only */};


本文参考了LuaAutoC 开源项目,并节选部分代码,修改而来。本文主要展示把C 语言 struct  ,enum等结构注册到Lua Code中,并可以在Lua Code中获取注册类型的运行时信息。

通过lua_pushinteger(L, 0); lua_setfield(L, LUA_GLOBALSINDEX, prefix"type_index"); 来管理生成唯一的类型ID,并存储在全局变量中,默认值为0 .
把所有类型都在全局变量表中注册,在Lua Code中就可以获取相关信息,在Lua Code中就可以内省C语言的自定义类型。
本文的核心通过一个全局唯一ID 和Lua Table本身的Hash系统来实现。代码很简单。上菜。

luademo.cpp :

extern "C" {#include "lua.h"#include "lauxlib.h"#include "lualib.h"}#define prefix "prefix_"//作者orangeduck 在他的博客中认为在动态语言中内省机制是核心。同时Lua 也是通过Type_ID来标示类型的。这与作者不谋而合。//Type_ID是通过运行时动态生成,默认从零开始。可以在运行时获取对象的类型ID.//LuaAutoC 已经在orangeduck 作者的项目中成功的应用。void luaA_open(lua_State* L) {  //用来在运行时动态生成类型的唯一ID,默认开始值为零. 在全局变量表设置  prefix "type_index"的值默认为1,用来标记所有类型.  lua_pushinteger(L, 0); lua_setfield(L, LUA_GLOBALSINDEX, prefix"type_index");  //在全局变量表,prefix "type_ids" 对应Table结构.称作idTable .名称-Id  lua_newtable(L); lua_setfield(L, LUA_GLOBALSINDEX, prefix"type_ids");  //在全局变量表,prefix "type_names"对应Table结构.称作nameTable.名称-名称   lua_newtable(L); lua_setfield(L, LUA_GLOBALSINDEX, prefix"type_names");  //在全局变量表, prefix "type_sizes"对应Table结构.称作sizeTable.名称-大小  lua_newtable(L); lua_setfield(L, LUA_GLOBALSINDEX, prefix"type_sizes");       //与上面类型,在注册表中注册enumsTable,enum_sizesTable,enums_valuesTable用来存储c语言中的枚举值  //这些作为本例中的核心系统。可以在运行时识别C语言用户自定义struct结构的内省信息。  lua_newtable(L); lua_setfield(L, LUA_GLOBALSINDEX, prefix"enums");  lua_newtable(L); lua_setfield(L, LUA_GLOBALSINDEX, prefix"enums_sizes");  lua_newtable(L); lua_setfield(L, LUA_GLOBALSINDEX, prefix"enums_values"); } //用来演示本例子的枚举 enum Week{Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday,};typedef lua_Integer luaA_Type;// 在全局变量表中管理所有注册的类型,可以在运行时通过名字获取内省信息//在全局环境中type_idsTable 和type_namesTable 以及type_values注册类型。//如果全局环境表中已经存在该类型则直接返回类型ID,否则注册新类型///////////////////////////////////////////////////////////////////// 以Week枚举举例:// _G[prefix_type_ids]["Week"]=WeekId// _G[prefix_type_names][WeekId]=WeekName// _G[prefix_type_sizes][WeekId]=WeekSize//////////////////////////////////////////////////////////////////luaA_Type luaA_type_add(lua_State* L, const char* type, size_t size) {  //获取在全局环境表中idTable,类型名称=ID  lua_getfield(L, LUA_GLOBALSINDEX, prefix"type_ids");  //在idTable中获取type_name是否存在  lua_getfield(L, -1, type);  //判断是否存在  if (lua_isnumber(L, -1)) {    //如果已经存在,则返回类型ID    luaA_Type id = lua_tointeger(L, -1);    lua_pop(L, 2);    return id;    }   else   {    //弹出idTable 和类型关联的ID    lua_pop(L, 2);    //获取自动生成的唯一索引,在运行时自动生成.    lua_getfield(L, LUA_GLOBALSINDEX, prefix"type_index");    //转换成整型    luaA_Type id = lua_tointeger(L, -1);//清空栈顶    lua_pop(L, 1);//自动生成下一个唯一索引    id++;//压入到虚拟栈    lua_pushinteger(L, id);//把生成的唯一索引设置成ID,并出栈    lua_setfield(L, LUA_GLOBALSINDEX, prefix"type_index");    //获取idTable    lua_getfield(L, LUA_GLOBALSINDEX, prefix"type_ids");//把自动生成的唯一ID压入栈中    lua_pushinteger(L, id);// 索引[键]=栈顶   设置idTable[type]=id    lua_setfield(L, -2, type);    lua_pop(L, 1);    //获取类型nameTable    lua_getfield(L, LUA_GLOBALSINDEX, prefix"type_names");//把唯一ID入栈    lua_pushinteger(L, id);//把类型name压入栈    lua_pushstring(L, type);//设置 索引[below 栈顶]=栈顶   nameTable[id]=type    lua_settable(L, -3);    lua_pop(L, 1);      lua_getfield(L, LUA_GLOBALSINDEX, prefix"type_sizes");    lua_pushinteger(L, id);    lua_pushinteger(L, size);    lua_settable(L, -3);    lua_pop(L, 1);    return id;      }}//注册enum类型,用来存储enum每一个枚举值// 以Week枚举举例:// _G[prefix_enums]["WeekId"]=WeekEnumTable// _G[prefix_enums_values]["WeekId"]=WeekEnumValueTable// _G[prefix_enums_sizes]["WeekId"]=WeekEnumSizeTablevoid luaA_enum_type(lua_State *L, luaA_Type type, size_t size){  lua_getfield(L, LUA_GLOBALSINDEX, prefix"enums");  lua_pushinteger(L, type);  lua_newtable(L);  lua_settable(L, -3);  lua_pop(L, 1);  lua_getfield(L, LUA_GLOBALSINDEX, prefix"enums_values");  lua_pushinteger(L, type);  lua_newtable(L);  lua_settable(L, -3);  lua_pop(L, 1);  //enum_sizesTable[name]  lua_getfield(L, LUA_GLOBALSINDEX, prefix"enums_sizes");  lua_pushinteger(L, type);  lua_pushinteger(L, size);  lua_settable(L, -3);  lua_pop(L, 1);}//实际注册enum枚举中每一个值到注册表中//注册enum类型,用来存储enum每一个枚举值// 以Week枚举举例:// _G[prefix_enums]["WeekId"]=WeekEnumTable// _G[prefix_enums_values]["WeekId"]=WeekEnumValueTable// _G[prefix_enums_sizes]["WeekId"]=WeekEnumSizeTable////// _G[prefix_enums_sizes]["WeekId"]["Monday"]=Monday//void luaA_enum_value_type(lua_State *L, luaA_Type type, int lvalue, const char* name) {  //获取 _G[prefix_enums]["WeekId"]=WeekEnumTable  lua_getfield(L, LUA_GLOBALSINDEX, prefix"enums");  lua_pushinteger(L, type);  lua_gettable(L, -2);  if (!lua_isnil(L, -1))   {    //存储enum大小    lua_getfield(L, LUA_GLOBALSINDEX, prefix"enums_sizes");    lua_pushinteger(L, type);    lua_gettable(L, -2);    size_t size = lua_tointeger(L, -1);    lua_pop(L, 2);        lua_pushstring(L, name);    lua_pushinteger(L, lvalue);    lua_settable(L,-3);    lua_getfield(L, LUA_GLOBALSINDEX, prefix"enums_values");    lua_pushinteger(L, type);    lua_gettable(L, -2);size_t v =0;if (!lua_isnil(L, -1)) { v =lua_tonumber(L,-1);}    lua_pushinteger(L, v);    lua_getfield(L, -4, name);    lua_settable(L, -3);    lua_pop(L, 4);    return;  }  lua_pop(L, 2);  lua_pushfstring(L, "luaA_enum_value: Enum '%s' not registered!", luaA_typename(L, type));  lua_error(L);}//实际注册enum枚举中每一个值到注册表中//注册enum类型,用来存储enum每一个枚举值// 以Week枚举举例:// _G[prefix_enums]["WeekId"]=WeekEnumTable// _G[prefix_enums_values]["WeekId"]=WeekEnumValueTable// _G[prefix_enums_sizes]["WeekId"]=WeekEnumSizeTable////// _G[prefix_enums_sizes]["WeekId"]["Monday"]=Monday//void luaA_enum_value_type(lua_State *L, luaA_Type type, int lvalue, const char* name) {  //获取 _G[prefix_enums]["WeekId"]=WeekEnumTable  lua_getfield(L, LUA_GLOBALSINDEX, prefix"enums");  lua_pushinteger(L, type);  lua_gettable(L, -2);  if (!lua_isnil(L, -1))   {    //存储enum大小    lua_getfield(L, LUA_GLOBALSINDEX, prefix"enums_sizes");    lua_pushinteger(L, type);    lua_gettable(L, -2);    size_t size = lua_tointeger(L, -1);    lua_pop(L, 2);        lua_pushstring(L, name);    lua_pushinteger(L, lvalue);    lua_settable(L,-3);    lua_getfield(L, LUA_GLOBALSINDEX, prefix"enums_values");    lua_pushinteger(L, type);    lua_gettable(L, -2);size_t v =0;if (!lua_isnil(L, -1)) { v =lua_tonumber(L,-1);}    lua_pushinteger(L, v);    lua_getfield(L, -4, name);    lua_settable(L, -3);    lua_pop(L, 4);    return;  }  lua_pop(L, 2);  lua_pushfstring(L, "luaA_enum_value: Enum '%s' not registered!", luaA_typename(L, type));  lua_error(L);}//利用C语言宏,把c语言中的enum struct 等类型作为字符串#define luaA_type(L, type) luaA_type_add(L, #type, sizeof(type))//定义enum类型#define luaA_enum(L, type) luaA_enum_type(L, luaA_type(L, type), sizeof(type))//注册枚举值#define luaA_enum_value(L,enumType,enumValue)  luaA_enum_value_type(L, luaA_type(L,enumType),enumValue,#enumValue) int main(int argc, char **argv){  lua_State *L = lua_open();  /* create state */  luaL_openlibs (L);  luaA_open(L);  //注册枚举类型  luaA_enum(L,Week);  //实际注册enum类型中的每一枚举值  luaA_enum_value(L,Week,Monday);  luaA_enum_value(L,Week,Tuesday);  luaA_enum_value(L,Week,Wednesday);  luaA_enum_value(L,Week,Thursday);  luaA_enum_value(L,Week,Friday);  luaA_enum_value(L,Week,Saturday);  luaA_enum_value(L,Week,Sunday);  luaL_dofile (L, "demo.lua");  lua_close(L);  return 1;}
luademo.lua文件代码:

print("=========")--根据注册的enum名称来获取类型Idlocal WeekId =_G["prefix_type_ids"]["Week"]--获取运行时信息local nameTable =_G["prefix_type_names"][WeekId]local sizeTable =_G["prefix_type_sizes"][WeekId]print(WeekId)print(nameTable)print(sizeTable)local Week= _G["prefix_enums"][WeekId]local WeekValue= _G["prefix_enums_values"][WeekId]local WeekSize = _G["prefix_enums_sizes"][WeekId]print(Week)print(WeekValue)print(WeekSize)--获取注册的枚举值print(Week.Monday)print(Week.Tuesday)print(Week.Wednesday)print(Week.Thursday)print(Week.Friday)print(Week.Saturday)print(Week.Sunday)


运行结果如下:



本文这样就结束了。抛装引玉,如果不对的地方,请支持来,大家共享知识。

0 0
原创粉丝点击