lua中如何加入第三方拓展c库

本文探讨Lua中伪随机数生成的问题,并介绍如何利用C语言扩展Lua功能以提高随机数生成的质量。文章分析了现有random函数的局限性,并推荐使用梅森旋转算法替代。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景及需求

lua作为一门发展成熟的脚本语言,正在变得越来越流行。它也可以作为和C/C++执行脚本交互的语言。并且Lua的整个库很小,当Lua自身带有的部分库无法满足我们自身需要实现的功能时,我们就需要引入其他人写的第三方c库来对已有的Lua功能进行扩展和优化。或者另一种情况,一些为了提高效率的功能。

这里我需要一个问题就是关于lua中产生伪随机数的问题。当我们用已有的random产生伪随机数的话,通常我们将时间设置为该函数的随机种子。但是这会有个问题,同样的随机种子在下一次产生的随机数是一样一样的。比如在一个for循环中想要产生很多随机数,因为for循环执行时间很短,导致随机种子也相同,出来的随机数会有很大可能会是相同的。那么就需要找到另外的更好的产生伪随机数的算法。比如梅森旋转算法,简介看这里点击打开链接 ,而在lua的第三方库中已经有同学实现了它,叫lrandom点击打开链接.如果你想要在自己本地测试一下这个lrandom库的话,可以选用一个很方便的Lua 的模块安装和部署工具 - LuaRocks,很开心这个的使用方法有同学详细描述了它点击打开链接 .当以上做过之后,你仅仅可以在自己本地来使用这个模块,而无法在自己工程中使用。


将第三方c库添加到工程中(lua调用c/c++)

那么先以lmathlib.c为例,这个数学库。

static const luaL_Reg mathlib[] = {
  {"abs",   math_abs},
  {"acos",  math_acos},
  {"asin",  math_asin},
  {"atan2", math_atan2},
  {"atan",  math_atan},
  {"ceil",  math_ceil},
  {"cosh",   math_cosh},
  {"cos",   math_cos},
  {"deg",   math_deg},
  {"exp",   math_exp},
  {"floor", math_floor},
  {"fmod",   math_fmod},
  {"frexp", math_frexp},
  {"ldexp", math_ldexp},
#if defined(LUA_COMPAT_LOG10)
  {"log10", math_log10},
#endif
  {"log",   math_log},
  {"max",   math_max},
  {"min",   math_min},
  {"modf",   math_modf},
  {"pow",   math_pow},
  {"rad",   math_rad},
  {"random",     math_random},
  {"randomseed", math_randomseed},
  {"sinh",   math_sinh},
  {"sin",   math_sin},
  {"sqrt",  math_sqrt},
  {"tanh",   math_tanh},
  {"tan",   math_tan},
  {NULL, NULL}
};
这里是关于所有的这个库中需要使用的函数的数组。将luaL_Reg这个结构体展开便可得知这个数组中每个元素的含义了。

<strong><span style="font-size:18px;">typedef struct luaL_Reg {
  const char *name;
  lua_CFunction func;
} luaL_Reg;</span></strong>
再将lua_CFunction展开看下

<strong><span style="font-size:18px;">typedef int (*lua_CFunction) (lua_State *L);</span></strong>
(这个是定义在lua.h中的,这个头文件中还同时定义了其他函数指针,)
这个就定义了所有的用这种方式的函数的定义规则比如看下求sin值。
<strong><span style="font-size:18px;">static int math_sin (lua_State *L) {
  lua_pushnumber(L, l_tg(sin)(luaL_checknumber(L, 1)));
  return 1;
}</span></strong>

在lua中传入的参数是如何传给c代码的那?都是通过类似的函数LuaL_checkXXXX来从栈中取得。将返回值Push到栈中,函数返回值是返回值的个数。

而大量的luaL_XXX这种命名规则,是代表此函数不是LUA的核心代码。

再来看这个.c文件中的另一段代码

