您的位置:首页 > 编程语言 > Lua

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

2015-06-28 13:20 796 查看
在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_TNIL		0
#define LUA_TBOOLEAN		1
#define LUA_TLIGHTUSERDATA	2
#define LUA_TNUMBER		3
#define LUA_TSTRING		4
#define LUA_TTABLE		5
#define LUA_TFUNCTION		6
#define LUA_TUSERDATA		7
#define LUA_TTHREAD		8


内置类型的实现,是通过值与类型相绑定来实现的。值是如何表示的,在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"]=WeekEnumSizeTable
void 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名称来获取类型Id
local 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)


运行结果如下:



本文这样就结束了。抛装引玉,如果不对的地方,请支持来,大家共享知识。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: