本篇仅简单介绍使用lua_tinker让lua调用C++函数的过程, C++调用lua函数可以参见博客Lua脚本和C++交互(一).

未完待续.

(一) lua调用C++全局函数

这里以lua_tinker自带的sample1作为分析例子.C++源码如下:

// sample1.cpp
#include "stdafx.h"
#include "../lua_tinker/lua_tinker.h" int cpp_func(int arg1, int arg2)
{
return arg1 + arg2;
} int _tmain(int argc, _TCHAR* argv[])
{
lua_State* L = lua_open();
luaopen_base(L);
lua_tinker::def(L, "cpp_func", cpp_func);
lua_tinker::dofile(L, "sample1.lua");
int result = lua_tinker::call<int>(L, "lua_func", , );
printf("lua_func(3,4) = %d\n", result);
lua_close(L); return ;
}

lua文件源码如下:

-- sample1.lua
result = cpp_func(, ) print("cpp_func(1,2) = "..result) function lua_func(arg1, arg2)
return arg1 + arg2
end

从main函数开始分析. 首先通过lua_open获取lua栈对象,再打开lua库(luaopen_base). 接下来,lua_tinker::def注册一个C++全局函数,lua_tinker::dofile打开lua文件,通过lua_tinker::call调用到sample1.lua里面的lua函数.最后关闭释放lua栈对象(lua_close).本文重点在于探究ua_tinker::def如何完成C++全局函数的注册.

(1) lua_tinker只有一个.h和.cpp文件,在lua_tinker.h里面,我们找到了上面的函数调用,如下:

// global function
// lua_tinker::def(L, "cpp_func", cpp_func);
template<typename F>
void def(lua_State* L, const char* name, F func)
{
lua_pushstring(L, name);
lua_pushlightuserdata(L, func);
push_functor(L, func);
lua_settable(L, LUA_GLOBALSINDEX);
}

在代码中,我们可以看到,首先将全局函数名称"cpp_func"压入lua栈(), 此时的lua栈只有这一个元素且为栈顶, 然后我们再将全局函数对应的函数指针压入栈(查lua_reference可以知道,如果是light userdata(而不是full userdata)的话,lua_pushlightuserdata只是简单地压入函数指针), 此时的lua栈有两个元素,栈顶为func函数指针,第2个元素是我们刚才压入的全局函数名称;第3步的push_functor是lua_tinker对压入cclosure(c闭包)函数lua_pushcclosure的一个封装,查看此函数:

template<typename RVal, typename T1, typename T2>
void push_functor(lua_State *L, RVal (*func)(T1,T2))
{
lua_pushcclosure(L, functor<T1,T2>::invoke<RVal>, );
}

这里涉及到模板函数的重载,模板相关知识参见系列博客C++ template —— 模板基础(一).这里不做过多阐述.

上面我们的全局函数cpp_func是一个接受两个参数且有一个返回值的函数,所以我们找到了如上的模板函数, 从代码中可以看到,push_functor()只是简单地调用lua函数lua_pushcclosure,但是对参数做了一层封装.lua_pushcclosure接受三个参数,第2个参数表示要压入的c闭包函数体(此处为functor结构体的invoke函数),第3个参数表示此c闭包关联的upvalue个数(最大255).本例中只有一个(会获取到上面通过lua_pushlightuserdata压入的函数指针), lua_pushcclosure压入c闭包,同时会把所有的upvalue(这里只有函数指针)全都pop出栈.此时的lua栈只有两个元素,栈顶也就是刚刚压入的cclosure,下面一个元素是函数名称. 接下来,我们再看看functor如何封装此函数指针:

template<typename T1, typename T2>
struct functor<T1,T2>
{
template<typename RVal>
static int invoke(lua_State *L) { push(L,upvalue_<RVal(*)(T1,T2)>(L)(read<T1>(L,),read<T2>(L,))); return ; }
template<>
static int invoke<void>(lua_State *L) { upvalue_<void(*)(T1,T2)>(L)(read<T1>(L,),read<T2>(L,)); return ; }
};

这里涉及到的是类(结构体)模板和类模板偏特化的内容,这里也不做过多阐述,感兴趣同样可以参见上面模板系列博客.

根据类模板偏特化,我们找到了上面接收两个模板参数的对应functor代码,可以看到,invoke执行的操作就是将全局函数压入栈. upvalue_获取闭包的upvalue,这里是全局函数cpp_func的指针,再通过read指定(cpp_func全局函数所需要的)两个参数,最后通过push压入lua栈.upvalue_代码如下:

// get value from cclosure
template<typename T>
T upvalue_(lua_State *L)
{
return user2type<T>::invoke(L, lua_upvalueindex());
}

lua_upvalueindex(1)获取cclosure中的第1个upvalue,也即全局函数地址.

最后,通过lua_settable(L, LUA_GLOBALSINDEX);找到全局表索引位置的全局表t,并以栈顶为value(cclosure),下一个元素为key(全局函数名称),设置t[key] = value;将封装过的元素函数指针设置进lua全局表里面,这样,在lua文件sample1.lua中,就可以直接通过全局函数名称"cpp_func"调用到C++的函数了.

本篇博文为个人分析,如发现错误,请留言指出,共同进步,谢谢!

