cocos2dx luabinding CC++LUA部分

cocos2dx luabinding CC++LUA部分,第1张

概述实验成功的快速自动绑定过程: 编辑一个cpp文件为lua进行绑定: 准备好ide: cocos ide 与 xcode 编辑cpp 原文件名称: LuaVideoBriageManager.cpp 1,进入当前项目:/Users/kaitiren/app/CocosLuaApp/frameworks/cocos2d-x/tools/tolua/LuaHttpClient.ini 2, 进行ini配 实验成功的快速自动绑定过程:

编辑一个cpp文件为lua进行绑定:

准备好IDe: cocos IDe 与 xcode

编辑cpp 原文件名称:LuaVIDeoBriageManager.cpp

1,进入当前项目:/Users/kaitiren/app/CocosLuaApp/frameworks/cocos2d-x/tools/tolua/LuahttpClIEnt.ini

2,进行ini配置文件复制,并该名为LuaVIDeoBriageManager,进入ini,该名所有的LuahttpClIEnt为LuaVIDeoBriageManager;

3,编辑genbindings.py文件,

cmd_args = {#'cocos2dx.ini' : ('cocos2d-x','lua_cocos2dx_auto'),\

#'cocos2dx_extension.ini' : ('cocos2dx_extension','lua_cocos2dx_extension_auto'),\

#'cocos2dx_ui.ini' : ('cocos2dx_ui','lua_cocos2dx_ui_auto'),\

#'cocos2dx_studio.ini' : ('cocos2dx_studio','lua_cocos2dx_studio_auto'),\

#'cocos2dx_spine.ini' : ('cocos2dx_spine','lua_cocos2dx_spine_auto'),\

#'cocos2dx_physics.ini' : ('cocos2dx_physics','lua_cocos2dx_physics_auto'),\

#'cocos2dx_experimental_vIDeo.ini' : ('cocos2dx_experimental_vIDeo','lua_cocos2dx_experimental_vIDeo_auto'),\

#'cocos2dx_experimental.ini' : ('cocos2dx_experimental','lua_cocos2dx_experimental_auto'),\

#'cocos2dx_controller.ini' : ('cocos2dx_controller','lua_cocos2dx_controller_auto'),\

'LuahttpClIEntManager.ini' : ('LuahttpClIEntManager','lua_LuahttpClIEntManager_auto'),\

'LuaVIDeoBriageManager.ini' : ('LuaVIDeoBriageManager','lua_LuaVIDeoBriageManager_auto'),\

# 'MyClass.ini' : ('MyClass','lua_MyClass_auto'),\

}

4,进入命令行工具,sudo python/Users/kaitiren/app/CocosLuaApp/frameworks/cocos2d-x/tools/tolua/genbindings.py

5,看到成功后,进入xcode当前项目中,cocos2d_lua_

bindings.xcodeproj项目下,auto文件夹下添加 (注意要加入ios工程)

lua_LuaVIDeoBriageManager_auto.cpp

lua_LuaVIDeoBriageManager_auto.hpp

编译成功后在进行注册部分

6,进行注册调用:

进入当前项目下的class目录中,进入bool AppDelegate::applicationDIDFinishLaunching()方法

LuaStack* stack = engine->getLuaStack();

stack->setXXTEAKeyAndSign("2dxLua",strlen("2dxLua"),"XXTEA",strlen("XXTEA"));

//register custom function

//LuaStack* stack = engine->getLuaStack();

//register_custom_function(stack->getLuaState());

lua_State *L = stack->getLuaState();

lua_getglobal(L,"_G");

// register_all_MyClass(L);

register_all_LuahttpClIEntManager(L);

register_all_LuaVIDeoBriageManager(L);

lua_settop(L,0);

#if (COCOS2D_DEBUG>0)

if (startRuntime())

return true;

#endif

... 完成

7. lua 调用 cpp中的方法测试:

localtestVIDeo= my.LuaVIDeoBriageManager:new()

testVIDeo:lua_call_cpp_open_vIDeo_with_string("http://www.baIDu.com")

--http://v.youku.com/v_show/id_XMTQ2NTEyNzQ4NA==.html?from=s1.8-1-1.2

能正常调用cpp方法成功;


编辑回调cpp call lua部分:

voID LuahttpClIEntManager:: cpp_call_lua_http_get_pic_writecode(LuahttpClIEnt* clIEnt,int _writecode,const char* _str_save_path_file)

{

LuaStack * L =LuaEngine::getInstance()->getLuaStack();

lua_State* tolua_s = L->getLuaState();

lua_getglobal(tolua_s,"cpp_call_lua_http_get_pic_writecode"); // 获取函数,压入栈中 cpp call lua

lua_pushnumber(tolua_s,_writecode);

lua_pushstring(tolua_s,_str_save_path_file);

int iRet= lua_pcall(tolua_s,2,0);// 压入第一个参数

if(iRet)

{

cclOG("cpp_call_lua_http_get_pic_writecode !!!");

}

///

// CC_SAFE_DELETE(clIEnt);

}


lua代码部分编辑方法:

functioncpp_call_lua_http_get_pic_writecode(_writecode,_str_save_path_file)

print("cpp_call_lua_http_get_pic_writecode _writecode: ".._writecode.." _str_save_path_file: ".._str_save_path_file)

end

调用成功完成


------------------------------------------------------------------------------------------------------------------------------------

cocos2dx原生lua绑定工具的总结

一、个人对“绑定”这词有两种理解
1、lua绑定到C++,就是C++能调用到lua的东西,那必须让C++知道有哪些lua函数或变量可以用
2、C++绑定到lua,就是lua能调用到C++的东西,当然也必须让lua知道有哪些C++东东可以给lua调用,所谓的“暴露”

这里说的绑定就是第2种情况,在lua中能调用到Cocos2dx的函数。
Cocos2dx通过工程里面的tools/tolua工具生成注册C++函数到lua的函数cpp文件
二、环境设置
工具:
NDK_ROOT 必须为androID-ndk-r9b 64bit版
Python 为32bit版,2.7版(因为有个插件是32位的(Cheetah),如果这个插件有64的,个人觉得用python64位没问题)
python插件:Cheetah、PyYAML(3.10以上版本、有32、64可选,但现在必选32)

@H_63_301@sudo easy_install pipsudo pip install PyYAMLsudo pip install Cheetah

环境设置具体查看https://github.com/cocos2d/bindings-generator
三、生成工具
生成工具是一个python文件控制(genbindings.py),这个python文件中可以设置自己的生成规则文件(ini)、生成路径等重要信息,可以参考http://www.cocoachina.com/bbs/read.php?tid=196416
编写或者修改genbindings.py要注意点的:
1、 设置cmd_args参数时,第一是ini文件名,最后一个是要生成的文件名,重点是第二个,这个参数是生成器在ini文件里面查找对应的块(即ini文件中中括号对应的文字,如[cocos2dx],一般是第一行),形如cmd_args = {'custom.ini' : ('custom','lua_custom_auto') }就是查找custom.ini中的[custom]段配置,最后生成名为lua_custom_auto的文件,如果第二个参数没对应上,出现Section not found in config file的错误
四、这里重点是,编写ini
1、首先必须了解正则表达式,百度直接搜索正则表达式
2、关键参数
prefix 关系到lua函数开头的名字,如prefix=cocos2dx,那么生成的代码就是以lua_cocos2dx开头
target_namespace 表面这些lua函数注册到哪个命名空间,在lua中,就是代表注册到那个表中,如target_namespace=custom,那么在lua中调用就是以 custom. 开头(但这个参数好像控制不到,真正能控制的是在C++里面是否有custom的命名空间)
这里有两点要注意的
(1)、如果有自定义的命名空间,cocos2dx主目录的tools文件夹下bindings-generator\targets\lua下的conversions.yaml(Js的是bindings-generator\targets\spIDermonkey下的conversions.yaml)添加自己的命名空间:
ns_map:
"cocos2d::extension::": "cc."
"cocos2d::ui::": "ccui."
"cocos2d::": "cc."
"spine::": "sp."
"cocostudio::": "ccs."
"cocosbuilder::": "cc."
"CocosDenshion::": "cc."
"custom::": "custom."
这个文件貌似是一些替换 *** 作配置(就是在生成C++文件中替换掉所设置的的字符串),自己研究一下就知道了。
(2)如果自己的代码没有命名空间,建议弄一个,不然遇到C++类中的类型,诸如下面的的C++代码:

?