<strong><span style="font-size:18px;">/*
** Open math library
*/
LUAMOD_API int luaopen_math (lua_State *L) {
  luaL_newlib(L, mathlib);
  lua_pushnumber(L, PI);
  lua_setfield(L, -2, "pi");
  lua_pushnumber(L, HUGE_VAL);
  lua_setfield(L, -2, "huge");
  return 1;
}</span></strong>

这个是将该函数作为一个模块放在一个table中。里面有函数地址等等。LUAMOD_API就是extern 供其他文件直接引用.

我们要加入的lrandom库实际跟这个数学库结构是一样的。

另一个重要文件linit.c

<strong><span style="font-size:18px;">/*
** these libs are loaded by lua.c and are readily available to any Lua
** program
*/
static const luaL_Reg loadedlibs[] = {
  {"_G", luaopen_base},
  {LUA_LOADLIBNAME, luaopen_package},
  {LUA_COLIBNAME, luaopen_coroutine},
  {LUA_TABLIBNAME, luaopen_table},
  {LUA_IOLIBNAME, luaopen_io},
  {LUA_OSLIBNAME, luaopen_os},
  {LUA_STRLIBNAME, luaopen_string},
  {LUA_BITLIBNAME, luaopen_bit32},
  {LUA_MATHLIBNAME, luaopen_math},
  {LUA_DBLIBNAME, luaopen_debug},
  {LUA_RANLIBNAME, luaopen_random},
  {NULL, NULL}
};


/*
** these libs are preloaded and must be required before used
*/
static const luaL_Reg preloadedlibs[] = {
  {NULL, NULL}
};</span></strong>
这两个数组放在一起看。首先loadLibs  LuaL_Reg是之前看到的结构体。以math库为例:
<strong><span style="font-size:18px;">#define LUA_MATHLIBNAME	"math"
LUAMOD_API int (luaopen_math) (lua_State *L);</span></strong>
该宏定义在lualib.h中  而这里的名字就是我们要再lua中使用的模块名。 下面是用来外链函数的。刚才看到的以luaopen为前缀的到处API.

<strong><span style="font-size:18px;">LUALIB_API void luaL_openlibs (lua_State *L) {
  const luaL_Reg *lib;
  /* call open functions from 'loadedlibs' and set results to global table */
  for (lib = loadedlibs; lib->func; lib++) {
    luaL_requiref(L, lib->name, lib->func, 1);
    lua_pop(L, 1);  /* remove lib */
  }
  /* add open functions from 'preloadedlibs' into 'package.preload' table */
  luaL_getsubtable(L, LUA_REGISTRYINDEX, "_PRELOAD");
  for (lib = preloadedlibs; lib->func; lib++) {
    lua_pushcfunction(L, lib->func);
    lua_setfield(L, -2, lib->name);
  }
  lua_pop(L, 1);  /* remove _PRELOAD table */
}</span></strong>
Opens all standard Lua libraries into the given state
将类库都加载到虚拟机中。这里分了两部分,分别用到了上面两个数组。可以看出perloadedLibs是在程序刚起来的时候就将模块进行预定义,在lua中需要用到的时候进行require,而loadedlibs则对相应模块进行加载,而不需要在lua中再进行require而直接使用。

从这里的循环也可以看出为什么之前数组中都是以NULL结尾的。

另一个问题是require是怎么work的。由于自己水平有限只能了解它的功能,它会根据模块名去查找这个table,然后判断是否已经加载过了该文件,找到后加载该模块。源码在loadlib.c中得 ll_require,具体的技术细节,可以参考下面仁兄的讲解:点击打开链接  ,

点击打开链接

点击打开链接 


这里只是简单地介绍了操作步骤和部分原理。这位同学提供了个完整的例子来提供参考。点击打开链接 

而具体整体的一个技术细节,觉得这位同学写的很详尽。点击打开链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值