-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
122 lines (122 loc) · 41 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[golang调用c动态库]]></title>
<url>%2F2017%2F09%2F10%2Fgolang%E8%B0%83%E7%94%A8%E5%8A%A8%E6%80%81%E5%BA%93%2F</url>
<content type="text"><![CDATA[golang以其高并发,部署简单,编译迅速,而且像动态语言一样的开发效率等特点,吸引了很多程序的关注,但是有时候为了运行速度需要跟c一起使用,现整理一个golang调用c动态链接库的demo程序,以供自己将来参考,以下文字,大多来源于网络,整理如下。 创建项目 12345678../cgolearn├── lib│ ├── test_so.c│ └── test_so.h└── src ├── load_so.c ├── load_so.h └── main.go 创建动态连接库 lib目录下文件内容如下: 123//test_so.hint test_so_func(int a,int b); 12345678//test_so.c#include "test_so.h"int test_so_func(int a,int b){ return a*b;} 生成so文件 12cd libgcc -shared ./test_so.c -o test_so.so 在golang代码中调用c动态库函数 src目录下文件内容如下: 123//load_so.hint do_test_so_func(int a,int b); 12345678910111213141516//load_so.c#include "load_so.h"#include <dlfcn.h>int do_test_so_func(int a,int b){ void* handle; typedef int (*FPTR)(int,int); handle = dlopen("../lib/test_so.so", 1); FPTR fptr = (FPTR)dlsym(handle, "test_so_func"); int result = (*fptr)(a,b); return result;} 1234567891011121314//main.gopackage main/*#include "load_so.h"#cgo LDFLAGS: -ldl*/import "C"import "fmt"func main() { fmt.Println("20*30=", C.do_test_so_func(20, 30))} 编译运行: 123cd srcgo build -o main./main 注意事项 1,import “C”,一定要紧跟着cgo标志的注释代码,中间不能有空行。 2,import “C”必须单独一行,不能和其它库一起导入。 待做事项 do_test_so_func可以优化成一个工厂函数,从Go代码中传入不同的标志,调用不同的函数,返回不同类型的返回值。或者创建一个函数映射表来调用。]]></content>
<categories>
<category>go</category>
</categories>
<tags>
<tag>go</tag>
<tag>c</tag>
</tags>
</entry>
<entry>
<title><![CDATA[服务端程序员常见面试问题整理]]></title>
<url>%2F2017%2F09%2F05%2F%E6%9C%8D%E5%8A%A1%E7%AB%AF%E7%A8%8B%E5%BA%8F%E5%91%98%E5%B8%B8%E8%A7%81%E9%9D%A2%E8%AF%95%E9%97%AE%E9%A2%98%E6%95%B4%E7%90%86%2F</url>
<content type="text"><![CDATA[程序员的日常工作,就像搭积木,编程语言,数据库,常用开发框架等,都是积木的一块,程序员就是阅读说明书,把这些积木配合起来,做成一个软件,所以程序员经常自嘲为码农。 常见的面试问题,大多都偏理论,就像你会用积木搭建一栋房子,突然有人问你,你知道积木是怎么做的吗?此时一脸懵逼。很多时候,知道有那么回事儿,但是概念并不清晰,现在自己整理下自己遇见的面试问题,希望以后避免懵逼的尴尬。 Python的GIL 在python的原始解释器CPython中存在着GIL(Global Interpreter Lock,全局解释器锁),因此在解释执行python代码时,会产生互斥锁来限制线程对共享资源的访问,直到解释器遇到I/O操作或者操作次数达到一定数目时才会释放GIL。所以,虽然CPython的线程库直接封装了系统的原生线程,但CPython整体作为一个进程,同一时间只会有一个获得GIL的线程在跑,其他线程则处于等待状态。这就造成了即使在多核CPU中,多线程也只是做着分时切换而已。这也是GIL饱受诟病的原因。 Python 不适合开发 CPU 密集型的程序, Python 多线程的意义就是能让每条语句宏观上并发执行。对于 IO 密集型的程序,Python 多线程还是有很大作用的。然而 Python 3 引入的 asyncio 模块使得很多 IO 操作有了更好的方式去解决,这就非常类似 Node.js 了,都是没有多线程,而是采用 Event Loop 来处理耗时的 IO 操作。 muiltprocessing库,也可以让多进程的python代码编写类似多线程。 进程、线程和协程的理解 进程拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度。 线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是的)。 协程和线程一样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。如果使用封装好的,例如gevent等协程库,程序员就不必承担调度的责任。 其他参考: 进程,线程,协程与并行,并发 进程线程协程的区别 TCP与UDP区别总结1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信5、TCP首部开销20字节;UDP的首部开销小,只有8个字节6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道 123456tcp协议和udp协议的差别 TCP UDP 是否连接 面向连接 面向非连接 传输可靠性 可靠 不可靠 应用场合 传输大量数据 少量数据 速度 慢 快 待续]]></content>
<categories>
<category>面试</category>
</categories>
<tags>
<tag>面试</tag>
</tags>
</entry>
<entry>
<title><![CDATA[cocoscreator 1.5.2 热更新 大厅 + 子游戏 不用重启DEMO]]></title>
<url>%2F2017%2F08%2F20%2Fcocoscreator-hall-with-subgames-demo%2F</url>
<content type="text"><![CDATA[cocos论坛关于该话题讨论帖子的地址 大厅和子游戏均为正常cocos creater项目,只是热更新的时候指向的地址和本地保存的目录不一样。 Demo工程Github地址 创建大厅项目 详细逻辑见 hall项目 创建子游戏项目 详细逻辑见 subgame项目 注意:坑1,不要都创建helloworld,可以一个空项目,一个helloworld项目再修改,否则加载子游戏的时候会爆错 Class already exists with the same cid : “280c3rsZJJKnZ9RqbALVwtK”. 导致子游戏按钮点起来没反应! 打开webstorm创建一个express项目命名hotupdate,目的为托管热更新文件,也可以用其他web服务器。在hotupdate项目的public文件夹下创建remote-assets文件夹。 在subgame项目根目录下创建build-template/jsb-binary/src文件夹,新建main.js dating.js文件在此文件夹,具体代码参见对应文件,这样每次构建编译,ccc就会自动帮你拷贝到build文件夹下面对应路径。 用cocoscreater构建,编译subgame。 然后在subgame根目录下,执行命令: node version_generator.js -v 1.0.0 -u http://192.168.2.114:3000/remote-assets/subgame/ -s build/jsb-binary -d remote-assets/ 生成子游戏的热更新文件。 拷贝build/js-binary下生成的res和src文件夹到 romote-assets文件夹。 此时,子游戏的所有热更新文件已经生成完毕。 拷贝subgam项目下romote-assets里面的所有文件到hotupdate项目下的public/romote-assets/subgame文件。 注意:坑2,生成的project.manifest放到public/romote-assets/subgame要改名字,改成跟代码里对应的名字,只要不叫project.manifest就行,例如peision.manifest或则sub_game_project.manifest(此处可以修改version_generator.js脚本逻辑给其重命名,免掉手动修改的麻烦)否则每次重启APP都会清空本地下载的子游戏,导致再次进入不了,具体原因未知。 启动hotupdate,在浏览器打开http://192.168.2.114:3000/remote-assets/subgame/version.manifest如果能看到正常的version.manifest内容说明,服务器搭建成功,热更新文件能正常访问。 此时运行hall,点击下载游戏,下载完成之后,点击进入游戏,点击返回大厅就能返回。到此,大厅加子游戏demo完成。 注意:坑3,在ccc自带的模拟器里面是不能返回大厅的,会报无法加载场景对应的json文件的错误。在xcode的模拟器里面没问题。 注意:build-template里面的main.js 和 dating.js 每个子游戏都应该有一份,需要仔细规划一下,统一代码风格,和项目相关的一些变量。 同时需要注意这俩文件初始化资源目录的地方开头加斜杠和不叫斜杠的区别,前者去找默认的大厅项目资源,后者是去相对目录找资源。]]></content>
<categories>
<category>cocos creator</category>
</categories>
<tags>
<tag>cocos2dx</tag>
<tag>cocos creator</tag>
</tags>
</entry>
<entry>
<title><![CDATA[微信小程序开发环境配置笔记]]></title>
<url>%2F2017%2F01%2F17%2F%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE%E7%AC%94%E8%AE%B0%2F</url>
<content type="text"><![CDATA[微信小程序开发环境配置, Nginx需要开启https, 申请ssl证书 调试时可以直接用内网http://ip:port/api/a/b?args={}&sig=xxxx进行联网 开发时小程序后台配置项目-开发环境不校验请求域以及TLS版本 去掉勾选不验证域名 上传时,需要在微信小程序后台,设置,开发设置添加校验域名,注意:不要带www,例如 https://wjdc.xxx.com 服务器需要搭建https环境 nginx开启https命令行 nginx -V 查看nginx是否开启了–with-http_ssl_module选项,如果没有,需要重新编译带上此选项 申请ssl证书去阿里云申请ssl证书,有免费试用一年的。服务器是阿里云的,推荐DNS类证书,注意对应域名不要带www,例如wjdc.xxx.com,否则会提示服务器被冒充,勾选自动创建主机记录和记录值,审核完毕后下载证书,解压到nginx.conf同级的cert目录下然后修改nginx.conf1234567891011121314151617181920212223242526server { listen 443; server_name wjdc.knightflower.com; charset utf-8; access_log /data/wjdc/logs/https_access.log main; ssl on; ssl_certificate cert/xxx.pem; ssl_certificate_key cert/xxx.key; ssl_session_timeout 5m; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; location / { try_files /_not_exists_ @backend; } location @backend { proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header Host $http_host; proxy_pass http://127.0.0.1:6066; }} 12nginx -t -c /etc/nginx/nginx.conf #校验配置文件是否有错nginx -s reload #重新加载配置文件 然后就可以在小程序里面用wx.request接口请求服务器,url:”https://wjdc.xxx.com/a/q/query“]]></content>
<categories>
<category>小程序</category>
</categories>
<tags>
<tag>nginx</tag>
<tag>https</tag>
</tags>
</entry>
<entry>
<title><![CDATA[cocos2dx 3.13.1 jsbinding 测试记录]]></title>
<url>%2F2016%2F11%2F16%2Fcocos2dx-3-13-1-jsbinding-%E6%B5%8B%E8%AF%95%E8%AE%B0%E5%BD%95%2F</url>
<content type="text"><![CDATA[cocos2dx 3.13.1 jsbinding 测试记录 cocos new -p com.mz.jsbtest -d ./ -l js JSBTest 在cocos2dx/cocos目录新建目录my 增加类文件 123456789101112131415161718192021222324252627//CustomClass.hpp#ifndef CustomClass_hpp#define CustomClass_hpp#include <stdio.h>#include <iostream>#include "cocos2d.h"USING_NS_CC;namespace cocos2d { class CustomClass : public cocos2d::Ref { public: CustomClass(); ~CustomClass(); bool init(); std::string helloMsg(); CREATE_FUNC(CustomClass); };} //namespace cocos2d#endif /* CustomClass_hpp */ 1234567891011121314151617181920//CustomClass.cpp#include "CustomClass.hpp"USING_NS_CC;CustomClass::CustomClass(){ }CustomClass::~CustomClass(){ }bool CustomClass::init(){ return true;}std::string CustomClass::helloMsg() { return "Hello from CustomClass::sayHello";} 在cocos2dx/tools/tojs 目录下新增cocos2dx_custom.ini配置文件用于描述绑定的名字空间,前缀等信息,如果提示错误尽量参考tojs目录下的其他ini文件的格式如cocos2dx_builder.ini,不同cocos2dx版本之间语法貌似有差异,注意这里我们新增的类继承了Ref类,所以base_classes_to_skip = Ref,否则会提示Ref x86_64未定义啥的,如果是纯是自己写的类就不用了 123456789101112131415161718192021222324252627[cocos2dx_custom]name = cocos2dx_customprefix = cocos2dx_customtarget_namespace = ccheaders = %(cocosdir)s/cocos/my/CustomClass.hppclasses = CustomClassandroid_headers = -I%(androidndkdir)s/platforms/android-14/arch-arm/usr/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi-v7a/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.8/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/include -I%(androidndkdir)s/sources/cxx-stl/gnu-libstdc++/4.9/includeandroid_flags = -D_SIZE_T_DEFINED_ clang_headers = -I%(clangllvmdir)s/%(clang_include)s clang_flags = -nostdinc -x c++ -std=c++11 -U __SSE__cocos_headers = -I%(cocosdir)s -I%(cocosdir)s/cocos -I%(cocosdir)s/cocos/editor-support -I%(cocosdir)s/cocos/platform/android -I%(cocosdir)s/externalcocos_flags = -DANDROIDextra_arguments = %(android_headers)s %(clang_headers)s %(cxxgenerator_headers)s %(cocos_headers)s %(android_flags)s %(clang_flags)s %(cocos_flags)s %(extra_flags)s classes_have_type_info = no# base classes which will be skipped when their sub-classes found them.base_classes_to_skip = Ref# Determining whether to use script object(js object) to control the lifecycle of native(cpp) object or the other way around. Supported values are 'yes' or 'no'.script_control_cpp = yes 在tojs目录下,修改执行genbindings.py文件尽量注释掉cmd_args中cocos2dx自带的那些绑定,再加入自己定制的绑定,否则容易出错,就呆重新建项目, 然后执行python genbindings.py也可以加在custom_cmd_args里面会生成到cocos2dx/frameworks/custome/auto,不过目录处理起来比较麻烦 生成的代码在cocos2dx/cocos/scripting/js-bindings/auto下面,加入Xcode中的cocos2d_js_bindings.xcodeprojmy目录加到头文件搜索路径 在AppDelegate.cpp引入生成的头文件 1#include "scripting/js-bindings/auto/jsb_cocos2dx_custom.hpp" 在applicationDidFinishLaunching函数加入 sc->addRegisterCallback(register_all_cocos2dx_custom); 然后就可以在app.js中调用我们绑定的代码了 123var customClass = cc.CustomClass.create();var msg = customClass.helloMsg()cc.log("customClass's msg is : " + msg)]]></content>
<categories>
<category>cocos2dx</category>
</categories>
<tags>
<tag>cocos2dx</tag>
<tag>jsb</tag>
</tags>
</entry>
<entry>
<title><![CDATA[从论坛下载的老版本cocos2dx项目怎么运行]]></title>
<url>%2F2016%2F10%2F19%2F%E4%BB%8E%E8%AE%BA%E5%9D%9B%E4%B8%8B%E8%BD%BD%E7%9A%84%E8%80%81%E7%89%88%E6%9C%ACcocos2dx%E9%A1%B9%E7%9B%AE%E6%80%8E%E4%B9%88%E8%BF%90%E8%A1%8C%2F</url>
<content type="text"><![CDATA[从论坛下载的cocos2dx项目,需要研究其逻辑,或者做二次开发,经常发现不能直接运行,以下是一些经验性的解决方法。 从网上下载的cocos2dx项目运行或升级步骤: 下载xxx.zip文件,解压 若是3.x的可以直接运行,2.x的如果不带库文件不能直接运行,需要放到cocos2dx-2.x.x的projects目录下面运行测试 如果放到projects文件夹下面可以正常运行,则进入如下步骤: 创建SVN目录树 将项目放入trunk目录 将cocos2dx引擎的四个文件夹 coco2dx CocosDenshion extensions external 拷贝到trunk目录 打开 ios项目文件 修改项目目录树里面的cocos2dx.xcodeproj Box2d chipmunk CocosDenshion extensions libwebsocket 指向的上述拷贝过来的对应文件夹 修改项目 build setting-> search paths 改为当前相对目录,如上即使依次删除 header search paths和library search paths 各个路径的“../../” 修改build phases->link binary with libraries 增加libcocos2dx.a 打开mac版项目文件 做上述同样步骤的修改 Android项目修改对应的 Android.mk文件索引对应的代码文件,build_native.sh修改COCOS2DX_ROOT osx系统升级,会遇到cocos2dx 2.x项目 如果遇到1m_pValueDict->setObject( CCString::create( (const char*)glGetString(GL_VENDOR)), "gl.vendor"); 报错 修改 mac项目的 AppController.mm文件的 applicationDidFinishLaunching函数在1glView = [[EAGLView alloc] initWithFrame:rect pixelFormat:pixelFormat]; 下面加入1[glView prepareOpenGL]; 方案一:1234567NSOpenGLContext *ctx = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];if(ctx){ [glView setOpenGLContext:ctx]; [ctx setView:glView]; [ctx makeCurrentContext];} 方案二:1[glView prepareOpenGL];]]></content>
<categories>
<category>cocos2dx</category>
</categories>
<tags>
<tag>cocos2dx</tag>
<tag>oc</tag>
</tags>
</entry>
<entry>
<title><![CDATA[用lua 5.2为c++创建动态链接库]]></title>
<url>%2F2014%2F09%2F07%2FCREATING-A-LUA-5-2-DYNAMIC-LINK-LIBRARY-OF-Cpp-FUNCTIONS%2F</url>
<content type="text"><![CDATA[原文链接:http://www.acamara.es/blog/2012/08/creating-a-lua-5-2-dynamic-link-library-of-c-functions/ 我的译文如下: 欢迎惠阅我的第四篇c++和Lua5.2混编的学习笔记。如果你还没读过我的前三篇文章,敬请移步如何在c++程序中运行lua脚本, 从lua脚本中向c++传递变量, 在lua中调用c++函数。 本篇新教程将讲述如何创建c动态链接库,以及如何在Lua5.2中使用它们。我将假设你已经懂得怎么创建动态链接库。本文代码将在通过Mac osx10.8.4, gcc4.8进行编译。你可以略做修改转换编译器,然后运行到其他系统。文中大多引述均来自lua用户WIKI和lua5.2的用户手册。 本文目的假设你已经有一些有用的并且能和Lua5.2适配的c或者c++函数(记住必须是lua_CFunction类型的函数哦)。为了重用代码,你希望将它们打包成库,然后再需要的时候动态链接。就如同你使用Lua标准库一样。Lua标准库中有io, string, table等等,在需要的时候可以加载它们。 本文,我们将学习怎样创建C语言动态链接库,并在Lua中调用它们。 由于本文使用的c++代码基本与前文一致,所以就省略不写了。但是会展示简短的Lua代码以便更好的理解程序的输出结果,以及将要创建C语言动态链接库的c++代码。你可以从个链接下载这三部分代码。 Lua脚本部分Lua脚本将动态的链接我们的新的库,其名字为cfunctions。1234567891011-- load the c-function libraryio.write("[Lua] Loading the C-function libraryn")cfunctions = require("cfunctions")-- use some of the functions defined in the libraryio.write("[Lua] Executing the C-library functionsn");local f = cfunctions.fun1()io.write("[Lua] Function 1 says it's ", f, " times funnyn"); f = cfunctions.fun2()io.write("[Lua] Function 2 says it's ", f, " times funnyn"); io.write("[Lua] Exiting Lua scriptn") 库文件代码如下:123456789101112131415161718192021222324252627282930313233343536373839404142434445#include <iostream> #include <lua.hpp>/* * Library functions */extern "C"{static int funval = 10;static int function_1(lua_State* l){ std::cout << "[DLL] Function 1 is very funny." << std::endl; std::cout << "[DLL] It's fun value is: " << funval << std::endl; lua_pushnumber(l, funval); return 1;}static int function_2(lua_State* l){ std::cout << "[DLL] Function 2 is twice as funny!" << std::endl; std::cout << "[DLL] It's fun value is: " << 2*funval << std::endl; lua_pushnumber(l, 2*funval); return 1;}/* * Registering functions */static const struct luaL_Reg cfunctions[] = { {"fun1", function_1}, {"fun2", function_2}, {NULL, NULL}};int __declspec(dllexport) luaopen_cfunctions(lua_State* l){ luaL_newlibtable(l, cfunctions); luaL_setfuncs(l, cfunctions, 0); return 1;}} // end extern "C" 细节分析用于产生动态链接库的代码包含一个导出函数luaopen_cfunctions()和两个隐藏的函数function_1(),function_2()。导出函数会将两个隐藏函数注册到Lua。这样,就不能从外部直接调用它们了。 为了注册该库,我们首先创建一个带有需要注册函数的信息的结构体。在我们的例子中就是luaL_Reg结构体的数组cfunctions: 12345static const struct luaL_Reg cfunctions[] = { {"fun1", function_1}, {"fun2", function_2}, {NULL, NULL}}; 需要说明的时,此例中在Lua将用fun1,fun2分别代替function_1,function_2。然后用luaL_newlibtable()在堆栈顶部创建library table,并用luaL_setfuncs()注册我们的函数。 12luaL_newlibtable(l, cfunctions);luaL_setfuncs(l, cfunctions, 0); 最后,返回加入堆栈的元素个数,只有一个,就是我们的library table。 咦,怎么报错了!!!可能有许多种原因,据我所知主要有以下两种: 如果报错信息是“The specified procedure could not be found”(找不到指定的程序)就很容易解决。我认为弹出这个错误的原因主要有两个: 其一,你忘记了用extern C 语句包含库文件代码。Lua底层用的是C语言,不能被c++链接。所以要告诉编译器用C语言的编译规则去编译库文件。解决方法就是用extern C包含你的库代码。 其二,你忘记了导出你的函数。即调用luaopen_cfunctions()声明你要导出的函数。在微软的编译器上,你还必须早导出函数前面加上__declspecs(dllexport)宏,在其他的编译器上,仅仅定义为extern函数就行了。否则,你就是把自己的函数隐藏了,当然,编译器不会给你好脸色的。 如果报错信息是“Multiple Lua VMs detected”(检测到多个Lua虚拟机),说明你遇到了很隐蔽的错误。哥只有一个Lua状态机,所以第一次看到这个错误信息,哥被迷惑了。你不能在Lua里面同时静态链接c++宿主程序和C库,你必须动态的往Lua库中链接它们。至少我这样解决了这个问题。 以上。 希望你已经学会怎么创建C函数库,并能通过require()函数动态的链接到Lua5.2。这与在c++中注册函数供Lua使用,并没有什么不同,但是却能大大提高代码的重用率。 稍后将奉上在c++和Lua中互传对象的教程,也即是我们的最终教程。敬请期待。]]></content>
<categories>
<category>lua</category>
</categories>
<tags>
<tag>lua</tag>
<tag>c++</tag>
</tags>
</entry>
<entry>
<title><![CDATA[在lua中调用c++函数]]></title>
<url>%2F2014%2F08%2F01%2FCALLING-Cpp-FUNCTIONS-FROM-LUA-5-2%2F</url>
<content type="text"><![CDATA[原文出处:http://www.acamara.es/blog/2012/08/calling-c-functions-from-lua-5-2/ 我的译文如下: 欢迎惠阅我的第三篇Lua5.2的学习笔记。如果你还没读过我的前两篇文章,敬请移步如何在c++程序中运行lua脚本, 从lua脚本中向c++传递变量。 本篇新教程讲讲述如何在Lua5.2中给c++函数传递参数,并接收返回值。文中大多引述均来自lua用户WIKI和lua5.2的用户手册。 嗯,我还是那个一如既往帅气阳光的基督小伙儿 Stigen Larsen。 首先,我将展示Lua和c++代码,以及其运行的输出结果。然后逐步剖析其中难点。 Lua 脚本部分Lua脚本将向c++函数传递一些参数,然后打印出c++返回的两个参数。代码如下: 123456-- call the registered C-functionio.write('[Lua] Calling the C functionn')a,b = displayLuaFunction(12, 3.141592, 'hola')-- print the return valuesio.write('[Lua] The C function returned <' .. a .. '> and <' .. b .. '>n') c++部分c++代码和前文基本一致。区别之处就是声明函数和将函数压入Lua堆栈的部分。完整代码如下: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879#include <iostream>#include <sstream>#include <lua.hpp>int displayLuaFunction(lua_State*);int main(int argc, char* argv[]){ // new Lua state std::cout << "[C++] Starting Lua state" << std::endl; lua_State *lua_state = luaL_newstate(); // load Lua libraries std::cout << "[C++] Loading Lua libraries" << std::endl; static const luaL_Reg lualibs[] = { {"base", luaopen_base}, {"io", luaopen_io}, {NULL, NULL} }; const luaL_Reg *lib = lualibs; for(; lib->func != NULL; lib++) { std::cout << " loading '" << lib->name << "'" << std::endl; luaL_requiref(lua_state, lib->name, lib->func, 1); lua_settop(lua_state, 0); } // push the C++ function to be called from Lua std::cout << "[C++] Pushing the C++ function" << std::endl; lua_pushcfunction(lua_state, displayLuaFunction); lua_setglobal(lua_state, "displayLuaFunction"); // load the script std::cout << "[C++] Loading the Lua script" << std::endl; int status = luaL_loadfile(lua_state, "parrotscript.lua"); std::cout << " return: " << status << std::endl; // run the script with the given arguments std::cout << "[C++] Running script" << std::endl; int result = 0; if(status == LUA_OK) { result = lua_pcall(lua_state, 0, LUA_MULTRET, 0); } else { std::cout << " Could not load the script." << std::endl; } // close the Lua state std::cout << "[C++] Closing the Lua state" << std::endl; return 0;}int displayLuaFunction(lua_State *l){ // number of input arguments int argc = lua_gettop(l); // print input arguments std::cout << "[C++] Function called from Lua with " << argc << " input arguments" << std::endl; for(int i=0; i<argc; i++) { std::cout << " input argument #" << argc-i << ": " << lua_tostring(l, lua_gettop(l)) << std::endl; lua_pop(l, 1); } // push to the stack the multiple return values std::cout << "[C++] Returning some values" << std::endl; lua_pushnumber(l, 12); lua_pushstring(l, "See you space cowboy"); // number of return values return 2;} 详细剖析函数在Lua中是一等公民。这意味着Lua函数可以像普通变量一样存储。我们可以像对待普通变量一样把函数压入Lua堆栈,然后命名为全局变量。123// push the C++ function to be called from Lualua_pushcfunction(lua_state, displayLuaFunction);lua_setglobal(lua_state, "displayLuaFunction"); Lua就可以通过这个全局变量访问我们的函数displayLuaFunction()。除此之外,还有一个宏lua_register(),可以把上述步骤合二为一。所以上述代码等价于: 12// push the C++ function to be called from Lualua_register(lua_state, "displayLuaFunction", displayLuaFunction); (感谢kpityu小伙儿在评论种指出这点) 为了便于c++和Lua交换数据,代码必须遵循一些规则。 规则NO1:函数的定义和lua_CFunction一致1typedef int (*lua_CFunction) (lua_State *L); 即函数必须接收指向lua_State的指针作为参数,并且返回整形。 规则NO2:遵守Lua和c++交换数据的协议。正如你所见,所有数据都是通过Lua堆栈作为桥梁交换的。例如,当Lua调用c++函数时1displayLuaFunction(a, b, c) Lua会为此函数创建新的堆栈,该堆栈独立于其他堆栈。堆栈种包含输入参数,因此呢,我们在c++函数中可以通过Lua堆栈来获取所有参数。12// number of input argumentsint argc = lua_gettop(l); 并且可以直接获取1lua_tostring(l, lua_gettop(l)); 需要注意的是,参数按传参顺序入栈,逆序出栈。由于一开始堆栈中只有输入参数,你可以直接通过索引获取其值。如果第二个参数可以被转化成字符串,我们可以这样获取它:1lua_tostring(l, 2); 一旦该函数执行完毕,其返回值必须放入堆栈以便Lua使用它们。123// push to the stack the multiple return valueslua_pushnumber(l, 12);lua_pushstring(l, "See you space cowboy"); 最后,为了让Lua能够获取c++回传的数据,我们需要返回放入Lua堆栈的变量的数量12// number of return valuesreturn 2; 是的,就是这么简单! 此文讲述了Lua和c++混编的基本协议。如果想查看更多示例,请查阅Lua的相关库,它们都是严格遵循这些规则的。 我在犹豫,下篇文章是讨论Lua和c++混编时动态链接库的使用,还是讨论Lua和c++之间对象的传递呢,敬请期待。]]></content>
<categories>
<category>lua</category>
</categories>
<tags>
<tag>lua</tag>
<tag>c++</tag>
</tags>
</entry>
<entry>
<title><![CDATA[从lua脚本中向c++传递变量]]></title>
<url>%2F2014%2F07%2F25%2FPASSING-VARIABLES-FROM-LUA-5-2-TO-Cpp%2F</url>
<content type="text"><![CDATA[原文出处:http://www.acamara.es/blog/2012/08/passing-variables-from-lua-5-2-to-c-and-vice-versa/ 我的译文如下 这是第二篇关于我和c++lua混编肉搏的笔记。如果你还没读过我的第一篇文章,敬请移步如何在c++程序中运行lua脚本。文中大多引述均来自lua用户WIKI和lua5.2的用户手册。上篇文章仅仅只是抛砖引玉,c++和lua混编的强悍之处我们并未深入介绍。我希望通过以下示例让你能更进一步了解和使用lua.我们的目标有两个:第一,从宿主语言c++往Lua脚本中传递参数。第二,从Lua脚本中接收返回数据。注意,Lua脚本可以返回不止一个数据哦! Lua脚本部分以下Lua脚本只是把我们存入全局的arg序列中的数据打印我们出来,然后返回不同类型Lua变量。代码如下: 1234567891011121314-- print the arguments passed from Cio.write("[Lua] These args were passed into the script from Cn")for i=1,#arg do print(" ", i, arg[i])end -- return a value of different Lua types (boolean, table, numeric, string)io.write("[Lua] Script returning data back to Cn")-- create the tablelocal temp = {}temp[1]=9temp[2]="See you space cowboy!"return true,temp,21,"I am a mushroom" 为了在c++和Lua之间互传信息,我们必须编译c++程序。 c++部分正如上篇教程,我会先奉上完整代码,然后逐步剖析。 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889#include <iostream>#include <sstream>#include <lua.hpp>int main(int argc, char* argv[]){ // new Lua state std::cout << "[C++] Starting Lua state" << std::endl; lua_State *lua_state = luaL_newstate(); // load Lua libraries std::cout << "[C++] Loading Lua libraries" << std::endl; static const luaL_Reg lualibs[] = { {"base", luaopen_base}, {"io", luaopen_io}, {NULL, NULL} }; const luaL_Reg *lib = lualibs; for(; lib->func != NULL; lib++) { std::cout << " loading '" << lib->name << "'" << std::endl; luaL_requiref(lua_state, lib->name, lib->func, 1); lua_settop(lua_state, 0); } // start the arg table in Lua std::cout << "[C++] Creating the arg table" << std::endl; lua_createtable(lua_state, 2, 0); lua_pushnumber(lua_state, 1); lua_pushnumber(lua_state, 49); lua_settable(lua_state, -3); lua_pushnumber(lua_state, 2); lua_pushstring(lua_state, "Life is a beach"); lua_settable(lua_state, -3); lua_setglobal(lua_state, "arg"); // load the script std::cout << "[C++] Loading the Lua script" << std::endl; int status = luaL_loadfile(lua_state, "parrotscript.lua"); std::cout << " return: " << status << std::endl; // run the script with the given arguments std::cout << "[C++] Running script" << std::endl; int result = 0; if(status == LUA_OK) { result = lua_pcall(lua_state, 0, LUA_MULTRET, 0); } else { std::cout << " Could not load the script." << std::endl; } // print the values returned from the script std::cout << "[C++] Values returned from the script:" << std::endl; std::stringstream str_buf; while(lua_gettop(lua_state)) { str_buf.str(std::string()); str_buf << " "; switch(lua_type(lua_state, lua_gettop(lua_state))) { case LUA_TNUMBER: str_buf << "script returned the number: " << lua_tonumber(lua_state, lua_gettop(lua_state)); break; case LUA_TTABLE: str_buf << "script returned a table"; break; case LUA_TSTRING: str_buf << "script returned the string: " << lua_tostring(lua_state, lua_gettop(lua_state)); break; case LUA_TBOOLEAN: str_buf << "script returned the boolean: " << lua_toboolean(lua_state, lua_gettop(lua_state)); break; default: str_buf << "script returned an unknown-type value"; } lua_pop(lua_state, 1); std::cout << str_buf.str() << std::endl; } // close the Lua state std::cout << "[C++] Closing the Lua state" << std::endl; return 0;} 正如亲眼所见,程序的基本结构一如从前。我们打开一个状态机,加载库文件,在状态机上运行脚本,最后关闭状态机。程序输出如下: 细节剖析细心如你者,一定发现了这次我们并未使用luaL_dofile()函数(事实上它是一个宏)来执行脚本。而是代之以luaL_loadfile()加载脚本,以lua_pcall()执行脚本。这是为了展示luaL_dofile宏的原理,实际上luaL_dofile是以上两个函数的合体,其宏定义如下: 1(luaL_loadfile(L, filename) || lua_pcall(L, 0, LUA_MULTRET, 0)) 我认为是时候,介绍一下Lua堆栈了。Lua堆栈是连接宿主语言和Lua的桥梁。宿主语言可以在堆栈中放入Lua状态机可见的数据,而Lua状态机也可以在Lua堆栈中放入宿主语言可见的数据。虽然听起来很老土,但是很有效。以下代码很直观的反应了这一点: 123456789// start the arg table in Lualua_createtable(lua_state, 2, 0);lua_pushnumber(lua_state, 1);lua_pushnumber(lua_state, 49);lua_settable(lua_state, -3);lua_pushnumber(lua_state, 2);lua_pushstring(lua_state, "Life is a beach");lua_settable(lua_state, -3);lua_setglobal(lua_state, "arg"); 这段代码的作用是创建类型为table的全局变量arg作为Lua的输入参数。此table使用连续存储,并含有连个元素: 12arg[1] = 49arg[2] = "Life is a beach" 这段代码可以分为三部分: 创建table第一部分通过lua_createtable()函数完成,此函数创建一个匿名table并把它存入堆栈的顶部,其定义如下: 1void lua_createtable(lua_State *L, int narr, int nrec); 其中参数narr和nrec分别用于指明连续存储元素和非连续存储元素的的数量。因为我们的table只有两个连续存储的元素,所以我们传递的实参为2和0. 填充table第二部分,用期望的值填充table。首先,用lua_push*()往Lua堆栈压入两个值,第一个是table的下标,第二个是下标对应得值。然后,通过lua_settable(lua_state, -3)将上述下标和对应的值存入table,-3代表从堆栈顶部往下数第三个数据,亦即是我们创建的匿名table。以上操作相当于如下代码: 1t[k] = v v,k分别是堆栈顶部第一和第二个元素。因此,以下代码: 123lua_pushnumber(lua_state, 1);lua_pushnumber(lua_state, 49);lua_settable(lua_state, -3); 就是设置我们的匿名table的下标1对应的值为49。需要注意的以上操作也将下标和对应的值弹出了堆栈,此时匿名table变成栈顶元素。 命名table第三部分,为创建和填充的table起个名字。这一步可以用lua_setglobal()函数,将table命名为全局arg的同时也将其弹出堆栈。此后,Lua就可以在脚本中识别这个table。一旦脚本运行,其返回值也可以通过Lua堆栈传递给c++。需要特别注意的是,Lua将返回值按return语句后的表达式,顺序入栈,逆序出栈。因为附带着冗余的系统信息,这段操作返回的信息量很大。基本上分为四步: 用lua_gettop()函数获取栈顶的具体位置用lua_type()函数获取栈中具体位置的数据类型用lua_to*()函数将堆栈中的数据转换为c++对应的类型用lua_pop()函数弹出栈中的数据 这些函数的名字很好的解释其作用。就是这样,很简单! 此文讨论了宿主语言和Lua共享数据的基本概念。你可以修改Lua脚本用以观察程序的输出变化。务必记住,你不需要重新编译就可以改变其执行逻辑。下篇文章将讨论如何在Lua脚本中调用c++函数,敬请期待。]]></content>
<categories>
<category>lua</category>
</categories>
<tags>
<tag>lua</tag>
<tag>c++</tag>
</tags>
</entry>
<entry>
<title><![CDATA[如何在c++程序中运行lua脚本]]></title>
<url>%2F2014%2F07%2F16%2FRUNNING-A-LUA-5-2-SCRIPT-FROM-Cpp%2F</url>
<content type="text"><![CDATA[原文出处:http://www.acamara.es/blog/2012/08/running-a-lua-5-2-script-from-c/ 译文如下: Lua 是一个胶水语言,可以很方便的去扩展其它宿主语言。说她强大是有很多原因的,而我最喜爱的莫过于她允许无需重新编译即可改变一个程序的执行逻辑。不幸的是,我发现网上很少有关于lua 5.2的混编教程,而lua 5.2与c++的混编与lua 5.1,lua 5.0,还有lua 4.x是有些差别的,其中包括如下主题: c++如何调用Lua 脚本 c++与Lua之间如何互传信息 Lua如何调用c++ Lua如何动态连接c函数库 Lua如何实现面向对象 如何在c++和Lua之间互传对象 此文仅是我个人关于上述第一个主题的一些看法。关于后续主题,我将稍后论述。文中相关代码引用自:Lua Wiki http://lua-users.org/wiki/SampleCodeLua 5.2 手册 http://www.lua.org/manual/5.2/manual.html中文手册 http://www.photoneray.com/Lua-5.2-Reference-Manual-ZH_CN/#lua_settop 我所使用的Lua为官方版本5.2.1,通过Mac osx10.8.4, gcc4.8进行编译。Lua的编译过程,以及Lua的语法细节,已超出本文范畴,具体详情请参考Lua在线文档。 c++调用Lua脚本这是你能用c++和Lua混编做的最简单的事儿。假如你的Lua脚本的如下: 12-- Simple Hello World Lua programprint('Hello World!') 然后,你想从一个简单的c++程序里面调用它。具体步骤如下: 创建Lua状态机–你可以认为是一个运行Lua脚本的虚拟机 加载所需要的Lua库 运行Lua脚本 关闭Lua状态机 因为我们包含了Lua的头文件,故项目必须能够连接预先编译好的Lua库文件,Lua的相关头文件也必须能被项目访问。 完整代码如下所示:123456789101112131415161718192021222324252627#include <lua.hpp>int main(int argc, char* argv[]){ // create new Lua state lua_State *lua_state; lua_state = luaL_newstate(); // load Lua libraries static const luaL_Reg lualibs[] = { { "base", luaopen_base }, { NULL, NULL} }; const luaL_Reg *lib = lualibs; for(; lib->func != NULL; lib++) { lib->func(lua_state); lua_settop(lua_state, 0); } // run the Lua script luaL_dofile(lua_state, "helloworld.lua"); // close the Lua state lua_close(lua_state);} 程序的输出正如我们所预料的那样: 细节分析让我们详细的剖析相关代码。 第一段代码是用来创建Lua状态机。123// create new Lua statelua_State *lua_state;lua_state = luaL_newstate(); 代码的意图很明显。唯一要做的就是把通过luaL_newstate()函数创建的Lua状态机的地址保存到一个指针。然后我们就可以通过指针来使用状态机。 第二段代码的作用是加载所需的库文件。这部分有点难以理解。每个Lua库,包括你自己生成的,都是被c++打开,并保存到Lua状态机的。 123456// load Lua librariesstatic const luaL_Reg lualibs[] ={ { "base", luaopen_base }, { NULL, NULL}}; 然后,我们把Lua状态机的指针当做参数传给库的加载函数来加载每个库。lua_settop()函数的的调用是为了确保清除Lua堆栈的所有变量。 123456const luaL_Reg *lib = lualibs;for(; lib->func != NULL; lib++){ lib->func(lua_state); lua_settop(lua_state, 0);} 第三段代码的作用是运行我们的Lua脚本。12// run the Lua scriptluaL_dofile(lua_state, "helloworld.lua"); 你唯一需要提供的就是状态机指针和脚本名称,如果脚本跟c++可执行文件不再同一目录,就加上完整路径。 最后一段代码的作用是关闭状态机。12// close the Lua statelua_close(lua_state); 这会清空状态机所使用的所有内存,除非一些罕见的情况。 以上。这是最简单的c++和Lua混编。稍后会奉上更加复杂的使用案例,敬请期待。]]></content>
<categories>
<category>lua</category>
</categories>
<tags>
<tag>lua</tag>
<tag>c++</tag>
</tags>
</entry>
</search>