1 2 3 4 5 6 7 8 class A { enum State @H_256_404@ { StateOne, StateTwo, } }


舀鱙A类中有函数参数或返回值用到State类型的,在生成过程中汇报错,报错消息称没有A的命名空间。。。那么这时候就得到第一点提及的地方加入一行:"A::":"A."的信息。在有命名空间的情况下,生成器直接通过。这里是个奇怪的问题。
headers 填入你所编写的代码的头文件
classes 填入要生成的类,支持多个类,支持正则表达式,如classes = A,^A,^A$
skip 指定屏蔽函数,即不需要暴露给lua的函数,支持正则表达式,如 skip = Sprite::[getQuad getBlendFunc ^setposition$ setBlendFunc]
rename_functions 重新命名暴露到lua中函数名(一般以C++编写函数名暴露到lua),如 rename_functions = SpriteFrameCache::[addSpriteFramesWithfile=addSpriteFrames getSpriteFrameByname=getSpriteFrame],
等号前是原函数名,等号后是lua中的新函数名
rename_classes重命名类,如 rename_classes = SimpleAudioEngine::AudioEngine。前面是原名,后面是lua新名。这个好像没效果,生成后还是那个名字
classes_have_no_parents 指定一些没有父类的类
base_classes_to_skip 指定一些需要跳过的基类
abstract_classes 指定一些抽象类或者没有构造函数的类,以手动方式编写注册到lua函数
编写完后,把只要执行修改过的自定义的或者修改过的genbindings.py(http://www.cocoachina.com/bbs/read.php?tid=196416提及)就可以生成出相关的代码
3、自动生成的代码会自动过滤掉C++中的protect、private属性、重载父类的函数、带省略号参数的函数(如Menu::create(MenuItem* item,...) )
五、嵌入到CPP代码中
1、一般情况下,我们会在AppDelegate.cpp下注册自己的函数,如
8 9 auto engine = LuaEngine::getInstance();

register_all_custom(engine->getLuaStack()->getLuaState());

register_all_custom_manual(engine->getLuaStack()->getLuaState());

ScriptEngineManager::getInstance()->setScriptEngine(engine);

engine->executeScriptfile( "src/main.lua" );


但是在3.0中,LuaEngine初始化过程中会加载几个lua文件,把lua栈清空,导致程序崩溃。这里参考http://www.cocoachina.com/bbs/read.php?tid=226180&page=1#1042506
建议是把那几个文件放到注册完自己函数后加载,如
9 10 11 12 13 "DeprecatedEnum.lua" ); "DeprecatedClass.lua" ); "Deprecated.lua" );

为了新手少走弯路,把乱七八糟的笔记贴出来,如有错误,不吝指教!






错误记录:

1)yaml

http://codyaray.com/2011/12/pyyaml-using-easy_install-on-mac-os-x-lion

1.报错

importError: No module named yaml

2.安装

sudo easy_install pyyaml

error

执行:sudo python -m easy_install pyyaml

执行:sudo easy_install pyyaml

(2)Cheetah


下载cheetah:http://pythonhosted.org//Cheetah/

进入目录执行 sudo python setup.py install



Cocos2d-x v3.2 lua绑定c++类方法总结

cocos2d-x在2.x版本里就是用tolua++和.pkg文件这么把自己注册进Lua环境里的。不过这种方法明显笨拙,既要写真正做事的.pkg文件,也要写桥接的.pkg文件和.h文件,工作量又大又枯燥。所以从cocos2d-x 3.x开始,用bindings-generator脚本代替了tolua++。

bindings-generator脚本的工作机制是:

1、不用挨个类地写桥接.pkg和.h文件了,直接定义一个ini文件,告诉脚本哪些类的哪些方法要暴露出来,注册到Lua环境里的模块名是什么,就行了,等于将原来的每个类乘以3个文件的工作量变成了所有类只需要1个.ini文件
2、摸清了tolua++工具的生成方法,改由Python脚本动态分析C++类,自动生成桥接的.h和.cpp代码,不调用tolua++命令了
3、虽然不再调用tolua++命令了,但是底层仍然使用tolua++的库函数,比如tolua_function,bindings-generator脚本生成的代码就跟使用tolua++工具生成的几乎一样

