开发工具【免费下载链接】sol2Sol3 (sol2 v3.0) - a C - Lua API wrapper with advanced features and top notch performance - is here, and its great! Documentation:项目地址https://gitcode.com/gh_mirrors/so/sol2点击查看免费下载导读本文基于 sol2 官方教程 cxx-in-lua.rst完整讲解如何把 C 自定义类型usertype / UDT注册进 Lua从定义一个普通struct player到用new_usertype绑定构造函数、成员函数、属性与只读变量再到运行时在 Lua 侧为类表追加方法。读完本文你将掌握 sol2 中最核心的把 C 类搬进 Lua的完整实战流程并了解构造函数、属性、只读、运行时扩展、继承、静态变量等进阶能力及其源码依据。前置知识建议先阅读 getting-started.rst 完成环境搭建需要一份 Lua 发行版如原生 Lua 或 LuaJITsol2 本身只封装 Lua API以及 functions.rst 了解函数注册。本文所有代码取自仓库内可直接编译运行的示例 usertype_advanced.cpp。一、两种把 C 对象交给 Lua的方式sol2 中C 自定义类型进入 Lua 有两条路径不注册直接传值如果你只是想把一个 C 对象传进 Lua例如作为参数传递、存放在全局变量里甚至不需要注册 usertype直接lua[p2] player(0);即可Lua 会把它作为 userdata 保存。注册 usertype 才能用成员如果要在 Lua 里访问该类型的成员变量、调用成员函数就必须先用new_usertype注册。文档明确说明If you dont call any member variables or functions, then you dont even have to register the usertype at all: just pass it through. But if you want variables and functions on your usertype inside of Lua, you need to register it.而且有一个非常实用的细节先传值、后注册也是允许的。示例在注册player之前就先执行了lua[p2] player(0);并注释说明此时 userdata 尚未绑定元表之后注册 usertype 时 sol2 会自动为它补上正确的 metatable。这一点在 usertype_basics.cpp 与 property.cpp 中也有同样的做法后者注释为 Yes, you can register after you set a value and it will connect up the usertype automatically。二、定义一个普通的 C 类player教程使用的示例类player定义在头文件示例源码中是 usertype_advanced.cpp 第 10–46 行:struct player { public: int bullets; int speed; player() : player(3, 100) { } player(int ammo) : player(ammo, 100) { } player(int ammo, int hitpoints) : bullets(ammo), hp(hitpoints) { } void boost() { speed 10; } bool shoot() { if (bullets 1) return false; --bullets; return true; } void set_hp(int value) { hp value; } int get_hp() const { return hp; } private: int hp; };这是一个相当精简的类三个构造函数默认、单参、双参、两个成员函数、一个私有成员hp通过 getter/setter 暴露。教程指出我们不希望在 Lua 里用原生 metatable 手工重写这套逻辑而是要让它以近乎原生对象的方式直接融入 Lua。三、目标让这段 Lua 脚本跑通我们希望下面的 Lua 脚本能正常执行取自 usertype_advanced.cpp 第 95–128 行的player_script-- call single argument integer constructor p1 player.new(2) -- p2 is still here from being -- set with lua[p2] player(0); below local p2shoots p2:shoot() assert(not p2shoots) -- had 0 ammo -- set variable property setter p1.hp 545 -- get variable through property unqualified_getter print(p1.hp) assert(p1.hp 545) local did_shoot_1 p1:shoot() print(did_shoot_1) print(p1.bullets) local did_shoot_2 p1:shoot() print(did_shoot_2) print(p1.bullets) local did_shoot_3 p1:shoot() print(did_shoot_3) -- can read print(p1.bullets) -- would error: is a readonly variable, cannot write -- p1.bullets 20 p1:boost() -- call the function we define at runtime from a Lua script p1:brake()这段脚本对 usertype 提出了这些要求player.new(2)—— 调用单参构造函数用点号语法调用p2:shoot()—— 成员函数调用冒号语法隐式传入selfp1.hp 545/print(p1.hp)—— 以成员变量语法读写一个私有字段底层走 setter/getterprint(p1.bullets)—— 读取公有成员p1:boost()—— 修改成员函数p1:brake()——在 C 中并不存在的方法需要运行时在 Lua 侧为类表动态追加。注意 Lua 中成员函数必须用冒号:调用冒号表示隐式传入self而构造函数用点号.new(...)调用不传self。四、用 new_usertype 完成绑定new_usertype是绑定的核心入口。它同时定义在sol::table与sol::state/sol::state_view上教程中的 table、state 文档均有说明本文直接用在sol::state lua上。绑定代码取自 usertype_advanced.cpp 第 53–85 行// set a variable p2 of type player with 0 ammo lua[p2] player(0); // make usertype metatable sol::usertypeplayer player_type lua.new_usertypeplayer(player, // 3 constructors sol::constructorsplayer(), player(int), player(int, int)()); // typical member function that returns a variable player_type[shoot] player::shoot; // typical member function player_type[boost] player::boost; // gets or set the value using member variable syntax player_type[hp] sol::property(player::get_hp, player::set_hp); // read and write variable player_type[speed] player::speed; // can only read from, not write to // .set(foo, bar) is the same as [foo] bar; player_type.set(bullets, sol::readonly(player::bullets));4.1 构造函数sol::constructorssol::constructorsplayer(), player(int), player(int, int)()按函数签名逐个列出要暴露的构造函数。文档usertype.rst说明若把constructors...作为第一个参数传入sol2 会自动以new为名注册构造函数若类型是默认可构造的default-constructiblesol2 还会自动生成new成员若不希望自动生成构造函数可传入sol::no_constructor也支持旧式sol::typesArgs...列表写法默认支持重载。从实现看table.hpp 第 63–81 行的new_usertype重载会对参数做静态断言参数必须成对出现并将第一个构造函数参数绑定到meta_function::construct其余键值对通过tuple_set批量设置——这也是为什么new_usertype的参数是名字 绑定值成对排列的原因。4.2 成员函数直接绑定成员指针player_type[shoot] player::shoot; player_type[boost] player::boost;成员函数指针player::shoot绑定到名字shoot在 Lua 中用p1:shoot()调用。usertype通过operator[]赋值即可完成绑定等价于.set(...)示例中注释明确说明.set(foo, bar)与[foo] bar等价。4.3 属性sol::property 包装 getter/setterplayer_type[hp] sol::property(player::get_hp, player::set_hp);hp是私有成员但通过sol::property(getter, setter)把它暴露为 Lua 侧的普通变量p1.hp 545会调用 setterprint(p1.hp)会调用 getter。property的实现见 property.hpp 第 37–96 行内部用property_wrapperR, W同时保存读函数与写函数EBCO 优化空基类并且会根据两个函数自由参数个数自动判断谁是 getter 谁是 setter只传一个函数时若参数少于 2 视为只读 getter否则视为只写 setter。配套的还有readonly_property与writeonly_property。4.4 读写成员变量与只读成员变量player_type[speed] player::speed; // 可读可写 player_type.set(bullets, sol::readonly(player::bullets)); // 只读speed直接绑定公有成员指针Lua 里可读可写bullets用sol::readonly(player::bullets)包装后绑定Lua 侧只能读不能写——脚本中被注释掉的p1.bullets 20一旦取消注释就会报错。readonly的底层实现property.hpp 第 98–118 行是一个readonly_wrapperT内部保存原成员指针通过operator T/operator const T分别暴露读写与只读两种视图从而让 sol2 的绑定机制识别出只读成员对象。更多绑定成员函数的写法usertype_basics.cpp 展示了两种等价方式一步式lua.new_usertypeship(...)与两步式先建sol::usertypeship再逐项赋值。五、运行时扩展把 Lua 函数挂到类表上脚本里用到的brake在 C 中并不存在也没有在绑定代码里注册。教程展示了另一种机制可以直接往类表class table里添加方法无需修改 C 代码。示例的prelude_scriptusertype_advanced.cpp 第 88–93 行function player:brake () self.speed 0 print(we hit the brakes!) end这是标准的 Lua 写法给player类表定义一个方法brake在player的 metatable 上生效所有player实例包括之前创建的p1、p2都能调用。之后脚本中的p1:brake()就会把speed置 0 并打印消息。文档强调usertype.rst 的 runtime functions 一节运行时只能给整个类添加函数不能给单个对象添加。仓库中 runtime_extension.cpp 展示了两种运行时扩展途径既可以通过 C 侧lua[object][func] [](object o) {...}添加也可以通过 Lua 脚本lua.script(function object:print () ... end)添加随后object.new()出来的实例即可调用。六、运行脚本并验证绑定完成后示例依次执行两段脚本lua.script(prelude_script); lua.script(player_script);文档还提示如果你更喜欢把脚本放到文件里可以把注释掉的lua.script_file(prelude_script.lua);和lua.script_file(player_script.lua);打开usertype_advanced.cpp 第 135–138 行此时需确保脚本文件位于可执行文件所在目录或调整路径。player_script中的断言全部通过p2因 0 弹药shoot()返回 falsehp通过 property 写入 545 再读回三次shoot()把弹药从 2 消耗到 0boost()与brake()依次生效。七、更多进阶能力usertype 还有这些玩法教程结尾提纲挈领地列出了更多能力全部有对应文档与示例可查7.1 构造函数的三套进阶方案sol::initializers(func1, func2, ...)处理初始化函数。函数签名必须是func(T*, Arguments...)或func(T, Arguments...)Lua 负责分配内存C 侧应使用placement new在已分配内存上构造不能直接new再赋给指针。见 usertype_initializers.cpp 中的holy::initialize。sol::factories(func1, func2, ...)工厂函数返回std::unique_ptrT、std::shared_ptrT或T等可用来阻止默认.new()的生成。同例中的holy::create返回std::unique_ptrholy, deleter。sol::call_constructor启用local v my_class(24)的类构造语法与 luabind、kaguya 风格兼容。见 usertype_constructors.cpp该例同时演示了sol::meta_function::constructfactoriescall_constructor三种用法并用lua.safe_scriptsol::script_pass_on_error校验脚本结果。7.2 自定义析构私有析构函数用sol::meta_function::garbage_collect或字符串__gcsol::destructor(holy::destroy)绑定自定义析构函数。注意析构函数必须用sol::destructor包装且 Lua 控制内存析构后由 Lua 回收空间因此不能调用delete只能调用析构函数本身。见 usertype_initializers.cpp。7.3 静态函数与全局变量绑定绑定函数的第一个参数若是T*/T则作为成员函数否则作为静态函数在 Lua 中以name.my_function(...)调用如 usertype_special_functions.cpp 中的vec.dot(v1, v2)。用sol::var绑定全局/静态变量sol::var(test::number)按值拷贝sol::var(std::ref(test::number))按引用绑定——C 侧修改会同步反映到 Lua。见 usertype_var.cpp该例还特别警告不要对即将离开作用域的局部变量使用std::ref引用绑定否则会悬垂引用。7.4 重载成员函数重载必须使用sol::overload(Func1, Func2, ...)语法——重复绑定同名函数并不会构成重载。文档同时提醒涉及继承时sol2 的重载采用先匹配先调用策略应把参数最派生最具体的版本放在最前面或在中间 C 函数里做类型转换。见 usertype.rst 的 overloading 与 inheritance overloading 两节。7.5 运算符与自动注册automagic运算符如、-通过sol::meta_function::addition等元方法名绑定配合sol::resolve签名(::operator)消除重载歧义meta_function枚举完整列出了所有可绑定的运算符与特殊方法名。见 usertype_special_functions.cpp。sol2 对支持to_string、operator()、begin()/end()、size()、比较运算符的类型会自动注册对应元方法pairs 迭代需 Lua 5.2LuaJIT/Lua 5.1 不支持可通过特化sol::is_automagicalT关闭自动注册用sol::is_to_stringableT关闭自动 to_string。7.6 继承必须显式注册全部基类sol::base_classes, sol::basesA()这样 sol2 才能在运行时把派生指针调整为基类指针。文档要求列出所有基类及其基类并提示sol2 不支持运行时从基类向下转型down-cast且当前实现中基类成员的自动查找可能带来性能开销稳妥做法是显式把基类成员也列在派生类绑定中。参考 inheritance.cpp 与 inheritance.rst。7.7 性能与编译提示文档提醒usertype.rst 的 performance note如果同时绑定成员变量与成员函数成员函数调用会有一个固定开销这是 Lua 实现本身的限制若只绑定函数不绑定变量sol2 会自动优化运行时获得最高性能。因此建议在易用性与性能之间权衡不要把所有东西都改成函数。大量使用 usertype 时MSVC 与 clang/gcc 可能需要额外编译选项参见 compilation.rst。八、更深入的自定义走向 customizationusertype 是把 C 类直接搬进 Lua的默认路径。如果需要超出 usertype 的定制例如把某个结构拆成多个 Lua 值、或让类型以特殊方式进出栈sol2 提供了四个扩展点sol_lua_check、sol_lua_get、sol_lua_push、sol_lua_check_get通过模板特化即可实现参见 customization.rst 与示例 customization_multiple.cpp。九、从本文出发仓库里的更多资源完整可运行示例usertype_advanced.cpp本文主线、usertype_basics.cpp两种绑定写法、usertype_constructors.cpp、usertype_initializers.cpp、usertype_special_functions.cpp、usertype_var.cpp。官方 API 文档usertype.rst、property.rst、table.rst、state.rst。相关教程functions.rst、ownership.rst所有权与生命周期、customization.rst。头文件实现入口table.hppnew_usertype各重载、table_core.hpp声明与new_enum等、property.hppproperty/readonly/var。掌握了new_usertype 各类绑定标签后你可以把越来越复杂的 C 类与数据结构平滑地交给 Lua 复用——这正是 sol2 的核心价值所在。赞分享开发工具【免费下载链接】sol2Sol3 (sol2 v3.0) - a C - Lua API wrapper with advanced features and top notch performance - is here, and its great! Documentation:项目地址https://gitcode.com/gh_mirrors/so/sol2点击查看免费下载相关推荐sol2 教程用 sol::usertypeT 将 C 结构与类无缝暴露给 Luasol2 教程用 sol::usertypeT 将 C 结构与类无缝暴露给 Lua 导读 本文以 sol2 官方 API 文档 documentati开发工具Sol2自定义点扩展如何为任意C类型实现Lua交互Sol2自定义点扩展如何为任意C类型实现Lua交互 Sol2是一个高性能的C与Lua API包装库提供了强大的自定义点扩展功能让开发者能够为任意C开发工具Sol2用户类型深度解析创建自定义C对象在Lua中的完整生命周期Sol2用户类型深度解析创建自定义C对象在Lua中的完整生命周期 在C与Lua的交互开发中 用户类型usertype 是连接两种语言的关键桥梁。开发工具上一篇distribution 仓库中的 azblob SDK 迁移实战从 azure-storage-blob-go 平滑升级到新 azblob 模块下一篇突破设备与延迟限制Sunshine游戏串流自建解决方案全攻略创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考