cocos2dx lua-binding 源码分析

2022-03-07  本文已影响0人  许彦峰

cocos2dx自带的lua-binding没有处理好std::vector<Vec2>的情况,修改过程如下:

cocos2d-x/tools/bindings-generator/generator.py

通过命令行调用这个py脚本,vscode调试配置


{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: generator",
            "type": "python",
            "request": "launch",
            "program": "E:/project/engineTools/frameworks/cocos2d-x/tools/bindings-generator/generator.py",
            "console": "integratedTerminal",
            "justMyCode": true,
            "cwd": "E:/project/engineTools/frameworks/cocos2d-x/tools/tolua",
            "args": [
                "e:/project/engineTools/frameworks/cocos2d-x/tools/tolua/cocos2dx_curve.ini",
                "-s",
                "cocos2dx_curve",
                "-t",
                "lua",
                "-o",
                "e:/project/engineTools/frameworks/cocos2d-x/cocos/scripting/lua-bindings/auto",
                "-n",
                "lua_cocos2dx_curve_auto"
            ]
        }
    ]
}

需要注意args,和ini的配置有关系

执行逻辑流程:

比较核心的2段代码

# 在Generator的构造函数里面,会解析ini配置里面的参数
generator = Generator(gen_opts)
# 开始正式生成代码    
generator.generate_code()
definitions:
  # the names of the functions - we use this to generate the code and to register the functions in
  # the javascript class
  ifunction: "lua_${generator.prefix}_${class_name}_${func_name}"
  sfunction: "lua_${generator.prefix}_${class_name}_${func_name}"
  constructor: "lua_${generator.prefix}_${class_name}_constructor"  
conversions:
  # some times you want to use a special native type when converting from spidermonkey to native
  # the most common case would be from JS-boolean to bool. Using "bool" will fail here since we
  # pass the address to the conversion method, and a JSBool is defined as an integer in spidermonkey
  native_types:
    float: "double"
  ns_map:
    "cocos2d::experimental::ui::": "ccexp."
  to_native:
    # lua to native
    int: "ok &= luaval_to_int32(tolua_S, ${arg_idx},(int *)&${out_value}, \"${lua_namespaced_class_name}:${func_name}\")"
  from_native:
    # native to lua
    int: "tolua_pushnumber(tolua_S,(lua_Number)${in_value})"

layout_head.h

\#include "base/ccConfig.h"
#if $macro_judgement
$macro_judgement
#end if 
\#ifndef __${prefix}_h__
\#define __${prefix}_h__
#if $hpp_headers
#for header in $hpp_headers
\#include "${header}"
#end for
#end if 

\#ifdef __cplusplus
extern "C" {
\#endif
\#include "tolua++.h"
\#ifdef __cplusplus
}
\#endif

int register_all_${prefix}(lua_State* tolua_S);

模板结果

#include "base/ccConfig.h"
#ifndef __cocos2dx_curve_h__
#define __cocos2dx_curve_h__

#ifdef __cplusplus
extern "C" {
#endif
#include "tolua++.h"
#ifdef __cplusplus
}
#endif

int register_all_cocos2dx_curve(lua_State* tolua_S);



#endif // __cocos2dx_curve_h__

以layout_foot.h为例

\#endif // __${prefix}_h__
#if $macro_judgement
\#endif //$macro_judgement
#end if  

主要的lua-binding逻辑代码

def  generate_code(self):
   # ... 检查语法是否正确等相关工作
   self._parse_headers() #解析c++的头文件  

def _parse_headers(self):
  for header in self.headers:
    self._deep_iterate(tu.cursor) # 依次迭代检测语法树

def _deep_iterate(self, cursor, depth=0):
   if is_targeted_class and self.in_listed_classes(cursor.displayname):
        if not self.generated_classes.has_key(cursor.displayname):
            nclass = NativeClass(cursor, self)
            nclass.generate_code() # 真正开始生成binding代码的入口

class NativeClass(object):
  def  generate_code(self):
        # ... 前后的逻辑都是准备工作,创建模板引擎
        for m in self.methods_clean():
            m['impl'].generate_code(self)
        for m in self.static_methods_clean():
            m['impl'].generate_code(self)
        if self.generator.script_type == "lua":
            for m in self.override_methods_clean():
                m['impl'].generate_code(self, is_override = True)
        for m in self.public_fields:
            if self.generator.should_bind_field(self.class_name, m.name):
                m.generate_code(self)

class  NativeFunction(object): # 生成具体的function
  def generate_code(self):
     # 到这里就需要看具体对应的模板文件逻辑了
    Template(file=os.path.join(gen.target, "templates", "sfunction.c"),
                            searchList=[current_class, self])

在sfunction.c模板文件中有调用

${arg.to_native({})}

to_native又回到了Python的脚本里面,这里就需要关注下c++头文件,举个例子:

class Action :public ActionInterval {
public:
    static Action* create(float duration, std::vector<Vec2> points);
};
 def to_native(self, convert_opts): #344
  keys = []
  # self.name为函数参数的类型
  # 比如Action的create函数参数分别为float、cocos2d::Vec2(注意这里是带namespace的)
  keys.append(self.name)
  if self.is_object:
    # 这里判断
    if not NativeType.dict_has_key_re(to_native_dict, keys):
      keys.append("object")
    def dict_has_key_re(dict, real_key_list):
        for real_key in real_key_list:
            for (k, v) in dict.items():
                if k.startswith('@'):
                    k = k[1:]
                    # @开头的,视为正则表达式,@之后的内容为正则
                    match = re.match("^" + k + "$", real_key)
                    if match:
                        return True
                else:
                    if k == real_key:
                        return True
        return False

比如Action的create方法,最终正则计算为

reg.match("^vector<cocos2d::Vec2.*>$", "vector<cocos2d::Vec2, std::allocator<cocos2d::Vec2> >")

conversions.yml需要这么写

conversions:
  to_native:
    "@vector<cocos2d::Vec2.*>":"这个是能够匹配上的",
    "@vector<Vec2.*>":"这个是无法匹配上的,因为没有写namespace",

value内容的书写,也需要遵守cheetah的语法,才能被模板引擎正确的替换。

最终结果:

conversions:
 to_native:
   "@vector<cocos2d::Vec2.*>": "ok &= luaval_to_std_vector_vec2(tolua_S, ${arg_idx}, &${out_value}, \"${lua_namespaced_class_name}:${func_name}\")"

hpp里面目录的问题

targets/lua/templates/layout_head.c

\#include "scripting/lua-bindings/auto/${out_file}.hpp"
#if $macro_judgement
$macro_judgement
#end if
#for header in $headers
    #set relative = os.path.relpath(header, $search_path)
    #if not '..' in relative
\#include "${relative.replace(os.path.sep, '/')}"
    #else
\#include "${os.path.basename(header)}"
    #end if
#end for
\#include "scripting/lua-bindings/manual/tolua_fix.h"
\#include "scripting/lua-bindings/manual/LuaBasicConversions.h"
#if $cpp_headers
#for header in $cpp_headers
\#include "${header}"
#end for
#end if

中间的目录跟$headers$search_path有关系,

 'search_path': os.path.abspath(os.path.join(userconfig.get('DEFAULT', 'cocosdir'), 'cocos')),

os.path.relpath的逻辑,导致只有在相对于cocos目录时,才会显示带目录的include,否则只显示文件名,所以想要带上路径的文件名,在不改动Python的前提下,只能放到cocos目录了

上一篇 下一篇

猜你喜欢

热点阅读