bindings-generator脚本掌握了生成tolua++桥接代码的主动权,不仅可以省下大量的.pkg和.h文件,而且可以更好地插入自定义代码,达到cocos2d-x环境下的一些特殊目的,比如内存回收之类的。所以cocos2d-x从3.x开始放弃了tolua++和.pkg而改用了自己写的bindings-generator脚本是非常值得赞赏的聪明做法。

接下来说怎么用bindings-generator脚本:

1、写自己的C++类,按照cocos2d-x的规矩,继承cocos2d::Ref类,以便使用cocos2d-x的内存回收机制。当然不这么干也行,但是不推荐,不然在Lua环境下对象的释放狠麻烦。
2、编写一个.ini文件,让bindings-generator可以根据这个配置文件知道C++类该怎么暴露出来
3、修改bindings-generator脚本,让它去读取这个.ini文件
4、执行bindings-generator脚本,生成桥接C++类方法
5、用Xcode将自定义的C++类和生成的桥接文件加入工程,不然编译不到
6、修改AppDelegate.cpp,执行桥接方法,自定义的C++类就注册进Lua环境里了

看着步骤挺多,其实都狠简单。下面一步一步来。

首先是自定义的C++类。我习惯将文件保存在frameworks/runtime-src/Classes/目录下:

frameworks/runtime-src/Classes/MyClass.h

#include "cocos2d.h"using namespace cocos2d;class MyClass : public Ref{public: MyClass()  {}; ~MyClass() {}; bool init() { return true; }; CREATE_FUNC(MyClass); int foo(int i);};

frameworks/runtime-src/Classes/MyClass.cpp

#include "MyClass.h"int MyClass::foo(int i){ return i + 100;}

然后编写.ini文件。在frameworks/cocos2d-x/tools/tolua/目录下能看到genbindings.py脚本和一大堆.ini文件,这些就是bindings-generator的实际执行环境了。随便找一个内容比较少的.ini文件,复制一份,重新命名为MyClass.ini。大部分内容都可以凑合不需要改,这里仅列出必须要改的重要部分:

frameworks/cocos2d-x/tools/tolua/MyClass.ini

[MyClass]prefix      = MyClasstarget_namespace = myheaders     = %(cocosdir)s/../runtime-src/Classes/MyClass.hclasses     = MyClass

也即在MyClass.ini中指定MyClass.h文件的位置,指定要暴露出来的类,指定注册进Lua环境的模块名。

注意,这个地方我踩了个坑。如果.ini配置文件中存在macro_judgement = ...宏定义,要特别小心,我第一次是从cocos2dx_controller.ini文件复制来的,结果没注意macro_judgement,导致生成的桥接类文件加入了不该加入的宏,只在iOS和AndroID平台上才起作用,对Mac平台无效,这个要特别注意。

然后修改genbindings.py文件129行附近,将MyClass.ini文件加进去:

frameworks/cocos2d-x/tools/tolua/genbindings.py