(二) lua调用C++ 的非全局函数

[参考]:lua 函数调用1 -- 闭包详解和C调用

lua_tinker源码笔记1的更多相关文章

  1. Zepto源码笔记(一)

    最近在研究Zepto的源码,这是第一篇分析,欢迎大家继续关注,第一次写源码笔记,希望大家多指点指点,第一篇文章由于首次分析原因不会有太多干货,希望后面的文章能成为各位大大心目中的干货. Zepto是一 ...

  2. redis源码笔记(一) —— 从redis的启动到command的分发

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载联系作者并保留声明头部与原文链接https://luzeshu.com/blog/redis1 本博客同步在http://www.cnblog ...

  3. AsyncTask源码笔记

    AsyncTask源码笔记 AsyncTask在注释中建议只用来做短时间的异步操作,也就是只有几秒的操作:如果是长时间的操作,建议还是使用java.util.concurrent包中的工具类,例如Ex ...

  4. Java Arrays 源码 笔记

    Arrays.java是Java中用来操作数组的类.使用这个工具类可以减少平常很多的工作量.了解其实现,可以避免一些错误的用法. 它提供的操作包括: 排序 sort 查找 binarySearch() ...

  5. Tomcat8源码笔记(八)明白Tomcat怎么部署webapps下项目

    以前没想过这么个问题:Tomcat怎么处理webapps下项目,并且我访问浏览器ip: port/项目名/请求路径,以SSM为例,Tomcat怎么就能将请求找到项目呢,项目还是个文件夹类型的? Tom ...

  6. Tomcat8源码笔记(七)组件启动Server Service Engine Host启动

    一.Tomcat启动的入口 Tomcat初始化简单流程前面博客介绍了一遍,组件除了StandardHost都有博客,欢迎大家指文中错误.Tomcat启动类是Bootstrap,而启动容器启动入口位于 ...

  7. Tomcat8源码笔记(六)连接器Connector分析

    根据 Tomcat8源码笔记(五)组件Container分析 前文分析,StandardService的初始化重心由 StandardEngine转移到了Connector的初始化,本篇记录下Conn ...

  8. Tomcat8源码笔记(五)组件Container分析

    Tomcat8源码笔记(四)Server和Service初始化 介绍过Tomcat中Service的初始化 最先初始化就是Container,而Container初始化过程是咋样的? 说到Contai ...

  9. Tomcat8源码笔记(四)Server和Service初始化

    上一章 简单说明下Tomcat各个组件: Server:服务器,Tomcat服务器,一个Tomcat只有一个Server组件; Service:业务层,是Server下最大的子容器,一个Server可 ...

随机推荐

  1. how to generate an analog output from a in-built pwm of Atmega 32AVR microcontrloller?

    how to generate an analog output from a in-built pwm of Atmega 32AVR microcontrloller? you need a re ...

  2. 用.Net打造一个移动客户端(Android/IOS)的服务端框架NHM(四)——Android端Http访问类(转)

    本章目的 在上一章中,我们利用Hibernate Tools完成了Android Model层的建立,依赖Hibernate Tools的强大功能,自动生成了Model层.在本章,我们将继续我们的项目 ...

  3. (转)Fur Shader

    转自:http://qiankanglai.me/misc/2014/11/15/fur-shader/ 花时间看了下毛发效果,苦于囊中羞涩没能买QuickFur.furFX等插件,最后找到了Fur ...

  4. SVN的Branch和Tag管理

    dev:开发主线 branch: 部分特殊客户的定制化版本 tag: 主线的某个发布版本 release: 主线的里程碑式的发布版本(相比上一里程碑版本,改动非常大,并且当前已经很稳定的) 你可以在b ...

  5. PL/SQL学习笔记之变量、常量、字面量、字符串

    一:变量 1:变量声明与初始化 variable_name datatype(约束) [:= | DEFAULT 初始值] 如: sales , ); name ); a ; greetings ) ...

  6. Bullet物理引擎的安装与使用

    图形赋予游戏一种视觉的吸引力,但是能够让游戏的世界鲜活起来的还应该是内部的物理引擎.物理引擎是游戏引擎中的子模块,是一种软件组件,可仿真物理系统.它根据牛顿力学定律,计算游戏中物体的合理的物理位置,并 ...

  7. 关于dealloc 注意事项

    以下讨论在 MRC 下. 1,不要在init和dealloc函数中使用accessor Don’t Use Accessor Methods in Initializer Methods and de ...

  8. [docker]docker4种网络最佳实战

    参考: http://hicu.be/docker-container-network-types docker默认3中网络类型 参考: https://docs.docker.com/engine/ ...

  9. 【Tomcat】Tomcat 系统架构与设计模式,第 1 部分: 工作原理

    这个分为两个部分的系列文章将研究 Apache Tomcat 的系统架构以及其运用的很多经典设计模式.本文是第 1 部分,将主要从 Tomcat 如何分发请求.如何处理多用户同时请求,还有它的多级容器 ...

  10. zookeeper 入门(一)

    1 下载安装 wget http://mirrors.hust.edu.cn/apache/zookeeper/stable/zookeeper-3.4.6.tar.gzcp zookeeper-3. ...