cmd_args = {'cocos2dx.ini' : ('cocos2d-x','lua_cocos2dx_auto'),\      'MyClass.ini' : ('MyClass',152);">'lua_MyClass_auto'),\      ...

(其实这一步本来是可以省略的,只要让genbindings.py脚本自动搜寻当前目录下的所有ini文件就行了,不知道将来cocos2d-x团队会不会这样优化)

至此,生成桥接文件的准备工作就做好了,执行genbindings.py脚本:

python genbindings.py

(在Mac系统上可能会遇到缺少yaml、Cheetah包的问题,安装这些Python包狠简单,先sudo easy_install pip,把pip装好,然后用pip各种pip searchsudo pip install就可以了)

成功执行genbindings.py脚本后,会在frameworks/cocos2d-x/cocos/scripting/lua-bindings/auto/目录下看到新生成的文件:

每次执行genbindings.py脚本时间都挺长的,因为它要重新处理一遍所有的.ini文件,建议大胆修改脚本文件,灵活处理,让它每次只处理需要的.ini文件就可以了,比如像这个样子:

frameworks/cocos2d-x/cocos/scripting/lua-bindings/auto/目录下观察一下生成的C++桥接文件lua_MyClass_auto.cpp,里面的注册函数名字为register_all_MyClass(),这就是将MyClass类注册进Lua环境的关键函数:

编辑frameworks/runtime-src/Classes/AppDelegate.cpp文件,首先在文件头加入对lua_MyClass_auto.hpp文件的引用:

然后在正确的代码位置加入对register_all_MyClass函数的调用:

最后在执行编译前,将新加入的这几个C++文件都加入到Xcode工程中,使得编译环境知道它们的存在:

这其中还有一个小坑,由于lua_MyClass_auto.cpp文件要引用MyClass.h文件,而这俩文件分属于不同的子项目,互相不认识头文件的搜寻路径,因此需要手工修改一下cocos2d_lua_bindings.xcodeproj子项目的User header Search Paths配置。特别注意一共有几个../

最后,就可以用cocos compile -p mac命令重新编译整个项目了,不出意外的话编译一定是成功的。

修改main.lua文件中,尝试调用一下MyClass类:

local test = my.MyClass:create()print("lua bind: " .. test:foo(99))

然后执行程序(用cocos rum -p mac或在Cocos Code IDE中均可),见证奇迹的时刻~~~~咦我擦?!程序崩溃!为毛?

这是我作为cocos2d-x初学者遇到的最大的坑,坑了我整整一天半,具体的研究细节就不详细说了,总之罪魁祸首是cocos2d-x框架中的ccluaEngine.cpp文件的这段代码:

原因是executeScriptfile函数执行时,对当前Lua环境中的栈进行了清理,当register_all_MyClass函数被调用时,Lua栈是全空的状态,函数内部执行到tolua_module函数调用时就崩溃了:

解决办法是修改AppDelegate.cpp为这个样子:

文本形式的代码如下:

AppDelegate.cpp

lua_State *L = stack->getLuaState();lua_getglobal(L,152);">"_G");register_all_MyClass(L);lua_settop(L,0);


巨坑问题解析:

坑1: 当完成hpp和cpp导入cocos2d_lua_bindings.xcodeproj时:

导入时一定要看option中是否关联了mac和ios项目中;


坑2: 设置cocos2d_lua_bindings.xcodeproj 中的TARGETS luabindings Mac 与 luabindings iOS中的serach path头文件目录与lib目录:

-header Search Paths中添加: (IOS 与 MAC)

-- $(SRCROOT)/../../../../../runtime-src/Classes

-library Search Paths: (MAC)

--$(SRCROOT)/../../../../external/lua/luajit/prebuilt/mac

-library Search Paths: (IOS)

--$(SRCROOT)/../../../../external/lua/luajit/prebuilt/ios

--$(SRCROOT)/../../../../external/lua/lua/prebuilt/ios


坑3:AppDelegate.cpp注册cpp中的register_all_MyClass方法:

register_all_MyClass





Cocos2d-x v3.3 lua绑定c++类方法总结

网上有很多cocos2d-x lua绑定c++类的接口教程,这篇文章也是总结他们的经验。 其中重点参考了http://cn.cocos2d-x.org/tutorial/show?id=1295,

整个过程步骤很详细,会比较傻瓜式,希望对新手读者入门有用。

教程基本环境:

1.使用引擎是v3.3 beta版本

2.开发环境:OSX Yosemite

3. Cocos code IDE

4. Xcode 6

1、在Mac上用Cocos code IDE 写lua还是比较便利的,我们用coco code 新建一个工程,命名LuaBindingTest,选择Add NativIE Code(为什么选这个可以去了解一下),工程新建完成后打开运行。如没问题,我们进行下一步。

2、用Xcode打开以上工程,选择ios 模拟器运行,第一次运行会有错误和异常问题,我们修改一下:在Xcode -》General,把Device OrIEntation的Portrait选项去掉(游戏一般横屏或竖屏),只保留Langscape left/Right,修改AppDelegate.cpp的applicationDIDFinishLaunching方法,注释掉initRuntime() ,run,应该能看到画面了。

3.xcode中,新建Class类(放在AppDelegate.cpp所在目录),命名MClass,打开cpp,我们随便加一些方法,MClass类执行绑定过程,并导出lua接口。

MClass.h

#ifndef __LUaBindingTest__MClass__

#define __LUaBindingTest__MClass__

#include <stdio.h>

#include"cocos2d.h"

usingnamespacecocos2d;

class MClass : public Ref{public:  bool init()  {    return true;  }  CREATE_FUNC(MClass);  voID dolog()  {    cclOG(MClass 绑定成功!");  }};

#endif /* defined(__LUaBindingTest__MClass__) */

4. 回到工程的Finder下,打开以下这个目录,

。。。 LuaBindingTest ? frameworks ? cocos2d-x ?tools? tolua

我们会看到很多ini文件和一个genbindings.py,ini是引擎模块的lua绑定配置,genbindings.py脚本会配置这些已存在的ini文件然后导出一个@L_404_13@导出给lua的接口文件(hpp和cpp文件)。为了不干扰引擎现有的lua接口代码,我们依葫芦画瓢,配置一个我们自己项目用到的lua绑定接口。 拷贝cocos2dx_spine.ini,重命名为game.ini ;拷贝genbindings.py,重命名为genbindings_game.py. 剩下的就是修改这两个文件。

game.ini

修改处:

[game]prefix = game#target_namespace,有点命名空间的作用,我举个例子,cocos2dx.ini的这个参数是cc,所以在lua里面调用Director 需要在#前面cc,这个参数可有可无,但建议写了就不要修改,我们这里不写target_namespace =headers = %(cocosdir)s/../runtime-src/Classes/MClass.hclasses = MClass skip = abstract_classes =

genbindings_game.py

只修改第129行

cmd_args = {‘game.ini‘: (‘game‘,‘lua_game_auto‘),\

}

5.终端,cd 到tolua目录,pythongenbindings_game.py。

  如果遇到错误,请阅读tolua目录下的 README.mdown文件,参照上面的方法解决(特别注意,需要的是NDK r9b)

6. 上面过程运行成功后我们会得到两个文件lua_game_auto.cpp和lua_game_auto.hpp,

  回到Xcode,导入这两个文件,注意导入到cocos2d_lua_bindings.xcodeproj库工程而不是项目工程!

添加 cocos2d_lua_bindings.xcodeproj 的header Search Paths. (小技巧,五个..)

7. 把我们的game模块注册进lua环境中,以往注册,我们需要在AppDelegate里面添加,v3.3之后,这些模块注册过程全部放到

lua_module_register.h里面的lua_module_register 方法中

...
//主要代码#include
lua_game_auto.hpp"int lua_module_register(lua_State* L){ //Dont‘ change the module register order unless you kNow what your are doing register_cocosdenshion_module(L); register_network_module(L); register_cocosbuilder_module(L); register_cocostudio_module(L); register_ui_moudle(L); register_extension_module(L); register_spine_module(L); register_cocos3d_module(L);#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS register_audioengine_module(L);#endif lua_getglobal(L,_G"); if (lua_istable(L,-1))stack:...,_G, { register_all_game(L); } lua_pop(L,128);">1); return 1;}

8. 至此,MClass的绑定过程已完成,我们在main.lua 中测试一下

local test = MClass:create()

test:dolog()

 xcode运行,看是否打印log,亲测是可以的。如果想在 cocos code运行,需要编译一次,工程右键 Cocostools-》Build custom runtimes(笔者 *** 作之后,Build提示成功,但运行提示MClass 为nil,未解决,还请指教)

9.当然,我们一个项目不可能只写一个类导出给lua用,如何实现多个c++类导出给lua呢?过程也非常简单, 这里,我们可以参考引擎的做法如cocos2dx.ini,即把多个类配置到同一个ini文件中。

为了避免需要在ini中引入多个c++头文件,我们新建一个lua_c_export.h文件,把需要绑定的类(自己完成这三个类的代码)的头文件include到这个头文件里,然后game.ini文件只配置这个lua_c_export.h的路径,这样可避免做多余修改,要知道配路径非常容易出错,如果多人协作,更容易混乱。可参考下面的图例

game.ini

终端,cd 到tolua目录,pythongenbindings_game.py。我们在lua文件 调用我们绑定的c++类,运行

打印log,下图说明我们多个类绑定成功

----------------------------------------------------------------------------------------------------------------------------------------------------------------------

'game.ini' : ('game','lua_game_auto'),\

总结

以上是内存溢出为你收集整理的cocos2dx luabinding C/C++/LUA部分全部内容,希望文章能够帮你解决cocos2dx luabinding C/C++/LUA部分所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1084952.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-27
下一篇 2022-05-27

发表评论

登录后才能评论

评论列表(0条)