From f7761cf2a63201f2d560da39f20b5fbf0ac43cdb Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 26 Feb 2026 20:52:33 +0000 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20implement=20mcpplibs.capi.lua=20?= =?UTF-8?q?=E2=80=94=20C++23=20module=20binding=20for=20Lua=20C=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add src/capi/lua.cppm (module interface) and src/capi/lua.cpp (implementation) - Wrap lua.h, lauxlib.h, lualib.h in mcpplibs::capi::lua namespace - Types: State, Number, Integer, CFunction, L_Reg, L_Buffer, etc. - Constants: status codes, type tags, operators, GC options, hook masks - Functions: 100+ bindings covering state, stack, push/access, tables, globals, calls, coroutines, GC, debug, auxiliary library, standard libs - Use extern C wrapper header to fix GCC C++ modules linkage issue - 97 comprehensive Google Test cases covering all API categories - 4 examples: basic, table, function, eval - Update xmake.lua, CMakeLists.txt, CI workflow, README, architecture docs - Design docs and task breakdown in docs/pr/ - Remove old templates.cppm placeholder module Co-authored-by: SPeak --- .github/workflows/ci.yml | 16 +- AGENTS.md | 27 + CMakeLists.txt | 40 +- README.md | 110 ++-- config.xlings | 4 +- docs/architecture.md | 221 +++----- docs/pr/design.md | 157 ++++++ docs/pr/tasks.md | 82 +++ examples/basic.cpp | 35 +- examples/eval.cpp | 117 ++++ examples/function.cpp | 120 +++++ examples/table.cpp | 95 ++++ examples/xmake.lua | 15 +- src/capi/lua.cpp | 422 +++++++++++++++ src/capi/lua.cppm | 343 ++++++++++++ src/capi/lua_headers.h | 10 + src/templates.cppm | 13 - tests/main.cpp | 1100 +++++++++++++++++++++++++++++++++++++- tests/xmake.lua | 7 +- xmake.lua | 8 +- 20 files changed, 2716 insertions(+), 226 deletions(-) create mode 100644 AGENTS.md create mode 100644 docs/pr/design.md create mode 100644 docs/pr/tasks.md create mode 100644 examples/eval.cpp create mode 100644 examples/function.cpp create mode 100644 examples/table.cpp create mode 100644 src/capi/lua.cpp create mode 100644 src/capi/lua.cppm create mode 100644 src/capi/lua_headers.h delete mode 100644 src/templates.cppm diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 80d23cf..6bdea37 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,10 +50,14 @@ jobs: xmake -y -vv -j$(nproc) - name: Test - run: xmake run templates_test + run: xmake run capi_lua_test - name: Run examples - run: xmake run basic + run: | + xmake run basic + xmake run table + xmake run function + xmake run eval build-macos: runs-on: macos-14 @@ -90,7 +94,11 @@ jobs: xmake -y -vv -j$env:NUMBER_OF_PROCESSORS - name: Test - run: xmake run templates_test + run: xmake run capi_lua_test - name: Run examples - run: xmake run basic + run: | + xmake run basic + xmake run table + xmake run function + xmake run eval diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..864461c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,27 @@ +## Cursor Cloud specific instructions + +### Project overview + +C++23 module binding for Lua C API: `import mcpplibs.capi.lua;`. Uses xmake as primary build system, GCC 15.1 for C++23 module support. + +### Environment + +- **xlings** provides both xmake and GCC 15.1; install via `curl -fsSL https://raw.githubusercontent.com/d2learn/xlings/refs/heads/main/tools/other/quick_install.sh | XLINGS_NON_INTERACTIVE=1 bash` +- After installing xlings, run `xlings install gcc@15.1 -y` to get GCC 15.1 +- PATH must include `$HOME/.xlings/bin` and `$HOME/.xlings/subos/current/bin` + +### Build, test, run + +Standard commands documented in `README.md`: +- `xmake f -m release -y` to configure +- `xmake -y -j$(nproc)` to build +- `xmake run capi_lua_test` to run tests (97 tests) +- `xmake run basic|table|function|eval` to run examples + +### Important caveats + +- **GCC C++ modules + C headers**: Lua headers must be wrapped with explicit `extern "C"` via `src/capi/lua_headers.h` in the global module fragment. Without this, GCC applies C++ name mangling to the C functions and linking fails. This is a GCC 15.1 behavior with C++ modules. +- **Interface + implementation split**: The module uses `.cppm` for declarations and `.cpp` for definitions. Functions cannot be `inline` in the `.cppm` because GCC would inline them at the import site where the Lua C declarations are not available, causing link errors. +- **Lua dependency**: xmake auto-downloads Lua via `add_requires("lua")`. Both `tests/xmake.lua` and `examples/xmake.lua` must include their own `add_requires("lua")` and `add_packages("lua")`. +- **Test target**: The test target is named `capi_lua_test` (not `templates_test` from the original template). +- **mcpp style**: Follow [mcpp-style-ref](https://github.com/mcpp-community/mcpp-style-ref). See `.agents/skills/mcpp-style-ref/SKILL.md` for details. diff --git a/CMakeLists.txt b/CMakeLists.txt index bd594ce..1ed959b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,26 +5,46 @@ set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "a9e1cf81-9932-4810-974b-6eccaf14e457") set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_MODULE_STD 1) -project(mcpplibs-templates VERSION 1.0.0 LANGUAGES CXX) +project(mcpplibs-capi-lua VERSION 1.0.0 LANGUAGES CXX) + +# Find Lua +find_package(PkgConfig QUIET) +if(PkgConfig_FOUND) + pkg_check_modules(LUA QUIET lua5.4 lua-5.4 lua54 lua) +endif() +if(NOT LUA_FOUND) + find_package(Lua QUIET) + if(LUA_FOUND) + set(LUA_INCLUDE_DIRS ${LUA_INCLUDE_DIR}) + set(LUA_LIBRARIES ${LUA_LIBRARIES}) + endif() +endif() # Library -add_library(mcpplibs-templates STATIC) +add_library(mcpplibs-capi-lua STATIC) -file(GLOB_RECURSE MODULE_SOURCES "src/*.cppm") +file(GLOB_RECURSE MODULE_SOURCES "src/capi/*.cppm") -target_sources(mcpplibs-templates +target_sources(mcpplibs-capi-lua PUBLIC FILE_SET CXX_MODULES FILES ${MODULE_SOURCES} ) +if(LUA_FOUND) + target_include_directories(mcpplibs-capi-lua PUBLIC ${LUA_INCLUDE_DIRS}) + target_link_libraries(mcpplibs-capi-lua PUBLIC ${LUA_LIBRARIES}) +endif() + # Test -add_executable(templates_test tests/main.cpp) -target_link_libraries(templates_test PRIVATE mcpplibs-templates) +add_executable(capi_lua_test tests/main.cpp) +target_link_libraries(capi_lua_test PRIVATE mcpplibs-capi-lua) enable_testing() -add_test(NAME mcpplibs-templates-test COMMAND templates_test) +add_test(NAME mcpplibs-capi-lua-test COMMAND capi_lua_test) -# Example -add_executable(basic examples/basic.cpp) -target_link_libraries(basic PRIVATE mcpplibs-templates) +# Examples +foreach(example basic table function eval) + add_executable(${example} examples/${example}.cpp) + target_link_libraries(${example} PRIVATE mcpplibs-capi-lua) +endforeach() diff --git a/README.md b/README.md index d38f923..8d30700 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,45 @@ -# mcpplibs templates +# mcpplibs capi-lua -> C++23 模块化库项目模板 - `import mcpplibs.templates;` +> Lua C API 的 C++23 模块化绑定 - `import mcpplibs.capi.lua;` -基于 C++23 模块的库项目模板,提供标准化的项目结构、构建配置和 CI/CD 流水线,帮助快速创建 mcpplibs 风格的模块化 C++ 库。 +将 Lua C API(`lua.h` / `lauxlib.h` / `lualib.h`)封装为 C++23 模块,让其他 mcpplibs 模块可以通过 `import` 直接使用 Lua,无需手动 `#include` C 头文件。 ## 特性 -- **C++23 模块** — `import mcpplibs.templates;` +- **C++23 模块** — `import mcpplibs.capi.lua;` +- **1:1 映射** — 接口与 Lua C API 完全对应,零额外抽象 +- **零开销** — 所有封装均为 `inline` 函数和 `constexpr` 常量 - **双构建系统** — 同时支持 xmake 和 CMake - **CI/CD** — GitHub Actions 多平台构建(Linux / macOS / Windows) -- **标准化结构** — 遵循 [mcpp-style-ref](https://github.com/mcpp-community/mcpp-style-ref) 编码规范 -- **开箱即用** — 包含示例、测试和架构文档 +- **遵循 mcpp-style-ref** — 标准化命名和项目结构 -## 项目结构 +## 命名映射 -``` -mcpplibs-templates/ -├── src/ # 模块源码 -│ └── templates.cppm # 主模块接口 -├── tests/ # 测试 -│ ├── main.cpp -│ └── xmake.lua -├── examples/ # 示例 -│ ├── basic.cpp -│ └── xmake.lua -├── docs/ # 文档 -│ └── architecture.md -├── .github/workflows/ # CI/CD -│ └── ci.yml -├── xmake.lua # xmake 构建配置 -├── CMakeLists.txt # CMake 构建配置 -└── config.xlings # xlings 工具链配置 -``` +在 `mcpplibs::capi::lua` 命名空间内,去掉 C API 前缀避免冗余: + +| C API | 模块内 | 示例 | +|---|---|---| +| `lua_State` | `State` | `lua::State*` | +| `lua_gettop()` | `gettop()` | `lua::gettop(L)` | +| `luaL_newstate()` | `L_newstate()` | `lua::L_newstate()` | +| `LUA_OK` | `OK` | `lua::OK` | +| `luaopen_base()` | `open_base()` | `lua::open_base(L)` | ## 快速开始 ```cpp import std; -import mcpplibs.templates; +import mcpplibs.capi.lua; + +namespace lua = mcpplibs::capi::lua; int main() { - mcpplibs::templates::hello_mcpp(); + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + + lua::L_dostring(L, "print('hello from Lua!')"); + + lua::close(L); return 0; } ``` @@ -57,8 +56,11 @@ xlings install ```bash xmake build # 构建库 -xmake run basic # 运行基础示例 -xmake run templates_test # 运行测试 +xmake run basic # 基本用法示例 +xmake run table # 表操作示例 +xmake run function # 函数注册示例 +xmake run eval # 脚本求值示例 +xmake run capi_lua_test # 运行测试 ``` **使用 CMake** @@ -69,6 +71,34 @@ cmake --build build ctest --test-dir build ``` +## 项目结构 + +``` +mcpplibs-capi-lua/ +├── src/ # 模块源码 +│ └── capi/ +│ └── lua.cppm # 主模块接口 (export module mcpplibs.capi.lua) +├── tests/ # 测试 +│ ├── main.cpp # Google Test 测试套件 +│ └── xmake.lua +├── examples/ # 示例 +│ ├── basic.cpp # 基本用法 +│ ├── table.cpp # 表操作 +│ ├── function.cpp # 函数注册与回调 +│ ├── eval.cpp # 脚本求值 +│ └── xmake.lua +├── docs/ # 文档 +│ ├── architecture.md # 架构文档 +│ └── pr/ +│ ├── design.md # 设计文档 +│ └── tasks.md # 任务清单 +├── .github/workflows/ # CI/CD +│ └── ci.yml +├── xmake.lua # xmake 构建配置 +├── CMakeLists.txt # CMake 构建配置 +└── config.xlings # xlings 工具链配置 +``` + ## 集成到构建工具 ### xmake @@ -76,20 +106,34 @@ ctest --test-dir build ```lua add_repositories("mcpplibs-index https://github.com/mcpplibs/mcpplibs-index.git") -add_requires("templates") +add_requires("capi-lua") target("myapp") set_kind("binary") set_languages("c++23") add_files("main.cpp") - add_packages("templates") + add_packages("capi-lua") set_policy("build.c++.modules", true) ``` +## 覆盖范围 + +封装了 Lua 5.4 C API 的核心功能: + +- **类型** — `State`, `Number`, `Integer`, `CFunction`, `L_Reg`, `L_Buffer` 等 +- **常量** — 状态码、类型标签、运算符、GC 选项、Hook 掩码等 +- **状态管理** — `L_newstate`, `close`, `newthread`, `version` 等 +- **栈操作** — `gettop`, `settop`, `pop`, `pushvalue`, `rotate`, `insert`, `remove`, `replace` 等 +- **入栈/取值** — `pushnil/number/integer/string/boolean`, `tonumber/tointeger/tostring/toboolean` 等 +- **表操作** — `newtable`, `getfield/setfield`, `gettable/settable`, `rawget/rawset`, `next` 等 +- **函数调用** — `call`, `pcall`, `pushcfunction`, `pushcclosure` 等 +- **辅助库** — `L_dostring`, `L_dofile`, `L_loadstring`, `L_ref/L_unref`, `L_setfuncs`, `L_error` 等 +- **标准库** — `open_base`, `open_math`, `open_string`, `open_table`, `open_io`, `open_os` 等 +- **调试** — `getstack`, `getinfo`, `sethook`, `gethook` 等 + ## 相关链接 - [mcpp-style-ref | 现代C++编码/项目风格参考](https://github.com/mcpp-community/mcpp-style-ref) -- [mcpplibs/cmdline | 命令行解析库](https://github.com/mcpplibs/cmdline) +- [mcpplibs/templates | 项目模板](https://github.com/mcpplibs/templates) - [mcpp社区官网](https://mcpp.d2learn.org) - [mcpp | 现代C++爱好者论坛](https://mcpp.d2learn.org/forum) -- [入门教程: 动手学现代C++](https://github.com/Sunrisepeak/mcpp-standard) diff --git a/config.xlings b/config.xlings index db3c96c..2a83621 100644 --- a/config.xlings +++ b/config.xlings @@ -1,4 +1,4 @@ -xname = "mcpplibs-templates" +xname = "mcpplibs-capi-lua" -- install by `xlings install` xim = { @@ -6,4 +6,4 @@ xim = { cmake = "4.0.2", ninja = "1.12.1", cpp = "", -- gcc15 or mingw13 -} \ No newline at end of file +} diff --git a/docs/architecture.md b/docs/architecture.md index 0ee2b6c..475f810 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,25 +1,32 @@ # 架构文档 -> mcpplibs/templates 项目架构与设计说明 +> mcpplibs/capi-lua 项目架构与设计说明 ## 概述 -`mcpplibs/templates` 是 mcpplibs 生态中的标准化 C++23 模块库项目模板。它提供了一套完整的项目骨架,包含模块源码、测试、示例、构建配置和 CI/CD 流水线,帮助开发者快速创建符合 [mcpp-style-ref](https://github.com/mcpp-community/mcpp-style-ref) 编码规范的模块化 C++ 库。 +`mcpplibs/capi-lua` 为 Lua C API 提供 C++23 模块化绑定,使其他 mcpplibs 模块可以通过 `import mcpplibs.capi.lua;` 直接使用 Lua,无需手动 `#include` C 头文件。 ## 目录结构 ``` -mcpplibs-templates/ +mcpplibs-capi-lua/ ├── src/ # 模块源码目录 -│ └── templates.cppm # 主模块接口文件 +│ └── capi/ +│ └── lua.cppm # 主模块接口文件 ├── tests/ # 测试目录 -│ ├── main.cpp # 测试入口 +│ ├── main.cpp # Google Test 测试入口 │ └── xmake.lua # 测试构建配置 ├── examples/ # 示例目录 -│ ├── basic.cpp # 基础用法示例 +│ ├── basic.cpp # 基本用法示例 +│ ├── table.cpp # 表操作示例 +│ ├── function.cpp # 函数注册示例 +│ ├── eval.cpp # 脚本求值示例 │ └── xmake.lua # 示例构建配置 ├── docs/ # 文档目录 -│ └── architecture.md # 架构文档(本文件) +│ ├── architecture.md # 架构文档(本文件) +│ └── pr/ +│ ├── design.md # 设计文档 +│ └── tasks.md # 任务清单 ├── .github/workflows/ # CI/CD 配置 │ └── ci.yml # GitHub Actions 流水线 ├── xmake.lua # xmake 构建配置(主配置) @@ -33,76 +40,80 @@ mcpplibs-templates/ ## 模块架构 -### 模块结构 +### 封装模式 ```mermaid graph TD - subgraph moduleLayer [模块层] - M["mcpplibs.templates"] + subgraph cHeaders [C 头文件(全局模块片段)] + H1["lua.h"] + H2["lauxlib.h"] + H3["lualib.h"] end - subgraph srcLayer [源码] - S["src/templates.cppm"] + subgraph moduleLayer [C++23 模块层] + M["mcpplibs.capi.lua"] end subgraph consumerLayer [使用方] T["tests/main.cpp"] - E["examples/basic.cpp"] + E1["examples/basic.cpp"] + E2["examples/table.cpp"] + E3["examples/function.cpp"] + E4["examples/eval.cpp"] end - S -->|"export module"| M + H1 -->|"#include"| M + H2 -->|"#include"| M + H3 -->|"#include"| M T -->|"import"| M - E -->|"import"| M + E1 -->|"import"| M + E2 -->|"import"| M + E3 -->|"import"| M + E4 -->|"import"| M ``` -### 模块命名规范 - -遵循 [mcpp-style-ref 2.4 模块命名规范](https://github.com/mcpp-community/mcpp-style-ref#24-%E6%A8%A1%E5%9D%97%E5%8F%8A%E6%A8%A1%E5%9D%97%E5%88%86%E5%8C%BA%E5%91%BD%E5%90%8D%E8%A7%84%E8%8C%83): - -- **模块名格式**: `组织.库名` — 例: `mcpplibs.templates` -- **模块分区**: `组织.库名:分区名` — 例: `mcpplibs.templates:utils` -- **命名空间**: 与模块名对应 — 例: `mcpplibs::templates` - ### 模块文件结构 -每个 `.cppm` 文件遵循以下结构: - ```cpp -module; // 全局模块片段(可选,用于传统头文件) +module; // 全局模块片段 + +#include // Lua 核心 API +#include // 辅助库 +#include // 标准库 -export module mcpplibs.templates; // 模块声明 +export module mcpplibs.capi.lua; // 模块声明 -import std; // 模块导入区域 +export namespace mcpplibs::capi::lua { // 统一命名空间 -namespace mcpplibs::templates { // 接口导出与实现 + // 类型别名(using) + using State = ::lua_State; - export void hello_mcpp() { - std::println("hello mcpp!"); - } + // 常量(inline constexpr) + inline constexpr int OK = LUA_OK; + // 函数(inline 转发) + inline State* L_newstate() { return ::luaL_newstate(); } } ``` -### 扩展模块分区 +### 命名映射规则 -当库规模增长时,可按照 [mcpp-style-ref 2.5](https://github.com/mcpp-community/mcpp-style-ref#25-%E5%A4%9A%E6%96%87%E4%BB%B6%E6%A8%A1%E5%9D%97%E5%92%8C%E7%9B%AE%E5%BD%95) 的模式拆分为多个模块分区: +| C API 前缀 | 模块内规则 | 示例 | +|---|---|---| +| `lua_` | 去前缀 | `lua_gettop` → `gettop` | +| `luaL_` | 去 `luaL_` 改为 `L_` | `luaL_newstate` → `L_newstate` | +| `LUA_` | 去前缀 | `LUA_OK` → `OK` | +| `luaopen_` | 去 `luaopen_` 改为 `open_` | `luaopen_base` → `open_base` | -``` -src/ -├── templates.cppm # 主模块,export import 各分区 -├── templates/ -│ ├── core.cppm # export module mcpplibs.templates:core -│ └── utils.cppm # export module mcpplibs.templates:utils -``` - -主模块文件中聚合导出: +### 封装分类 -```cpp -export module mcpplibs.templates; - -export import :core; -export import :utils; -``` +| 类别 | 技术手段 | 数量 | +|---|---|---| +| 类型别名 | `using Type = ::lua_Type;` | 15+ | +| 常量 | `inline constexpr int X = LUA_X;` | 50+ | +| C 函数转发 | `inline ... func(...) { return ::lua_func(...); }` | 80+ | +| 宏转函数 | `inline ... func(...) { ... }` | 20+ | +| 变参转发 | `template inline ...` | 3 | ## 编码规范 @@ -110,12 +121,10 @@ export import :utils; | 类别 | 风格 | 示例 | |------|------|------| -| 类型名 | PascalCase(大驼峰) | `StyleRef`, `HttpServer` | -| 对象/数据成员 | camelCase(小驼峰) | `fileName`, `configText` | -| 函数 | snake_case(下划线) | `load_config()`, `parse_()` | -| 私有成员 | `_` 后缀 | `data_`, `parse_()` | -| 命名空间 | 全小写 | `mcpplibs`, `mcpplibs::templates` | -| 全局变量 | `g` 前缀 | `gStyleRef` | +| 类型名 | PascalCase(大驼峰) | `State`, `Number`, `CFunction` | +| 函数 | snake_case(下划线) | `gettop()`, `L_newstate()` | +| 常量 | UPPER_SNAKE | `OK`, `TNIL`, `MULTRET` | +| 命名空间 | 全小写 | `mcpplibs::capi::lua` | ## 构建系统 @@ -126,9 +135,9 @@ graph LR subgraph xmakeBuild [xmake 构建] X1["xmake.lua"] -->|includes| X2["tests/xmake.lua"] X1 -->|includes| X3["examples/xmake.lua"] - X1 -->|"target: mcpplibs-templates"| LIB["静态库"] - X2 -->|"target: templates_test"| TEST["测试可执行文件"] - X3 -->|"target: basic"| EXAMPLE["示例可执行文件"] + X1 -->|"target: mcpplibs-capi-lua"| LIB["静态库"] + X2 -->|"target: capi_lua_test"| TEST["测试可执行文件"] + X3 -->|"targets: basic, table, function, eval"| EXAMPLES["示例可执行文件"] end ``` @@ -139,112 +148,34 @@ graph LR ```lua add_rules("mode.release", "mode.debug") set_languages("c++23") +add_requires("lua") -target("mcpplibs-templates") +target("mcpplibs-capi-lua") set_kind("static") - add_files("src/*.cppm", { public = true, install = true }) + add_files("src/capi/*.cppm", { public = true, install = true }) + add_packages("lua", { public = true }) set_policy("build.c++.modules", true) includes("examples", "tests") ``` 关键配置说明: -- `set_languages("c++23")` — 启用 C++23 标准 -- `add_files("src/*.cppm", { public = true, install = true })` — 通配模块源文件,支持自动发现新增分区 -- `set_policy("build.c++.modules", true)` — 启用 C++20/23 模块支持 -- `includes("examples", "tests")` — 引入子目录构建配置,保持主文件简洁 - -### CMake - -`CMakeLists.txt` 提供对 CMake 4.0+ 的支持,使用实验性 `import std` 功能: - -```cmake -cmake_minimum_required(VERSION 4.0.2) -set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "a9e1cf81-9932-4810-974b-6eccaf14e457") -set(CMAKE_CXX_STANDARD 23) -set(CMAKE_CXX_MODULE_STD 1) -``` - -### 工具链配置 - -`config.xlings` 定义项目所需的工具链版本: - -```lua -xname = "mcpplibs-templates" - -xim = { - xmake = "3.0.4", - cmake = "4.0.2", - ninja = "1.12.1", - cpp = "", -- gcc15 或 mingw13 -} -``` - -通过 `xlings install` 可一键安装所有依赖工具。 +- `add_requires("lua")` — xmake 自动下载并编译 Lua +- `add_packages("lua", { public = true })` — 公开传递 Lua 头文件和链接库给依赖方 +- `set_policy("build.c++.modules", true)` — 启用 C++23 模块支持 ## CI/CD 流水线 ### 多平台构建矩阵 -```mermaid -graph TD - subgraph ciPipeline [GitHub Actions CI] - TRIGGER["push / pull_request"] --> LINUX["Linux"] - TRIGGER --> MACOS["macOS"] - TRIGGER --> WINDOWS["Windows"] - - LINUX -->|"GCC 15.1 via Xlings"| L_BUILD["构建 + 测试 + 示例"] - MACOS -->|"LLVM 20 via Homebrew"| M_BUILD["仅构建"] - WINDOWS -->|"MSVC"| W_BUILD["构建 + 测试 + 示例"] - end -``` - | 平台 | 编译器 | 构建 | 测试 | 示例 | |------|--------|------|------|------| | Linux (Ubuntu) | GCC 15.1 | Y | Y | Y | | macOS | LLVM 20 | Y | - | - | | Windows | MSVC | Y | Y | Y | -macOS 当前仅构建不运行测试和示例,因为部分模块特性在 Clang/LLVM 上的支持仍在完善中。 - -## 如何基于模板创建新库 - -1. **克隆模板** - -```bash -git clone https://github.com/mcpplibs/templates.git mcpplibs-mylib -cd mcpplibs-mylib -rm -rf .git && git init -``` - -2. **重命名模块** - -将 `templates` 替换为你的库名(例如 `mylib`): - -- `src/templates.cppm` → 修改 `export module mcpplibs.mylib;` -- `xmake.lua` → 修改 target 名称为 `mcpplibs-mylib` -- `CMakeLists.txt` → 修改 project 和 target 名称 -- `config.xlings` → 修改 `xname` - -3. **添加模块分区**(按需) - -在 `src/` 下创建新的 `.cppm` 文件,xmake 会通过通配符 `src/*.cppm` 自动发现。 - -4. **编写测试和示例** - -在 `tests/` 和 `examples/` 下添加对应的源文件和 xmake target。 - -5. **推送并验证 CI** - -```bash -git add . -git commit -m "init: mcpplibs-mylib" -git remote add origin https://github.com/mcpplibs/mylib.git -git push -u origin main -``` - ## 参考 +- [Lua 5.4 Reference Manual](https://www.lua.org/manual/5.4/) - [mcpp-style-ref | 现代C++编码/项目风格参考](https://github.com/mcpp-community/mcpp-style-ref) -- [mcpplibs/cmdline | 命令行解析库(参考项目)](https://github.com/mcpplibs/cmdline) -- [mcpp社区官网](https://mcpp.d2learn.org) +- [mcpplibs/templates | 项目模板](https://github.com/mcpplibs/templates) diff --git a/docs/pr/design.md b/docs/pr/design.md new file mode 100644 index 0000000..418190e --- /dev/null +++ b/docs/pr/design.md @@ -0,0 +1,157 @@ +# mcpplibs.capi.lua 设计文档 + +> Lua C API 的 C++23 模块化绑定方案 + +## 1. 目标 + +为 Lua C API(`lua.h` / `lauxlib.h` / `lualib.h`)提供 C++23 模块化封装,使其他 mcpplibs 模块可以通过 `import mcpplibs.capi.lua;` 直接使用 Lua,无需手动 `#include` C 头文件。 + +## 2. 设计原则 + +1. **最简单的方式** — 不引入额外抽象层,1:1 映射 C API +2. **接口与 C 保持一致** — 函数签名、参数类型完全对应原始 C API +3. **命名空间去前缀** — 在 `mcpplibs::capi::lua` 命名空间内去掉 `lua_` / `luaL_` / `LUA_` / `luaopen_` 前缀,避免冗余 +4. **遵循 mcpp-style-ref** — 类型 PascalCase、常量 UPPER_SNAKE、命名空间全小写 + +## 3. 命名映射规则 + +| C API 前缀 | 模块内规则 | 示例 | +|---|---|---| +| `lua_State` | 类型保留去 `lua_` → `State` | `mcpplibs::capi::lua::State` | +| `lua_Number` | 类型去 `lua_` → `Number` | `mcpplibs::capi::lua::Number` | +| `lua_gettop()` | 函数去 `lua_` → `gettop()` | `mcpplibs::capi::lua::gettop(L)` | +| `luaL_newstate()` | 函数去 `luaL_` → `L_newstate()` | `mcpplibs::capi::lua::L_newstate()` | +| `LUA_OK` | 常量去 `LUA_` → `OK` | `mcpplibs::capi::lua::OK` | +| `LUA_TNIL` | 常量去 `LUA_` → `TNIL` | `mcpplibs::capi::lua::TNIL` | +| `luaopen_base()` | 函数去 `luaopen_` → `open_base()` | `mcpplibs::capi::lua::open_base(L)` | + +## 4. 模块结构 + +``` +src/ +└── capi/ + └── lua.cppm # export module mcpplibs.capi.lua +``` + +### 模块文件布局 + +```cpp +module; + +#include +#include +#include + +export module mcpplibs.capi.lua; + +export namespace mcpplibs::capi::lua { + + // ===== 类型别名 ===== + using State = ::lua_State; + using Number = ::lua_Number; + // ... + + // ===== 常量 ===== + inline constexpr int OK = LUA_OK; + inline constexpr int TNIL = LUA_TNIL; + // ... + + // ===== 函数(inline 转发) ===== + inline State* L_newstate() { return ::luaL_newstate(); } + inline void close(State* L) { ::lua_close(L); } + // ... +} +``` + +### 技术要点 + +1. **全局模块片段** — `#include` C 头文件放在 `module;` 之后、`export module` 之前 +2. **inline 转发函数** — 所有 C 函数和宏都封装为 `inline` 函数,零开销 +3. **constexpr 常量** — C 宏常量转为 `inline constexpr`,类型安全 +4. **变参函数** — `luaL_error` 等变参函数通过 C++ 模板参数包转发 + +## 5. 封装分类 + +### 5.1 类型(using 别名) + +| 模块内名称 | C API 原名 | +|---|---| +| `State` | `lua_State` | +| `Number` | `lua_Number` | +| `Integer` | `lua_Integer` | +| `Unsigned` | `lua_Unsigned` | +| `CFunction` | `lua_CFunction` | +| `KFunction` | `lua_KFunction` | +| `KContext` | `lua_KContext` | +| `Alloc` | `lua_Alloc` | +| `Reader` | `lua_Reader` | +| `Writer` | `lua_Writer` | +| `WarnFunction` | `lua_WarnFunction` | +| `Hook` | `lua_Hook` | +| `Debug` | `lua_Debug` | +| `L_Reg` | `luaL_Reg` | +| `L_Buffer` | `luaL_Buffer` | + +### 5.2 常量(inline constexpr) + +- 类型标签:`TNONE`, `TNIL`, `TBOOLEAN`, `TNUMBER`, `TSTRING`, `TTABLE`, `TFUNCTION`, `TUSERDATA`, `TTHREAD`, `TLIGHTUSERDATA` +- 状态码:`OK`, `YIELD`, `ERRRUN`, `ERRSYNTAX`, `ERRMEM`, `ERRERR`, `ERRFILE` +- 比较运算:`OPEQ`, `OPLT`, `OPLE` +- 算术运算:`OPADD`, `OPSUB`, `OPMUL`, `OPMOD`, `OPPOW`, `OPDIV`, `OPIDIV`, `OPBAND`, `OPBOR`, `OPBXOR`, `OPSHL`, `OPSHR`, `OPUNM`, `OPBNOT` +- 其他:`MULTRET`, `REGISTRYINDEX`, `MINSTACK`, `RIDX_MAINTHREAD`, `RIDX_GLOBALS`, `NOREF`, `REFNIL` + +### 5.3 函数(inline 转发) + +覆盖以下类别: + +- **状态管理** — `close`, `newthread`, `version`, `atpanic` +- **栈操作** — `gettop`, `settop`, `pop`, `pushvalue`, `rotate`, `copy`, `checkstack`, `absindex`, `insert`, `remove`, `replace` +- **入栈** — `pushnil`, `pushnumber`, `pushinteger`, `pushstring`, `pushlstring`, `pushboolean`, `pushcclosure`, `pushcfunction`, `pushlightuserdata`, `pushthread`, `pushglobaltable` +- **类型检查** — `type`, `type_name`, `isnumber`, `isstring`, `isinteger`, `iscfunction`, `isuserdata`, `isnil`, `isboolean`, `istable`, `isfunction`, `islightuserdata`, `isthread`, `isnone`, `isnoneornil` +- **取值** — `tonumberx`, `tointegerx`, `tonumber`, `tointeger`, `toboolean`, `tolstring`, `tostring`, `tocfunction`, `touserdata`, `tothread`, `topointer`, `rawlen` +- **表操作** — `createtable`, `newtable`, `gettable`, `getfield`, `geti`, `settable`, `setfield`, `seti`, `rawget`, `rawgeti`, `rawgetp`, `rawset`, `rawseti`, `rawsetp`, `getmetatable`, `setmetatable`, `next` +- **全局变量** — `getglobal`, `setglobal` +- **调用** — `callk`, `call`, `pcallk`, `pcall` +- **加载** — `load`, `dump` +- **算术/比较** — `arith`, `rawequal`, `compare` +- **其他** — `concat`, `len`, `error`, `status`, `gc`, `newuserdatauv`, `getiuservalue`, `setiuservalue`, `toclose`, `closeslot` +- **辅助库 (luaL)** — `L_newstate`, `L_openlibs`, `L_loadstring`, `L_loadfile`, `L_loadfilex`, `L_loadbufferx`, `L_dostring`, `L_dofile`, `L_ref`, `L_unref`, `L_checkinteger`, `L_checknumber`, `L_checkstring`, `L_optinteger`, `L_optnumber`, `L_checktype`, `L_checkany`, `L_newmetatable`, `L_setmetatable`, `L_getmetatable`, `L_len`, `L_gsub`, `L_setfuncs`, `L_requiref`, `L_traceback`, `L_typename`, `L_error`, `L_argerror`, `L_typeerror`, `L_checkudata`, `L_testudata`, `L_callmeta`, `L_getmetafield`, `L_getsubtable`, `L_where` +- **标准库** — `open_base`, `open_coroutine`, `open_table`, `open_io`, `open_os`, `open_string`, `open_utf8`, `open_math`, `open_debug`, `open_package` + +## 6. 构建配置 + +### xmake(主构建系统) + +```lua +add_requires("lua") + +target("mcpplibs-capi-lua") + set_kind("static") + add_files("src/capi/*.cppm", { public = true, install = true }) + add_packages("lua", { public = true }) + set_policy("build.c++.modules", true) +``` + +### 依赖 + +- **Lua 5.4** — 通过 xmake `add_requires("lua")` 自动下载构建 +- **Google Test** — 测试使用 `add_requires("gtest")` + +## 7. 使用示例 + +```cpp +import std; +import mcpplibs.capi.lua; + +namespace lua = mcpplibs::capi::lua; + +int main() { + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + + lua::L_dostring(L, "print('hello from Lua!')"); + + lua::close(L); + return 0; +} +``` diff --git a/docs/pr/tasks.md b/docs/pr/tasks.md new file mode 100644 index 0000000..36eea75 --- /dev/null +++ b/docs/pr/tasks.md @@ -0,0 +1,82 @@ +# mcpplibs.capi.lua 任务拆解 + +> PR 实施计划与任务清单 + +## 任务列表 + +### Phase 1: 模块实现 + +- [x] **T1** 创建 `src/capi/lua.cppm` — 主模块接口文件 + - 全局模块片段引入 `lua.h` / `lauxlib.h` / `lualib.h` + - 导出类型别名(State, Number, Integer, CFunction 等) + - 导出常量(OK, TNIL, TBOOLEAN, MULTRET 等) + - 导出函数(状态管理、栈操作、入栈、取值、表操作、全局变量、调用、辅助库、标准库) + +### Phase 2: 构建配置 + +- [x] **T2** 更新 `xmake.lua` — 新 target `mcpplibs-capi-lua`,添加 lua 依赖 +- [x] **T3** 更新 `tests/xmake.lua` — 测试 target 关联新库 +- [x] **T4** 更新 `examples/xmake.lua` — 示例 targets 关联新库 +- [x] **T5** 更新 `CMakeLists.txt` — CMake 构建支持 +- [x] **T6** 更新 `config.xlings` — 项目名称 + +### Phase 3: 测试 + +- [x] **T7** 重写 `tests/main.cpp` — 全面的单元测试 + - 状态创建与销毁 + - 栈操作(push/pop/get/set) + - 类型检查 + - 数值操作 + - 字符串操作 + - 布尔操作 + - 表操作(创建、读写、遍历) + - 全局变量 + - Lua 脚本执行(luaL_dostring) + - 函数调用(pcall) + - 辅助库函数 + - 错误处理 + - 常量正确性 + +### Phase 4: 示例 + +- [x] **T8** `examples/basic.cpp` — 基本用法:创建状态、执行脚本、获取结果 +- [x] **T9** `examples/table.cpp` — 表操作:创建表、嵌套表、遍历 +- [x] **T10** `examples/function.cpp` — C 函数注册:注册 C 函数到 Lua、回调 +- [x] **T11** `examples/eval.cpp` — 脚本求值:执行 Lua 代码、获取返回值 + +### Phase 5: 文档与 CI + +- [x] **T12** 更新 `README.md` — 项目说明、用法示例 +- [x] **T13** 更新 `.github/workflows/ci.yml` — CI 流水线适配 +- [x] **T14** 创建 `docs/pr/design.md` — 设计文档 +- [x] **T15** 创建 `docs/pr/tasks.md` — 任务文档(本文件) +- [x] **T16** 更新 `docs/architecture.md` — 架构文档 + +### Phase 6: 验证 + +- [x] **T17** 本地构建通过 +- [x] **T18** 本地测试通过 +- [x] **T19** 本地示例运行通过 +- [x] **T20** 推送 GitHub 并等待 CI 全部通过 + +## 文件变更清单 + +| 文件 | 操作 | 说明 | +|---|---|---| +| `src/capi/lua.cppm` | 新增 | 主模块接口 | +| `src/templates.cppm` | 删除 | 移除模板占位模块 | +| `xmake.lua` | 修改 | 新 target + lua 依赖 | +| `tests/main.cpp` | 重写 | 全面的 Lua 绑定测试 | +| `tests/xmake.lua` | 修改 | 关联新库 | +| `examples/basic.cpp` | 重写 | Lua 基本用法 | +| `examples/table.cpp` | 新增 | 表操作示例 | +| `examples/function.cpp` | 新增 | 函数注册示例 | +| `examples/eval.cpp` | 新增 | 脚本求值示例 | +| `examples/xmake.lua` | 修改 | 多示例 targets | +| `CMakeLists.txt` | 修改 | CMake 适配 | +| `config.xlings` | 修改 | 项目名称 | +| `README.md` | 重写 | 新项目说明 | +| `.github/workflows/ci.yml` | 修改 | CI 适配 | +| `docs/architecture.md` | 修改 | 架构文档适配 | +| `docs/pr/design.md` | 新增 | 设计文档 | +| `docs/pr/tasks.md` | 新增 | 任务文档 | diff --git a/examples/basic.cpp b/examples/basic.cpp index e43bb29..7b0338a 100644 --- a/examples/basic.cpp +++ b/examples/basic.cpp @@ -1,8 +1,37 @@ import std; -import mcpplibs.templates; +import mcpplibs.capi.lua; + +namespace lua = mcpplibs::capi::lua; int main() { - std::println("=== mcpplibs.templates basic example ==="); - mcpplibs::templates::hello_mcpp(); + std::println("=== mcpplibs.capi.lua basic example ===\n"); + + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + + std::println("[1] Lua version: {}", lua::version(L)); + + std::println("[2] Execute Lua code:"); + lua::L_dostring(L, "print(' hello from Lua!')"); + + std::println("[3] Push and read values:"); + lua::pushinteger(L, 42); + lua::pushstring(L, "mcpp"); + lua::pushnumber(L, 3.14); + lua::pushboolean(L, 1); + + std::println(" stack size = {}", lua::gettop(L)); + std::println(" [1] integer = {}", lua::tointeger(L, 1)); + std::println(" [2] string = {}", lua::tostring(L, 2)); + std::println(" [3] number = {}", lua::tonumber(L, 3)); + std::println(" [4] boolean = {}", lua::toboolean(L, 4)); + + std::println("[4] Compute in Lua:"); + lua::L_dostring(L, "result = 6 * 7"); + lua::getglobal(L, "result"); + std::println(" 6 * 7 = {}", lua::tointeger(L, -1)); + + lua::close(L); + std::println("\ndone."); return 0; } diff --git a/examples/eval.cpp b/examples/eval.cpp new file mode 100644 index 0000000..990879c --- /dev/null +++ b/examples/eval.cpp @@ -0,0 +1,117 @@ +import std; +import mcpplibs.capi.lua; + +namespace lua = mcpplibs::capi::lua; + +static void run_and_show(lua::State* L, const char* code) { + std::println(" lua> {}", code); + if (lua::L_dostring(L, code) != lua::OK) { + std::println(" error: {}", lua::tostring(L, -1)); + lua::pop(L, 1); + } +} + +int main() { + std::println("=== mcpplibs.capi.lua eval example ===\n"); + + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + + // --- Evaluate expressions and get results --- + std::println("[1] Evaluate expressions:"); + auto eval = [](lua::State* L, const char* expr) -> bool { + auto code = std::string("return ") + expr; + if (lua::L_dostring(L, code.c_str()) == lua::OK) { + const char* typeName = lua::L_typename(L, -1); + if (lua::isinteger(L, -1)) { + std::println(" {} = {} ({})", expr, lua::tointeger(L, -1), typeName); + } else if (lua::isnumber(L, -1)) { + std::println(" {} = {} ({})", expr, lua::tonumber(L, -1), typeName); + } else if (lua::isboolean(L, -1)) { + std::println(" {} = {} ({})", expr, lua::toboolean(L, -1) ? "true" : "false", typeName); + } else if (lua::isstring(L, -1)) { + std::println(" {} = \"{}\" ({})", expr, lua::tostring(L, -1), typeName); + } else if (lua::isnil(L, -1)) { + std::println(" {} = nil ({})", expr, typeName); + } else { + std::println(" {} = [{}]", expr, typeName); + } + lua::pop(L, 1); + return true; + } + std::println(" {} => error: {}", expr, lua::tostring(L, -1)); + lua::pop(L, 1); + return false; + }; + + eval(L, "1 + 2 * 3"); + eval(L, "2 ^ 10"); + eval(L, "10 // 3"); + eval(L, "10 % 3"); + eval(L, "'hello' .. ' ' .. 'world'"); + eval(L, "#'abcdef'"); + eval(L, "true and false"); + eval(L, "not false"); + eval(L, "type(42)"); + eval(L, "math.pi"); + + // --- Multi-line Lua script --- + std::println("\n[2] Multi-line script:"); + run_and_show(L, + "local sum = 0\n" + "for i = 1, 100 do sum = sum + i end\n" + "print(' sum(1..100) = ' .. sum)\n" + ); + + // --- Error handling --- + std::println("\n[3] Error handling:"); + run_and_show(L, "error('intentional error')"); + + // --- Load and execute chunks --- + std::println("\n[4] Load and execute chunk:"); + int status = lua::L_loadstring(L, + "local function fact(n)\n" + " if n <= 1 then return 1 end\n" + " return n * fact(n - 1)\n" + "end\n" + "return fact(10)\n" + ); + if (status == lua::OK) { + std::println(" chunk loaded successfully"); + if (lua::pcall(L, 0, 1, 0) == lua::OK) { + std::println(" 10! = {}", lua::tointeger(L, -1)); + lua::pop(L, 1); + } + } + + // --- Multiple return values --- + std::println("\n[5] Multiple return values:"); + lua::L_dostring(L, + "function multi_ret()\n" + " return 'hello', 42, true\n" + "end\n" + ); + lua::getglobal(L, "multi_ret"); + if (lua::pcall(L, 0, 3, 0) == lua::OK) { + std::println(" ret[1] = \"{}\" ({})", lua::tostring(L, -3), lua::L_typename(L, -3)); + std::println(" ret[2] = {} ({})", lua::tointeger(L, -2), lua::L_typename(L, -2)); + std::println(" ret[3] = {} ({})", lua::toboolean(L, -1) ? "true" : "false", lua::L_typename(L, -1)); + lua::pop(L, 3); + } + + // --- String buffer operations --- + std::println("\n[6] Buffer operations:"); + lua::L_Buffer buf; + lua::L_buffinit(L, &buf); + lua::L_addstring(&buf, "Lua "); + lua::L_addstring(&buf, std::to_string(static_cast(lua::version(L) / 100)).c_str()); + lua::L_addstring(&buf, "."); + lua::L_addstring(&buf, std::to_string(static_cast(lua::version(L)) % 100).c_str()); + lua::L_pushresult(&buf); + std::println(" built string: \"{}\"", lua::tostring(L, -1)); + lua::pop(L, 1); + + lua::close(L); + std::println("\ndone."); + return 0; +} diff --git a/examples/function.cpp b/examples/function.cpp new file mode 100644 index 0000000..f84abf0 --- /dev/null +++ b/examples/function.cpp @@ -0,0 +1,120 @@ +import std; +import mcpplibs.capi.lua; + +namespace lua = mcpplibs::capi::lua; + +static int cpp_add(lua::State* L) { + auto a = lua::tonumber(L, 1); + auto b = lua::tonumber(L, 2); + lua::pushnumber(L, a + b); + return 1; +} + +static int cpp_greet(lua::State* L) { + auto* name = lua::tostring(L, 1); + lua::pushfstring(L, "hello, %s!", name); + return 1; +} + +static int cpp_range(lua::State* L) { + auto start = lua::tointeger(L, 1); + auto stop = lua::tointeger(L, 2); + lua::newtable(L); + for (lua::Integer i { start }; i <= stop; i++) { + lua::pushinteger(L, i); + lua::rawseti(L, -2, i - start + 1); + } + return 1; +} + +static int cpp_counter(lua::State* L) { + auto count = lua::tointeger(L, lua::upvalueindex(1)); + count++; + lua::pushinteger(L, count); + lua::copy(L, -1, lua::upvalueindex(1)); + return 1; +} + +int main() { + std::println("=== mcpplibs.capi.lua function example ===\n"); + + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + + // --- Register simple functions --- + std::println("[1] Register C++ functions:"); + lua::register_func(L, "cpp_add", cpp_add); + lua::register_func(L, "cpp_greet", cpp_greet); + lua::register_func(L, "cpp_range", cpp_range); + + lua::L_dostring(L, + "print(' cpp_add(10, 20) = ' .. cpp_add(10, 20))\n" + "print(' cpp_greet(\"mcpp\") = ' .. cpp_greet('mcpp'))\n" + ); + + // --- Return table from C++ function --- + std::println("[2] Return table from C++:"); + lua::L_dostring(L, + "local nums = cpp_range(1, 5)\n" + "local parts = {}\n" + "for _, v in ipairs(nums) do parts[#parts+1] = tostring(v) end\n" + "print(' cpp_range(1, 5) = {' .. table.concat(parts, ', ') .. '}')\n" + ); + + // --- Closure with upvalue --- + std::println("[3] Closure with upvalue:"); + lua::pushinteger(L, 0); + lua::pushcclosure(L, cpp_counter, 1); + lua::setglobal(L, "counter"); + + lua::L_dostring(L, + "print(' counter() = ' .. counter())\n" + "print(' counter() = ' .. counter())\n" + "print(' counter() = ' .. counter())\n" + ); + + // --- Register a module with luaL_setfuncs --- + std::println("[4] Register module with L_setfuncs:"); + static const lua::L_Reg mymath[] = { + {"square", [](lua::State* L) -> int { + auto x = lua::tonumber(L, 1); + lua::pushnumber(L, x * x); + return 1; + }}, + {"cube", [](lua::State* L) -> int { + auto x = lua::tonumber(L, 1); + lua::pushnumber(L, x * x * x); + return 1; + }}, + {nullptr, nullptr} + }; + + lua::newtable(L); + lua::L_setfuncs(L, mymath, 0); + lua::setglobal(L, "mymath"); + + lua::L_dostring(L, + "print(' mymath.square(5) = ' .. mymath.square(5))\n" + "print(' mymath.cube(3) = ' .. mymath.cube(3))\n" + ); + + // --- Call Lua function from C++ --- + std::println("[5] Call Lua function from C++:"); + lua::L_dostring(L, + "function fibonacci(n)\n" + " if n <= 1 then return n end\n" + " return fibonacci(n-1) + fibonacci(n-2)\n" + "end\n" + ); + + lua::getglobal(L, "fibonacci"); + lua::pushinteger(L, 10); + if (lua::pcall(L, 1, 1, 0) == lua::OK) { + std::println(" fibonacci(10) = {}", lua::tointeger(L, -1)); + } + lua::pop(L, 1); + + lua::close(L); + std::println("\ndone."); + return 0; +} diff --git a/examples/table.cpp b/examples/table.cpp new file mode 100644 index 0000000..e3044cc --- /dev/null +++ b/examples/table.cpp @@ -0,0 +1,95 @@ +import std; +import mcpplibs.capi.lua; + +namespace lua = mcpplibs::capi::lua; + +int main() { + std::println("=== mcpplibs.capi.lua table example ===\n"); + + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + + // --- Create a table from C++ --- + std::println("[1] Create table from C++:"); + lua::newtable(L); + lua::pushstring(L, "Alice"); + lua::setfield(L, -2, "name"); + lua::pushinteger(L, 30); + lua::setfield(L, -2, "age"); + lua::pushstring(L, "alice@example.com"); + lua::setfield(L, -2, "email"); + lua::setglobal(L, "person"); + + lua::L_dostring(L, + "print(' name = ' .. person.name)\n" + "print(' age = ' .. person.age)\n" + "print(' email = ' .. person.email)\n" + ); + + // --- Array-style table --- + std::println("[2] Array table:"); + lua::newtable(L); + const char* fruits[] { "apple", "banana", "cherry", "date" }; + for (int i { 0 }; i < 4; i++) { + lua::pushstring(L, fruits[i]); + lua::rawseti(L, -2, i + 1); + } + lua::setglobal(L, "fruits"); + + lua::L_dostring(L, + "for i, fruit in ipairs(fruits) do\n" + " print(' [' .. i .. '] = ' .. fruit)\n" + "end\n" + ); + + // --- Nested tables --- + std::println("[3] Nested tables:"); + lua::newtable(L); + + lua::newtable(L); + lua::pushnumber(L, 1.0); + lua::setfield(L, -2, "x"); + lua::pushnumber(L, 2.0); + lua::setfield(L, -2, "y"); + lua::setfield(L, -2, "position"); + + lua::newtable(L); + lua::pushnumber(L, 10.0); + lua::setfield(L, -2, "width"); + lua::pushnumber(L, 20.0); + lua::setfield(L, -2, "height"); + lua::setfield(L, -2, "size"); + + lua::setglobal(L, "rect"); + + lua::L_dostring(L, + "print(' rect.position = (' .. rect.position.x .. ', ' .. rect.position.y .. ')')\n" + "print(' rect.size = ' .. rect.size.width .. ' x ' .. rect.size.height)\n" + ); + + // --- Table traversal from C++ --- + std::println("[4] Traverse table from C++:"); + lua::L_dostring(L, "config = { debug=true, level=5, name='test' }"); + lua::getglobal(L, "config"); + lua::pushnil(L); + while (lua::next(L, -2) != 0) { + const char* key = lua::tostring(L, -2); + const char* typeName = lua::L_typename(L, -1); + if (lua::isstring(L, -1)) { + std::println(" {} ({}) = {}", key, typeName, lua::tostring(L, -1)); + } else if (lua::isinteger(L, -1)) { + std::println(" {} ({}) = {}", key, typeName, lua::tointeger(L, -1)); + } else if (lua::isboolean(L, -1)) { + std::println(" {} ({}) = {}", key, typeName, lua::toboolean(L, -1) ? "true" : "false"); + } else if (lua::isnumber(L, -1)) { + std::println(" {} ({}) = {}", key, typeName, lua::tonumber(L, -1)); + } else { + std::println(" {} ({}) = <{}>", key, typeName, typeName); + } + lua::pop(L, 1); + } + + lua::close(L); + std::println("\ndone."); + return 0; +} diff --git a/examples/xmake.lua b/examples/xmake.lua index 7564132..685802c 100644 --- a/examples/xmake.lua +++ b/examples/xmake.lua @@ -2,8 +2,13 @@ add_rules("mode.debug", "mode.release") set_languages("c++23") -target("basic") - set_kind("binary") - add_files("basic.cpp") - add_deps("mcpplibs-templates") - set_policy("build.c++.modules", true) +add_requires("lua") + +for _, name in ipairs({"basic", "table", "function", "eval"}) do + target(name) + set_kind("binary") + add_files(name .. ".cpp") + add_deps("mcpplibs-capi-lua") + add_packages("lua") + set_policy("build.c++.modules", true) +end diff --git a/src/capi/lua.cpp b/src/capi/lua.cpp new file mode 100644 index 0000000..77f194d --- /dev/null +++ b/src/capi/lua.cpp @@ -0,0 +1,422 @@ +module; + +#include "lua_headers.h" + +module mcpplibs.capi.lua; + +namespace mcpplibs::capi::lua { + +// ============================================================================ +// State Management +// ============================================================================ + +State* newstate(Alloc f, void* ud) { return ::lua_newstate(f, ud); } +void close(State* L) { ::lua_close(L); } +State* newthread(State* L) { return ::lua_newthread(L); } +CFunction atpanic(State* L, CFunction panicf) { return ::lua_atpanic(L, panicf); } +Number version(State* L) { return ::lua_version(L); } + +// ============================================================================ +// Stack Operations +// ============================================================================ + +int absindex(State* L, int idx) { return ::lua_absindex(L, idx); } +int gettop(State* L) { return ::lua_gettop(L); } +void settop(State* L, int idx) { ::lua_settop(L, idx); } +void pop(State* L, int n) { ::lua_settop(L, -(n) - 1); } +void pushvalue(State* L, int idx) { ::lua_pushvalue(L, idx); } +void rotate(State* L, int idx, int n) { ::lua_rotate(L, idx, n); } +void copy(State* L, int fromidx, int toidx) { ::lua_copy(L, fromidx, toidx); } +int checkstack(State* L, int n) { return ::lua_checkstack(L, n); } +void xmove(State* from, State* to, int n) { ::lua_xmove(from, to, n); } + +void insert(State* L, int idx) { + ::lua_rotate(L, (idx), 1); +} + +void remove(State* L, int idx) { + ::lua_rotate(L, (idx), -1); + ::lua_settop(L, -2); +} + +void replace(State* L, int idx) { + ::lua_copy(L, -1, (idx)); + ::lua_settop(L, -2); +} + +// ============================================================================ +// Push Functions (C -> Stack) +// ============================================================================ + +void pushnil(State* L) { ::lua_pushnil(L); } +void pushnumber(State* L, Number n) { ::lua_pushnumber(L, n); } +void pushinteger(State* L, Integer n) { ::lua_pushinteger(L, n); } + +const char* pushlstring(State* L, const char* s, unsigned long long len) { + return ::lua_pushlstring(L, s, static_cast(len)); +} + +const char* pushstring(State* L, const char* s) { return ::lua_pushstring(L, s); } +void pushcclosure(State* L, CFunction fn, int n) { ::lua_pushcclosure(L, fn, n); } +void pushcfunction(State* L, CFunction f) { ::lua_pushcclosure(L, (f), 0); } +void pushboolean(State* L, int b) { ::lua_pushboolean(L, b); } +void pushlightuserdata(State* L, void* p) { ::lua_pushlightuserdata(L, p); } +int pushthread(State* L) { return ::lua_pushthread(L); } + +void pushglobaltable(State* L) { + ::lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); +} + +const char* pushfstring(State* L, const char* fmt) { + return ::lua_pushfstring(L, "%s", fmt); +} + +const char* pushfstring(State* L, const char* fmt, const char* arg1) { + return ::lua_pushfstring(L, fmt, arg1); +} + +const char* pushfstring(State* L, const char* fmt, const char* arg1, int arg2) { + return ::lua_pushfstring(L, fmt, arg1, arg2); +} + +const char* pushfstring(State* L, const char* fmt, int arg1) { + return ::lua_pushfstring(L, fmt, arg1); +} + +// ============================================================================ +// Type Check Functions +// ============================================================================ + +int type(State* L, int idx) { return ::lua_type(L, idx); } +const char* type_name(State* L, int tp) { return ::lua_typename(L, tp); } +int isnumber(State* L, int idx) { return ::lua_isnumber(L, idx); } +int isstring(State* L, int idx) { return ::lua_isstring(L, idx); } +int iscfunction(State* L, int idx) { return ::lua_iscfunction(L, idx); } +int isinteger(State* L, int idx) { return ::lua_isinteger(L, idx); } +int isuserdata(State* L, int idx) { return ::lua_isuserdata(L, idx); } +int isfunction(State* L, int idx) { return ::lua_type(L, (idx)) == LUA_TFUNCTION; } +int istable(State* L, int idx) { return ::lua_type(L, (idx)) == LUA_TTABLE; } +int islightuserdata(State* L, int idx) { return ::lua_type(L, (idx)) == LUA_TLIGHTUSERDATA; } +int isnil(State* L, int idx) { return ::lua_type(L, (idx)) == LUA_TNIL; } +int isboolean(State* L, int idx) { return ::lua_type(L, (idx)) == LUA_TBOOLEAN; } +int isthread(State* L, int idx) { return ::lua_type(L, (idx)) == LUA_TTHREAD; } +int isnone(State* L, int idx) { return ::lua_type(L, (idx)) == LUA_TNONE; } +int isnoneornil(State* L, int idx) { return ::lua_type(L, (idx)) <= 0; } + +// ============================================================================ +// Access Functions (Stack -> C) +// ============================================================================ + +Number tonumberx(State* L, int idx, int* isnum) { return ::lua_tonumberx(L, idx, isnum); } +Integer tointegerx(State* L, int idx, int* isnum) { return ::lua_tointegerx(L, idx, isnum); } +Number tonumber(State* L, int idx) { return ::lua_tonumberx(L, (idx), nullptr); } +Integer tointeger(State* L, int idx) { return ::lua_tointegerx(L, (idx), nullptr); } +int toboolean(State* L, int idx) { return ::lua_toboolean(L, idx); } + +const char* tolstring(State* L, int idx, unsigned long long* len) { + return ::lua_tolstring(L, idx, reinterpret_cast(len)); +} + +const char* tostring(State* L, int idx) { return ::lua_tolstring(L, (idx), nullptr); } +CFunction tocfunction(State* L, int idx) { return ::lua_tocfunction(L, idx); } +void* touserdata(State* L, int idx) { return ::lua_touserdata(L, idx); } +State* tothread(State* L, int idx) { return ::lua_tothread(L, idx); } +const void* topointer(State* L, int idx) { return ::lua_topointer(L, idx); } +Unsigned rawlen(State* L, int idx) { return ::lua_rawlen(L, idx); } + +// ============================================================================ +// Arithmetic and Comparison +// ============================================================================ + +void arith(State* L, int op) { ::lua_arith(L, op); } +int rawequal(State* L, int idx1, int idx2) { return ::lua_rawequal(L, idx1, idx2); } +int compare(State* L, int idx1, int idx2, int op) { return ::lua_compare(L, idx1, idx2, op); } + +// ============================================================================ +// Table Access (Get) +// ============================================================================ + +int getglobal(State* L, const char* name) { return ::lua_getglobal(L, name); } +int gettable(State* L, int idx) { return ::lua_gettable(L, idx); } +int getfield(State* L, int idx, const char* k) { return ::lua_getfield(L, idx, k); } +int geti(State* L, int idx, Integer n) { return ::lua_geti(L, idx, n); } +int rawget(State* L, int idx) { return ::lua_rawget(L, idx); } +int rawgeti(State* L, int idx, Integer n) { return ::lua_rawgeti(L, idx, n); } +int rawgetp(State* L, int idx, const void* p) { return ::lua_rawgetp(L, idx, p); } +void createtable(State* L, int narr, int nrec) { ::lua_createtable(L, narr, nrec); } +void newtable(State* L) { ::lua_createtable(L, 0, 0); } + +void* newuserdatauv(State* L, unsigned long long sz, int nuvalue) { + return ::lua_newuserdatauv(L, static_cast(sz), nuvalue); +} + +void* newuserdata(State* L, unsigned long long sz) { + return ::lua_newuserdatauv(L, static_cast(sz), 1); +} + +int getmetatable(State* L, int objindex) { return ::lua_getmetatable(L, objindex); } +int getiuservalue(State* L, int idx, int n) { return ::lua_getiuservalue(L, idx, n); } + +// ============================================================================ +// Table Access (Set) +// ============================================================================ + +void setglobal(State* L, const char* name) { ::lua_setglobal(L, name); } +void settable(State* L, int idx) { ::lua_settable(L, idx); } +void setfield(State* L, int idx, const char* k) { ::lua_setfield(L, idx, k); } +void seti(State* L, int idx, Integer n) { ::lua_seti(L, idx, n); } +void rawset(State* L, int idx) { ::lua_rawset(L, idx); } +void rawseti(State* L, int idx, Integer n) { ::lua_rawseti(L, idx, n); } +void rawsetp(State* L, int idx, const void* p) { ::lua_rawsetp(L, idx, p); } +int setmetatable(State* L, int objindex) { return ::lua_setmetatable(L, objindex); } +int setiuservalue(State* L, int idx, int n) { return ::lua_setiuservalue(L, idx, n); } + +// ============================================================================ +// Call / Load / Execute +// ============================================================================ + +void callk(State* L, int nargs, int nresults, KContext ctx, KFunction k) { + ::lua_callk(L, nargs, nresults, ctx, k); +} + +void call(State* L, int nargs, int nresults) { + ::lua_callk(L, (nargs), (nresults), 0, nullptr); +} + +int pcallk(State* L, int nargs, int nresults, int errfunc, KContext ctx, KFunction k) { + return ::lua_pcallk(L, nargs, nresults, errfunc, ctx, k); +} + +int pcall(State* L, int nargs, int nresults, int errfunc) { + return ::lua_pcallk(L, (nargs), (nresults), (errfunc), 0, nullptr); +} + +int load(State* L, Reader reader, void* dt, const char* chunkname, const char* mode) { + return ::lua_load(L, reader, dt, chunkname, mode); +} + +int dump(State* L, Writer writer, void* data, int strip) { + return ::lua_dump(L, writer, data, strip); +} + +// ============================================================================ +// Coroutine +// ============================================================================ + +int yieldk(State* L, int nresults, KContext ctx, KFunction k) { + return ::lua_yieldk(L, nresults, ctx, k); +} + +int resume(State* L, State* from, int narg, int* nres) { + return ::lua_resume(L, from, narg, nres); +} + +int status(State* L) { return ::lua_status(L); } +int isyieldable(State* L) { return ::lua_isyieldable(L); } + +// ============================================================================ +// GC +// ============================================================================ + +int gc(State* L, int what) { return ::lua_gc(L, what, 0); } +int gc(State* L, int what, int data) { return ::lua_gc(L, what, data); } +int gc(State* L, int what, int data, int data2) { return ::lua_gc(L, what, data, data2); } + +// ============================================================================ +// Misc +// ============================================================================ + +int error(State* L) { return ::lua_error(L); } +int next(State* L, int idx) { return ::lua_next(L, idx); } +void concat(State* L, int n) { ::lua_concat(L, n); } +void len(State* L, int idx) { ::lua_len(L, idx); } + +unsigned long long stringtonumber(State* L, const char* s) { + return static_cast(::lua_stringtonumber(L, s)); +} + +Alloc getallocf(State* L, void** ud) { return ::lua_getallocf(L, ud); } +void setallocf(State* L, Alloc f, void* ud) { ::lua_setallocf(L, f, ud); } +void toclose(State* L, int idx) { ::lua_toclose(L, idx); } +void closeslot(State* L, int idx) { ::lua_closeslot(L, idx); } +void* getextraspace(State* L) { return lua_getextraspace(L); } +void setwarnf(State* L, WarnFunction f, void* ud) { ::lua_setwarnf(L, f, ud); } +void warning(State* L, const char* msg, int tocont) { ::lua_warning(L, msg, tocont); } + +void register_func(State* L, const char* name, CFunction f) { + ::lua_pushcclosure(L, (f), 0); + ::lua_setglobal(L, (name)); +} + +int upvalueindex(int i) { return lua_upvalueindex(i); } + +// ============================================================================ +// Debug Interface +// ============================================================================ + +int getstack(State* L, int level, Debug* ar) { return ::lua_getstack(L, level, ar); } +int getinfo(State* L, const char* what, Debug* ar) { return ::lua_getinfo(L, what, ar); } +const char* getlocal(State* L, const Debug* ar, int n) { return ::lua_getlocal(L, ar, n); } +const char* setlocal(State* L, const Debug* ar, int n) { return ::lua_setlocal(L, ar, n); } +const char* getupvalue(State* L, int funcindex, int n) { return ::lua_getupvalue(L, funcindex, n); } +const char* setupvalue(State* L, int funcindex, int n) { return ::lua_setupvalue(L, funcindex, n); } +void* upvalueid(State* L, int fidx, int n) { return ::lua_upvalueid(L, fidx, n); } + +void upvaluejoin(State* L, int fidx1, int n1, int fidx2, int n2) { + ::lua_upvaluejoin(L, fidx1, n1, fidx2, n2); +} + +void sethook(State* L, Hook func, int mask, int count) { ::lua_sethook(L, func, mask, count); } +Hook gethook(State* L) { return ::lua_gethook(L); } +int gethookmask(State* L) { return ::lua_gethookmask(L); } +int gethookcount(State* L) { return ::lua_gethookcount(L); } + +// ============================================================================ +// Auxiliary Library (luaL_*) +// ============================================================================ + +State* L_newstate() { return ::luaL_newstate(); } +void L_openlibs(State* L) { ::luaL_openlibs(L); } +int L_loadstring(State* L, const char* s) { return ::luaL_loadstring(L, s); } + +int L_loadfilex(State* L, const char* filename, const char* mode) { + return ::luaL_loadfilex(L, filename, mode); +} + +int L_loadfile(State* L, const char* filename) { + return ::luaL_loadfilex(L, (filename), nullptr); +} + +int L_loadbufferx(State* L, const char* buff, unsigned long long sz, + const char* name, const char* mode) { + return ::luaL_loadbufferx(L, buff, static_cast(sz), name, mode); +} + +int L_dostring(State* L, const char* s) { + return ::luaL_loadstring(L, s) || ::lua_pcallk(L, 0, LUA_MULTRET, 0, 0, nullptr); +} + +int L_dofile(State* L, const char* filename) { + return ::luaL_loadfilex(L, filename, nullptr) || ::lua_pcallk(L, 0, LUA_MULTRET, 0, 0, nullptr); +} + +int L_getmetafield(State* L, int obj, const char* e) { return ::luaL_getmetafield(L, obj, e); } +int L_callmeta(State* L, int obj, const char* e) { return ::luaL_callmeta(L, obj, e); } + +const char* L_tolstring(State* L, int idx, unsigned long long* len) { + return ::luaL_tolstring(L, idx, reinterpret_cast(len)); +} + +int L_argerror(State* L, int arg, const char* extramsg) { return ::luaL_argerror(L, arg, extramsg); } +int L_typeerror(State* L, int arg, const char* tname) { return ::luaL_typeerror(L, arg, tname); } + +const char* L_checklstring(State* L, int arg, unsigned long long* l) { + return ::luaL_checklstring(L, arg, reinterpret_cast(l)); +} + +const char* L_optlstring(State* L, int arg, const char* def, unsigned long long* l) { + return ::luaL_optlstring(L, arg, def, reinterpret_cast(l)); +} + +Number L_checknumber(State* L, int arg) { return ::luaL_checknumber(L, arg); } +Number L_optnumber(State* L, int arg, Number def) { return ::luaL_optnumber(L, arg, def); } +Integer L_checkinteger(State* L, int arg) { return ::luaL_checkinteger(L, arg); } +Integer L_optinteger(State* L, int arg, Integer def) { return ::luaL_optinteger(L, arg, def); } +void L_checkstack(State* L, int sz, const char* msg) { ::luaL_checkstack(L, sz, msg); } +void L_checktype(State* L, int arg, int t) { ::luaL_checktype(L, arg, t); } +void L_checkany(State* L, int arg) { ::luaL_checkany(L, arg); } +int L_newmetatable(State* L, const char* tname) { return ::luaL_newmetatable(L, tname); } +void L_setmetatable(State* L, const char* tname) { ::luaL_setmetatable(L, tname); } +void* L_testudata(State* L, int ud, const char* tname) { return ::luaL_testudata(L, ud, tname); } +void* L_checkudata(State* L, int ud, const char* tname) { return ::luaL_checkudata(L, ud, tname); } +void L_where(State* L, int lvl) { ::luaL_where(L, lvl); } + +int L_checkoption(State* L, int arg, const char* def, const char* const lst[]) { + return ::luaL_checkoption(L, arg, def, lst); +} + +int L_ref(State* L, int t) { return ::luaL_ref(L, t); } +void L_unref(State* L, int t, int ref) { ::luaL_unref(L, t, ref); } +Integer L_len(State* L, int idx) { return ::luaL_len(L, idx); } + +const char* L_gsub(State* L, const char* s, const char* p, const char* r) { + return ::luaL_gsub(L, s, p, r); +} + +void L_setfuncs(State* L, const L_Reg* l, int nup) { ::luaL_setfuncs(L, l, nup); } + +int L_getsubtable(State* L, int idx, const char* fname) { + return ::luaL_getsubtable(L, idx, fname); +} + +void L_traceback(State* L, State* L1, const char* msg, int level) { + ::luaL_traceback(L, L1, msg, level); +} + +void L_requiref(State* L, const char* modname, CFunction openf, int glb) { + ::luaL_requiref(L, modname, openf, glb); +} + +const char* L_typename(State* L, int idx) { + return ::lua_typename(L, ::lua_type(L, (idx))); +} + +const char* L_checkstring(State* L, int arg) { + return ::luaL_checklstring(L, (arg), nullptr); +} + +const char* L_optstring(State* L, int arg, const char* def) { + return ::luaL_optlstring(L, (arg), (def), nullptr); +} + +int L_getmetatable(State* L, const char* n) { + return ::lua_getfield(L, LUA_REGISTRYINDEX, (n)); +} + +void L_checkversion(State* L) { ::luaL_checkversion(L); } + +int L_error(State* L, const char* fmt) { return ::luaL_error(L, "%s", fmt); } +int L_error(State* L, const char* fmt, const char* arg1) { return ::luaL_error(L, fmt, arg1); } + +// ============================================================================ +// Buffer Operations +// ============================================================================ + +void L_buffinit(State* L, L_Buffer* B) { ::luaL_buffinit(L, B); } + +char* L_prepbuffsize(L_Buffer* B, unsigned long long sz) { + return ::luaL_prepbuffsize(B, static_cast(sz)); +} + +void L_addlstring(L_Buffer* B, const char* s, unsigned long long l) { + ::luaL_addlstring(B, s, static_cast(l)); +} + +void L_addstring(L_Buffer* B, const char* s) { ::luaL_addstring(B, s); } +void L_addvalue(L_Buffer* B) { ::luaL_addvalue(B); } +void L_pushresult(L_Buffer* B) { ::luaL_pushresult(B); } + +void L_pushresultsize(L_Buffer* B, unsigned long long sz) { + ::luaL_pushresultsize(B, static_cast(sz)); +} + +char* L_buffinitsize(State* L, L_Buffer* B, unsigned long long sz) { + return ::luaL_buffinitsize(L, B, static_cast(sz)); +} + +// ============================================================================ +// Standard Libraries (luaopen_*) +// ============================================================================ + +int open_base(State* L) { return ::luaopen_base(L); } +int open_coroutine(State* L) { return ::luaopen_coroutine(L); } +int open_table(State* L) { return ::luaopen_table(L); } +int open_io(State* L) { return ::luaopen_io(L); } +int open_os(State* L) { return ::luaopen_os(L); } +int open_string(State* L) { return ::luaopen_string(L); } +int open_utf8(State* L) { return ::luaopen_utf8(L); } +int open_math(State* L) { return ::luaopen_math(L); } +int open_debug(State* L) { return ::luaopen_debug(L); } +int open_package(State* L) { return ::luaopen_package(L); } + +} // namespace mcpplibs::capi::lua diff --git a/src/capi/lua.cppm b/src/capi/lua.cppm new file mode 100644 index 0000000..fd4f099 --- /dev/null +++ b/src/capi/lua.cppm @@ -0,0 +1,343 @@ +module; + +#include "lua_headers.h" + +export module mcpplibs.capi.lua; + +export namespace mcpplibs::capi::lua { + +// ============================================================================ +// Types +// ============================================================================ + +using State = ::lua_State; +using Number = ::lua_Number; +using Integer = ::lua_Integer; +using Unsigned = ::lua_Unsigned; +using CFunction = ::lua_CFunction; +using KFunction = ::lua_KFunction; +using KContext = ::lua_KContext; +using Alloc = ::lua_Alloc; +using Reader = ::lua_Reader; +using Writer = ::lua_Writer; +using WarnFunction = ::lua_WarnFunction; +using Hook = ::lua_Hook; +using Debug = ::lua_Debug; +using L_Reg = ::luaL_Reg; +using L_Buffer = ::luaL_Buffer; + +// ============================================================================ +// Constants — Status Codes +// ============================================================================ + +inline constexpr int OK = LUA_OK; +inline constexpr int YIELD = LUA_YIELD; +inline constexpr int ERRRUN = LUA_ERRRUN; +inline constexpr int ERRSYNTAX = LUA_ERRSYNTAX; +inline constexpr int ERRMEM = LUA_ERRMEM; +inline constexpr int ERRERR = LUA_ERRERR; +inline constexpr int ERRFILE = LUA_ERRFILE; + +// ============================================================================ +// Constants — Type Tags +// ============================================================================ + +inline constexpr int TNONE = LUA_TNONE; +inline constexpr int TNIL = LUA_TNIL; +inline constexpr int TBOOLEAN = LUA_TBOOLEAN; +inline constexpr int TLIGHTUSERDATA = LUA_TLIGHTUSERDATA; +inline constexpr int TNUMBER = LUA_TNUMBER; +inline constexpr int TSTRING = LUA_TSTRING; +inline constexpr int TTABLE = LUA_TTABLE; +inline constexpr int TFUNCTION = LUA_TFUNCTION; +inline constexpr int TUSERDATA = LUA_TUSERDATA; +inline constexpr int TTHREAD = LUA_TTHREAD; +inline constexpr int NUMTYPES = LUA_NUMTYPES; + +// ============================================================================ +// Constants — Arithmetic / Comparison Operators +// ============================================================================ + +inline constexpr int OPADD = LUA_OPADD; +inline constexpr int OPSUB = LUA_OPSUB; +inline constexpr int OPMUL = LUA_OPMUL; +inline constexpr int OPMOD = LUA_OPMOD; +inline constexpr int OPPOW = LUA_OPPOW; +inline constexpr int OPDIV = LUA_OPDIV; +inline constexpr int OPIDIV = LUA_OPIDIV; +inline constexpr int OPBAND = LUA_OPBAND; +inline constexpr int OPBOR = LUA_OPBOR; +inline constexpr int OPBXOR = LUA_OPBXOR; +inline constexpr int OPSHL = LUA_OPSHL; +inline constexpr int OPSHR = LUA_OPSHR; +inline constexpr int OPUNM = LUA_OPUNM; +inline constexpr int OPBNOT = LUA_OPBNOT; + +inline constexpr int OPEQ = LUA_OPEQ; +inline constexpr int OPLT = LUA_OPLT; +inline constexpr int OPLE = LUA_OPLE; + +// ============================================================================ +// Constants — Misc +// ============================================================================ + +inline constexpr int MULTRET = LUA_MULTRET; +inline constexpr int REGISTRYINDEX = LUA_REGISTRYINDEX; +inline constexpr int MINSTACK = LUA_MINSTACK; +inline constexpr int RIDX_MAINTHREAD = LUA_RIDX_MAINTHREAD; +inline constexpr int RIDX_GLOBALS = LUA_RIDX_GLOBALS; +inline constexpr int NOREF = LUA_NOREF; +inline constexpr int REFNIL = LUA_REFNIL; + +// ============================================================================ +// Constants — GC Options +// ============================================================================ + +inline constexpr int GCSTOP = LUA_GCSTOP; +inline constexpr int GCRESTART = LUA_GCRESTART; +inline constexpr int GCCOLLECT = LUA_GCCOLLECT; +inline constexpr int GCCOUNT = LUA_GCCOUNT; +inline constexpr int GCCOUNTB = LUA_GCCOUNTB; +inline constexpr int GCSTEP = LUA_GCSTEP; +inline constexpr int GCSETPAUSE = LUA_GCSETPAUSE; +inline constexpr int GCSETSTEPMUL = LUA_GCSETSTEPMUL; +inline constexpr int GCISRUNNING = LUA_GCISRUNNING; +inline constexpr int GCGEN = LUA_GCGEN; +inline constexpr int GCINC = LUA_GCINC; + +// ============================================================================ +// Constants — Hook Masks +// ============================================================================ + +inline constexpr int MASKCALL = LUA_MASKCALL; +inline constexpr int MASKRET = LUA_MASKRET; +inline constexpr int MASKLINE = LUA_MASKLINE; +inline constexpr int MASKCOUNT = LUA_MASKCOUNT; + +inline constexpr int HOOKCALL = LUA_HOOKCALL; +inline constexpr int HOOKRET = LUA_HOOKRET; +inline constexpr int HOOKLINE = LUA_HOOKLINE; +inline constexpr int HOOKCOUNT = LUA_HOOKCOUNT; +inline constexpr int HOOKTAILCALL = LUA_HOOKTAILCALL; + +// ============================================================================ +// Function Declarations +// ============================================================================ + +// State Management +State* newstate(Alloc f, void* ud); +void close(State* L); +State* newthread(State* L); +CFunction atpanic(State* L, CFunction panicf); +Number version(State* L); + +// Stack Operations +int absindex(State* L, int idx); +int gettop(State* L); +void settop(State* L, int idx); +void pop(State* L, int n); +void pushvalue(State* L, int idx); +void rotate(State* L, int idx, int n); +void copy(State* L, int fromidx, int toidx); +int checkstack(State* L, int n); +void xmove(State* from, State* to, int n); +void insert(State* L, int idx); +void remove(State* L, int idx); +void replace(State* L, int idx); + +// Push Functions (C -> Stack) +void pushnil(State* L); +void pushnumber(State* L, Number n); +void pushinteger(State* L, Integer n); +const char* pushlstring(State* L, const char* s, unsigned long long len); +const char* pushstring(State* L, const char* s); +void pushcclosure(State* L, CFunction fn, int n); +void pushcfunction(State* L, CFunction f); +void pushboolean(State* L, int b); +void pushlightuserdata(State* L, void* p); +int pushthread(State* L); +void pushglobaltable(State* L); +const char* pushfstring(State* L, const char* fmt); +const char* pushfstring(State* L, const char* fmt, const char* arg1); +const char* pushfstring(State* L, const char* fmt, const char* arg1, int arg2); +const char* pushfstring(State* L, const char* fmt, int arg1); + +// Type Check Functions +int type(State* L, int idx); +const char* type_name(State* L, int tp); +int isnumber(State* L, int idx); +int isstring(State* L, int idx); +int iscfunction(State* L, int idx); +int isinteger(State* L, int idx); +int isuserdata(State* L, int idx); +int isfunction(State* L, int idx); +int istable(State* L, int idx); +int islightuserdata(State* L, int idx); +int isnil(State* L, int idx); +int isboolean(State* L, int idx); +int isthread(State* L, int idx); +int isnone(State* L, int idx); +int isnoneornil(State* L, int idx); + +// Access Functions (Stack -> C) +Number tonumberx(State* L, int idx, int* isnum); +Integer tointegerx(State* L, int idx, int* isnum); +Number tonumber(State* L, int idx); +Integer tointeger(State* L, int idx); +int toboolean(State* L, int idx); +const char* tolstring(State* L, int idx, unsigned long long* len); +const char* tostring(State* L, int idx); +CFunction tocfunction(State* L, int idx); +void* touserdata(State* L, int idx); +State* tothread(State* L, int idx); +const void* topointer(State* L, int idx); +Unsigned rawlen(State* L, int idx); + +// Arithmetic and Comparison +void arith(State* L, int op); +int rawequal(State* L, int idx1, int idx2); +int compare(State* L, int idx1, int idx2, int op); + +// Table Access (Get) +int getglobal(State* L, const char* name); +int gettable(State* L, int idx); +int getfield(State* L, int idx, const char* k); +int geti(State* L, int idx, Integer n); +int rawget(State* L, int idx); +int rawgeti(State* L, int idx, Integer n); +int rawgetp(State* L, int idx, const void* p); +void createtable(State* L, int narr, int nrec); +void newtable(State* L); +void* newuserdatauv(State* L, unsigned long long sz, int nuvalue); +void* newuserdata(State* L, unsigned long long sz); +int getmetatable(State* L, int objindex); +int getiuservalue(State* L, int idx, int n); + +// Table Access (Set) +void setglobal(State* L, const char* name); +void settable(State* L, int idx); +void setfield(State* L, int idx, const char* k); +void seti(State* L, int idx, Integer n); +void rawset(State* L, int idx); +void rawseti(State* L, int idx, Integer n); +void rawsetp(State* L, int idx, const void* p); +int setmetatable(State* L, int objindex); +int setiuservalue(State* L, int idx, int n); + +// Call / Load / Execute +void callk(State* L, int nargs, int nresults, KContext ctx, KFunction k); +void call(State* L, int nargs, int nresults); +int pcallk(State* L, int nargs, int nresults, int errfunc, KContext ctx, KFunction k); +int pcall(State* L, int nargs, int nresults, int errfunc); +int load(State* L, Reader reader, void* dt, const char* chunkname, const char* mode); +int dump(State* L, Writer writer, void* data, int strip); + +// Coroutine +int yieldk(State* L, int nresults, KContext ctx, KFunction k); +int resume(State* L, State* from, int narg, int* nres); +int status(State* L); +int isyieldable(State* L); + +// GC +int gc(State* L, int what); +int gc(State* L, int what, int data); +int gc(State* L, int what, int data, int data2); + +// Misc +int error(State* L); +int next(State* L, int idx); +void concat(State* L, int n); +void len(State* L, int idx); +unsigned long long stringtonumber(State* L, const char* s); +Alloc getallocf(State* L, void** ud); +void setallocf(State* L, Alloc f, void* ud); +void toclose(State* L, int idx); +void closeslot(State* L, int idx); +void* getextraspace(State* L); +void setwarnf(State* L, WarnFunction f, void* ud); +void warning(State* L, const char* msg, int tocont); +void register_func(State* L, const char* name, CFunction f); +int upvalueindex(int i); + +// Debug Interface +int getstack(State* L, int level, Debug* ar); +int getinfo(State* L, const char* what, Debug* ar); +const char* getlocal(State* L, const Debug* ar, int n); +const char* setlocal(State* L, const Debug* ar, int n); +const char* getupvalue(State* L, int funcindex, int n); +const char* setupvalue(State* L, int funcindex, int n); +void* upvalueid(State* L, int fidx, int n); +void upvaluejoin(State* L, int fidx1, int n1, int fidx2, int n2); +void sethook(State* L, Hook func, int mask, int count); +Hook gethook(State* L); +int gethookmask(State* L); +int gethookcount(State* L); + +// Auxiliary Library (luaL_*) +State* L_newstate(); +void L_openlibs(State* L); +int L_loadstring(State* L, const char* s); +int L_loadfilex(State* L, const char* filename, const char* mode); +int L_loadfile(State* L, const char* filename); +int L_loadbufferx(State* L, const char* buff, unsigned long long sz, const char* name, const char* mode); +int L_dostring(State* L, const char* s); +int L_dofile(State* L, const char* filename); +int L_getmetafield(State* L, int obj, const char* e); +int L_callmeta(State* L, int obj, const char* e); +const char* L_tolstring(State* L, int idx, unsigned long long* len); +int L_argerror(State* L, int arg, const char* extramsg); +int L_typeerror(State* L, int arg, const char* tname); +const char* L_checklstring(State* L, int arg, unsigned long long* l); +const char* L_optlstring(State* L, int arg, const char* def, unsigned long long* l); +Number L_checknumber(State* L, int arg); +Number L_optnumber(State* L, int arg, Number def); +Integer L_checkinteger(State* L, int arg); +Integer L_optinteger(State* L, int arg, Integer def); +void L_checkstack(State* L, int sz, const char* msg); +void L_checktype(State* L, int arg, int t); +void L_checkany(State* L, int arg); +int L_newmetatable(State* L, const char* tname); +void L_setmetatable(State* L, const char* tname); +void* L_testudata(State* L, int ud, const char* tname); +void* L_checkudata(State* L, int ud, const char* tname); +void L_where(State* L, int lvl); +int L_checkoption(State* L, int arg, const char* def, const char* const lst[]); +int L_ref(State* L, int t); +void L_unref(State* L, int t, int ref); +Integer L_len(State* L, int idx); +const char* L_gsub(State* L, const char* s, const char* p, const char* r); +void L_setfuncs(State* L, const L_Reg* l, int nup); +int L_getsubtable(State* L, int idx, const char* fname); +void L_traceback(State* L, State* L1, const char* msg, int level); +void L_requiref(State* L, const char* modname, CFunction openf, int glb); +const char* L_typename(State* L, int idx); +const char* L_checkstring(State* L, int arg); +const char* L_optstring(State* L, int arg, const char* def); +int L_getmetatable(State* L, const char* n); +void L_checkversion(State* L); +int L_error(State* L, const char* fmt); +int L_error(State* L, const char* fmt, const char* arg1); + +// Buffer Operations +void L_buffinit(State* L, L_Buffer* B); +char* L_prepbuffsize(L_Buffer* B, unsigned long long sz); +void L_addlstring(L_Buffer* B, const char* s, unsigned long long l); +void L_addstring(L_Buffer* B, const char* s); +void L_addvalue(L_Buffer* B); +void L_pushresult(L_Buffer* B); +void L_pushresultsize(L_Buffer* B, unsigned long long sz); +char* L_buffinitsize(State* L, L_Buffer* B, unsigned long long sz); + +// Standard Libraries (luaopen_*) +int open_base(State* L); +int open_coroutine(State* L); +int open_table(State* L); +int open_io(State* L); +int open_os(State* L); +int open_string(State* L); +int open_utf8(State* L); +int open_math(State* L); +int open_debug(State* L); +int open_package(State* L); + +} // namespace mcpplibs::capi::lua diff --git a/src/capi/lua_headers.h b/src/capi/lua_headers.h new file mode 100644 index 0000000..cfb7b32 --- /dev/null +++ b/src/capi/lua_headers.h @@ -0,0 +1,10 @@ +#ifndef MCPPLIBS_CAPI_LUA_HEADERS_H +#define MCPPLIBS_CAPI_LUA_HEADERS_H + +extern "C" { +#include +#include +#include +} + +#endif diff --git a/src/templates.cppm b/src/templates.cppm deleted file mode 100644 index e10e70f..0000000 --- a/src/templates.cppm +++ /dev/null @@ -1,13 +0,0 @@ -module; - -export module mcpplibs.templates; - -import std; - -namespace mcpplibs::templates { - - export void hello_mcpp() { - std::println("hello mcpp!"); - } - -} diff --git a/tests/main.cpp b/tests/main.cpp index 0691005..a46b1eb 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -1,12 +1,1100 @@ #include -import mcpplibs.templates; +import mcpplibs.capi.lua; -TEST(TemplatesTest, HelloMcpp) { - testing::internal::CaptureStdout(); - mcpplibs::templates::hello_mcpp(); - std::string output = testing::internal::GetCapturedStdout(); - EXPECT_EQ(output, "hello mcpp!\n"); +namespace lua = mcpplibs::capi::lua; + +// ============================================================================ +// State Management +// ============================================================================ + +TEST(StateTest, CreateAndClose) { + auto* L = lua::L_newstate(); + ASSERT_NE(L, nullptr); + lua::close(L); +} + +TEST(StateTest, OpenLibs) { + auto* L = lua::L_newstate(); + ASSERT_NE(L, nullptr); + lua::L_openlibs(L); + lua::close(L); +} + +TEST(StateTest, Version) { + auto* L = lua::L_newstate(); + auto ver = lua::version(L); + EXPECT_GE(ver, 504.0); + lua::close(L); +} + +TEST(StateTest, CheckVersion) { + auto* L = lua::L_newstate(); + lua::L_checkversion(L); + lua::close(L); +} + +// ============================================================================ +// Constants +// ============================================================================ + +TEST(ConstantsTest, StatusCodes) { + EXPECT_EQ(lua::OK, 0); + EXPECT_NE(lua::ERRRUN, 0); + EXPECT_NE(lua::ERRSYNTAX, 0); + EXPECT_NE(lua::ERRMEM, 0); + EXPECT_NE(lua::ERRERR, 0); +} + +TEST(ConstantsTest, TypeTags) { + EXPECT_EQ(lua::TNONE, -1); + EXPECT_EQ(lua::TNIL, 0); + EXPECT_NE(lua::TBOOLEAN, lua::TNIL); + EXPECT_NE(lua::TNUMBER, lua::TSTRING); + EXPECT_NE(lua::TTABLE, lua::TFUNCTION); +} + +TEST(ConstantsTest, ArithOps) { + EXPECT_NE(lua::OPADD, lua::OPSUB); + EXPECT_NE(lua::OPMUL, lua::OPDIV); +} + +TEST(ConstantsTest, CompareOps) { + EXPECT_NE(lua::OPEQ, lua::OPLT); + EXPECT_NE(lua::OPLT, lua::OPLE); +} + +TEST(ConstantsTest, MiscConstants) { + EXPECT_EQ(lua::MULTRET, -1); + EXPECT_NE(lua::REGISTRYINDEX, 0); + EXPECT_GT(lua::MINSTACK, 0); +} + +TEST(ConstantsTest, GCOptions) { + EXPECT_NE(lua::GCSTOP, lua::GCRESTART); + EXPECT_NE(lua::GCCOLLECT, lua::GCCOUNT); +} + +// ============================================================================ +// Stack Operations +// ============================================================================ + +TEST(StackTest, GetTopEmpty) { + auto* L = lua::L_newstate(); + EXPECT_EQ(lua::gettop(L), 0); + lua::close(L); +} + +TEST(StackTest, PushAndGetTop) { + auto* L = lua::L_newstate(); + lua::pushnil(L); + EXPECT_EQ(lua::gettop(L), 1); + lua::pushnumber(L, 3.14); + EXPECT_EQ(lua::gettop(L), 2); + lua::pushinteger(L, 42); + EXPECT_EQ(lua::gettop(L), 3); + lua::close(L); +} + +TEST(StackTest, SetTop) { + auto* L = lua::L_newstate(); + lua::pushinteger(L, 1); + lua::pushinteger(L, 2); + lua::pushinteger(L, 3); + EXPECT_EQ(lua::gettop(L), 3); + lua::settop(L, 1); + EXPECT_EQ(lua::gettop(L), 1); + lua::close(L); +} + +TEST(StackTest, Pop) { + auto* L = lua::L_newstate(); + lua::pushinteger(L, 1); + lua::pushinteger(L, 2); + lua::pushinteger(L, 3); + lua::pop(L, 2); + EXPECT_EQ(lua::gettop(L), 1); + lua::close(L); +} + +TEST(StackTest, PushValue) { + auto* L = lua::L_newstate(); + lua::pushinteger(L, 42); + lua::pushvalue(L, -1); + EXPECT_EQ(lua::gettop(L), 2); + EXPECT_EQ(lua::tointeger(L, -1), 42); + EXPECT_EQ(lua::tointeger(L, -2), 42); + lua::close(L); +} + +TEST(StackTest, AbsIndex) { + auto* L = lua::L_newstate(); + lua::pushinteger(L, 10); + lua::pushinteger(L, 20); + EXPECT_EQ(lua::absindex(L, -1), 2); + EXPECT_EQ(lua::absindex(L, -2), 1); + EXPECT_EQ(lua::absindex(L, 1), 1); + lua::close(L); +} + +TEST(StackTest, CheckStack) { + auto* L = lua::L_newstate(); + EXPECT_NE(lua::checkstack(L, 100), 0); + lua::close(L); +} + +TEST(StackTest, Rotate) { + auto* L = lua::L_newstate(); + lua::pushinteger(L, 1); + lua::pushinteger(L, 2); + lua::pushinteger(L, 3); + lua::rotate(L, 1, 1); + EXPECT_EQ(lua::tointeger(L, 1), 3); + EXPECT_EQ(lua::tointeger(L, 2), 1); + EXPECT_EQ(lua::tointeger(L, 3), 2); + lua::close(L); +} + +TEST(StackTest, Copy) { + auto* L = lua::L_newstate(); + lua::pushinteger(L, 10); + lua::pushinteger(L, 20); + lua::copy(L, 2, 1); + EXPECT_EQ(lua::tointeger(L, 1), 20); + lua::close(L); +} + +TEST(StackTest, Insert) { + auto* L = lua::L_newstate(); + lua::pushinteger(L, 1); + lua::pushinteger(L, 2); + lua::pushinteger(L, 3); + lua::insert(L, 1); + EXPECT_EQ(lua::tointeger(L, 1), 3); + EXPECT_EQ(lua::tointeger(L, 2), 1); + EXPECT_EQ(lua::tointeger(L, 3), 2); + lua::close(L); +} + +TEST(StackTest, Remove) { + auto* L = lua::L_newstate(); + lua::pushinteger(L, 1); + lua::pushinteger(L, 2); + lua::pushinteger(L, 3); + lua::remove(L, 2); + EXPECT_EQ(lua::gettop(L), 2); + EXPECT_EQ(lua::tointeger(L, 1), 1); + EXPECT_EQ(lua::tointeger(L, 2), 3); + lua::close(L); +} + +TEST(StackTest, Replace) { + auto* L = lua::L_newstate(); + lua::pushinteger(L, 1); + lua::pushinteger(L, 2); + lua::pushinteger(L, 99); + lua::replace(L, 1); + EXPECT_EQ(lua::gettop(L), 2); + EXPECT_EQ(lua::tointeger(L, 1), 99); + lua::close(L); +} + +// ============================================================================ +// Push and Type Check +// ============================================================================ + +TEST(PushTest, PushNil) { + auto* L = lua::L_newstate(); + lua::pushnil(L); + EXPECT_NE(lua::isnil(L, -1), 0); + EXPECT_EQ(lua::type(L, -1), lua::TNIL); + lua::close(L); +} + +TEST(PushTest, PushNumber) { + auto* L = lua::L_newstate(); + lua::pushnumber(L, 3.14); + EXPECT_NE(lua::isnumber(L, -1), 0); + EXPECT_EQ(lua::type(L, -1), lua::TNUMBER); + EXPECT_DOUBLE_EQ(lua::tonumber(L, -1), 3.14); + lua::close(L); +} + +TEST(PushTest, PushInteger) { + auto* L = lua::L_newstate(); + lua::pushinteger(L, 42); + EXPECT_NE(lua::isinteger(L, -1), 0); + EXPECT_NE(lua::isnumber(L, -1), 0); + EXPECT_EQ(lua::tointeger(L, -1), 42); + lua::close(L); +} + +TEST(PushTest, PushString) { + auto* L = lua::L_newstate(); + lua::pushstring(L, "hello"); + EXPECT_NE(lua::isstring(L, -1), 0); + EXPECT_EQ(lua::type(L, -1), lua::TSTRING); + EXPECT_STREQ(lua::tostring(L, -1), "hello"); + lua::close(L); +} + +TEST(PushTest, PushLString) { + auto* L = lua::L_newstate(); + lua::pushlstring(L, "hello world", 5); + EXPECT_STREQ(lua::tostring(L, -1), "hello"); + lua::close(L); +} + +TEST(PushTest, PushBoolean) { + auto* L = lua::L_newstate(); + lua::pushboolean(L, 1); + EXPECT_NE(lua::isboolean(L, -1), 0); + EXPECT_EQ(lua::type(L, -1), lua::TBOOLEAN); + EXPECT_EQ(lua::toboolean(L, -1), 1); + + lua::pushboolean(L, 0); + EXPECT_EQ(lua::toboolean(L, -1), 0); + lua::close(L); +} + +TEST(PushTest, PushLightUserdata) { + auto* L = lua::L_newstate(); + int dummy { 0 }; + lua::pushlightuserdata(L, &dummy); + EXPECT_NE(lua::islightuserdata(L, -1), 0); + EXPECT_EQ(lua::touserdata(L, -1), &dummy); + lua::close(L); +} + +TEST(PushTest, PushCFunction) { + auto* L = lua::L_newstate(); + lua::CFunction fn = [](lua::State*) -> int { return 0; }; + lua::pushcfunction(L, fn); + EXPECT_NE(lua::iscfunction(L, -1), 0); + EXPECT_NE(lua::isfunction(L, -1), 0); + lua::close(L); +} + +TEST(PushTest, PushFString) { + auto* L = lua::L_newstate(); + const char* result = lua::pushfstring(L, "hello %s %d", "world", 42); + EXPECT_STREQ(result, "hello world 42"); + EXPECT_STREQ(lua::tostring(L, -1), "hello world 42"); + lua::close(L); +} + +// ============================================================================ +// Type Names +// ============================================================================ + +TEST(TypeNameTest, AllTypes) { + auto* L = lua::L_newstate(); + EXPECT_STREQ(lua::type_name(L, lua::TNIL), "nil"); + EXPECT_STREQ(lua::type_name(L, lua::TBOOLEAN), "boolean"); + EXPECT_STREQ(lua::type_name(L, lua::TNUMBER), "number"); + EXPECT_STREQ(lua::type_name(L, lua::TSTRING), "string"); + EXPECT_STREQ(lua::type_name(L, lua::TTABLE), "table"); + EXPECT_STREQ(lua::type_name(L, lua::TFUNCTION), "function"); + EXPECT_STREQ(lua::type_name(L, lua::TUSERDATA), "userdata"); + EXPECT_STREQ(lua::type_name(L, lua::TTHREAD), "thread"); + lua::close(L); +} + +TEST(TypeNameTest, LTypeName) { + auto* L = lua::L_newstate(); + lua::pushinteger(L, 42); + EXPECT_STREQ(lua::L_typename(L, -1), "number"); + lua::pushstring(L, "test"); + EXPECT_STREQ(lua::L_typename(L, -1), "string"); + lua::close(L); +} + +// ============================================================================ +// Access Functions +// ============================================================================ + +TEST(AccessTest, ToNumberX) { + auto* L = lua::L_newstate(); + lua::pushnumber(L, 2.718); + int isnum { 0 }; + auto val = lua::tonumberx(L, -1, &isnum); + EXPECT_NE(isnum, 0); + EXPECT_DOUBLE_EQ(val, 2.718); + lua::close(L); +} + +TEST(AccessTest, ToIntegerX) { + auto* L = lua::L_newstate(); + lua::pushinteger(L, 100); + int isnum { 0 }; + auto val = lua::tointegerx(L, -1, &isnum); + EXPECT_NE(isnum, 0); + EXPECT_EQ(val, 100); + lua::close(L); +} + +TEST(AccessTest, ToLString) { + auto* L = lua::L_newstate(); + lua::pushstring(L, "test"); + unsigned long long len { 0 }; + auto* s = lua::tolstring(L, -1, &len); + EXPECT_STREQ(s, "test"); + EXPECT_EQ(len, 4u); + lua::close(L); +} + +TEST(AccessTest, RawLen) { + auto* L = lua::L_newstate(); + lua::pushstring(L, "hello"); + EXPECT_EQ(lua::rawlen(L, -1), 5u); + lua::close(L); +} + +TEST(AccessTest, ToPointer) { + auto* L = lua::L_newstate(); + lua::newtable(L); + auto* p = lua::topointer(L, -1); + EXPECT_NE(p, nullptr); + lua::close(L); +} + +// ============================================================================ +// Isnone / Isnoneornil +// ============================================================================ + +TEST(TypeCheckTest, IsNone) { + auto* L = lua::L_newstate(); + EXPECT_NE(lua::isnone(L, 1), 0); + lua::pushinteger(L, 1); + EXPECT_EQ(lua::isnone(L, 1), 0); + lua::close(L); +} + +TEST(TypeCheckTest, IsNoneOrNil) { + auto* L = lua::L_newstate(); + EXPECT_NE(lua::isnoneornil(L, 1), 0); + lua::pushnil(L); + EXPECT_NE(lua::isnoneornil(L, 1), 0); + lua::pushinteger(L, 1); + EXPECT_EQ(lua::isnoneornil(L, 2), 0); + lua::close(L); +} + +// ============================================================================ +// Table Operations +// ============================================================================ + +TEST(TableTest, CreateAndSetField) { + auto* L = lua::L_newstate(); + lua::newtable(L); + lua::pushinteger(L, 42); + lua::setfield(L, -2, "x"); + + lua::getfield(L, -1, "x"); + EXPECT_EQ(lua::tointeger(L, -1), 42); + lua::close(L); +} + +TEST(TableTest, CreateTablePreAlloc) { + auto* L = lua::L_newstate(); + lua::createtable(L, 3, 2); + EXPECT_NE(lua::istable(L, -1), 0); + lua::close(L); +} + +TEST(TableTest, RawSetAndGet) { + auto* L = lua::L_newstate(); + lua::newtable(L); + + lua::pushstring(L, "key"); + lua::pushstring(L, "value"); + lua::rawset(L, -3); + + lua::pushstring(L, "key"); + lua::rawget(L, -2); + EXPECT_STREQ(lua::tostring(L, -1), "value"); + lua::close(L); +} + +TEST(TableTest, RawSetIAndGetI) { + auto* L = lua::L_newstate(); + lua::newtable(L); + + lua::pushstring(L, "first"); + lua::rawseti(L, -2, 1); + lua::pushstring(L, "second"); + lua::rawseti(L, -2, 2); + + lua::rawgeti(L, -1, 1); + EXPECT_STREQ(lua::tostring(L, -1), "first"); + lua::pop(L, 1); + + lua::rawgeti(L, -1, 2); + EXPECT_STREQ(lua::tostring(L, -1), "second"); + lua::close(L); +} + +TEST(TableTest, SetIAndGetI) { + auto* L = lua::L_newstate(); + lua::newtable(L); + + lua::pushinteger(L, 100); + lua::seti(L, -2, 1); + + lua::geti(L, -1, 1); + EXPECT_EQ(lua::tointeger(L, -1), 100); + lua::close(L); +} + +TEST(TableTest, SetAndGetTable) { + auto* L = lua::L_newstate(); + lua::newtable(L); + + lua::pushstring(L, "name"); + lua::pushstring(L, "mcpp"); + lua::settable(L, -3); + + lua::pushstring(L, "name"); + lua::gettable(L, -2); + EXPECT_STREQ(lua::tostring(L, -1), "mcpp"); + lua::close(L); +} + +TEST(TableTest, Next) { + auto* L = lua::L_newstate(); + lua::newtable(L); + lua::pushinteger(L, 10); + lua::setfield(L, -2, "a"); + lua::pushinteger(L, 20); + lua::setfield(L, -2, "b"); + + int count { 0 }; + lua::pushnil(L); + while (lua::next(L, -2) != 0) { + count++; + lua::pop(L, 1); + } + EXPECT_EQ(count, 2); + lua::close(L); +} + +TEST(TableTest, RawLen) { + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + lua::L_dostring(L, "t = {10, 20, 30}"); + lua::getglobal(L, "t"); + EXPECT_EQ(lua::rawlen(L, -1), 3u); + lua::close(L); +} + +TEST(TableTest, Metatable) { + auto* L = lua::L_newstate(); + lua::newtable(L); + lua::newtable(L); + lua::setmetatable(L, -2); + EXPECT_NE(lua::getmetatable(L, -1), 0); + lua::close(L); +} + +// ============================================================================ +// Global Variables +// ============================================================================ + +TEST(GlobalTest, SetAndGet) { + auto* L = lua::L_newstate(); + lua::pushinteger(L, 123); + lua::setglobal(L, "myvar"); + + lua::getglobal(L, "myvar"); + EXPECT_EQ(lua::tointeger(L, -1), 123); + lua::close(L); +} + +TEST(GlobalTest, PushGlobalTable) { + auto* L = lua::L_newstate(); + lua::pushglobaltable(L); + EXPECT_NE(lua::istable(L, -1), 0); + lua::close(L); +} + +// ============================================================================ +// Lua Script Execution +// ============================================================================ + +TEST(ExecTest, DoString) { + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + int result = lua::L_dostring(L, "x = 1 + 2"); + EXPECT_EQ(result, lua::OK); + lua::getglobal(L, "x"); + EXPECT_EQ(lua::tointeger(L, -1), 3); + lua::close(L); +} + +TEST(ExecTest, DoStringSyntaxError) { + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + int result = lua::L_dostring(L, "this is not valid lua!"); + EXPECT_NE(result, lua::OK); + lua::close(L); +} + +TEST(ExecTest, DoStringReturnValue) { + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + lua::L_dostring(L, "function add(a, b) return a + b end"); + lua::getglobal(L, "add"); + lua::pushinteger(L, 10); + lua::pushinteger(L, 20); + int result = lua::pcall(L, 2, 1, 0); + EXPECT_EQ(result, lua::OK); + EXPECT_EQ(lua::tointeger(L, -1), 30); + lua::close(L); +} + +TEST(ExecTest, LoadString) { + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + int result = lua::L_loadstring(L, "return 42"); + EXPECT_EQ(result, lua::OK); + EXPECT_NE(lua::isfunction(L, -1), 0); + result = lua::pcall(L, 0, 1, 0); + EXPECT_EQ(result, lua::OK); + EXPECT_EQ(lua::tointeger(L, -1), 42); + lua::close(L); +} + +// ============================================================================ +// Function Calls +// ============================================================================ + +TEST(CallTest, PCallSuccess) { + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + lua::L_dostring(L, "function greet(name) return 'hello ' .. name end"); + lua::getglobal(L, "greet"); + lua::pushstring(L, "mcpp"); + int result = lua::pcall(L, 1, 1, 0); + EXPECT_EQ(result, lua::OK); + EXPECT_STREQ(lua::tostring(L, -1), "hello mcpp"); + lua::close(L); +} + +TEST(CallTest, PCallError) { + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + lua::L_dostring(L, "function bad() error('oops') end"); + lua::getglobal(L, "bad"); + int result = lua::pcall(L, 0, 0, 0); + EXPECT_EQ(result, lua::ERRRUN); + lua::close(L); +} + +TEST(CallTest, CFunctionCallback) { + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + + lua::CFunction add_func = [](lua::State* L) -> int { + auto a = lua::tointeger(L, 1); + auto b = lua::tointeger(L, 2); + lua::pushinteger(L, a + b); + return 1; + }; + + lua::register_func(L, "c_add", add_func); + lua::L_dostring(L, "result = c_add(10, 32)"); + lua::getglobal(L, "result"); + EXPECT_EQ(lua::tointeger(L, -1), 42); + lua::close(L); +} + +TEST(CallTest, PushCClosure) { + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + + lua::pushinteger(L, 100); + lua::CFunction closure_fn = [](lua::State* L) -> int { + auto upval = lua::tointeger(L, lua::upvalueindex(1)); + auto arg = lua::tointeger(L, 1); + lua::pushinteger(L, upval + arg); + return 1; + }; + lua::pushcclosure(L, closure_fn, 1); + lua::setglobal(L, "add_100"); + + lua::L_dostring(L, "result = add_100(5)"); + lua::getglobal(L, "result"); + EXPECT_EQ(lua::tointeger(L, -1), 105); + lua::close(L); +} + +// ============================================================================ +// Arithmetic and Comparison +// ============================================================================ + +TEST(ArithTest, Add) { + auto* L = lua::L_newstate(); + lua::pushinteger(L, 10); + lua::pushinteger(L, 20); + lua::arith(L, lua::OPADD); + EXPECT_EQ(lua::tointeger(L, -1), 30); + lua::close(L); +} + +TEST(ArithTest, Mul) { + auto* L = lua::L_newstate(); + lua::pushnumber(L, 3.0); + lua::pushnumber(L, 4.0); + lua::arith(L, lua::OPMUL); + EXPECT_DOUBLE_EQ(lua::tonumber(L, -1), 12.0); + lua::close(L); +} + +TEST(ArithTest, Unm) { + auto* L = lua::L_newstate(); + lua::pushinteger(L, 42); + lua::arith(L, lua::OPUNM); + EXPECT_EQ(lua::tointeger(L, -1), -42); + lua::close(L); +} + +TEST(CompareTest, RawEqual) { + auto* L = lua::L_newstate(); + lua::pushinteger(L, 42); + lua::pushinteger(L, 42); + EXPECT_NE(lua::rawequal(L, -1, -2), 0); + + lua::pushinteger(L, 43); + EXPECT_EQ(lua::rawequal(L, -1, -2), 0); + lua::close(L); +} + +TEST(CompareTest, Compare) { + auto* L = lua::L_newstate(); + lua::pushinteger(L, 10); + lua::pushinteger(L, 20); + EXPECT_NE(lua::compare(L, -2, -1, lua::OPLT), 0); + EXPECT_EQ(lua::compare(L, -1, -2, lua::OPLT), 0); + EXPECT_NE(lua::compare(L, -2, -1, lua::OPLE), 0); + lua::close(L); +} + +// ============================================================================ +// String Operations +// ============================================================================ + +TEST(StringTest, Concat) { + auto* L = lua::L_newstate(); + lua::pushstring(L, "hello"); + lua::pushstring(L, " "); + lua::pushstring(L, "world"); + lua::concat(L, 3); + EXPECT_STREQ(lua::tostring(L, -1), "hello world"); + lua::close(L); +} + +TEST(StringTest, StringToNumber) { + auto* L = lua::L_newstate(); + auto len = lua::stringtonumber(L, "42"); + EXPECT_GT(len, 0u); + EXPECT_EQ(lua::tointeger(L, -1), 42); + lua::close(L); +} + +TEST(StringTest, Len) { + auto* L = lua::L_newstate(); + lua::pushstring(L, "hello"); + lua::len(L, -1); + EXPECT_EQ(lua::tointeger(L, -1), 5); + lua::close(L); +} + +// ============================================================================ +// Auxiliary Library +// ============================================================================ + +TEST(AuxTest, Ref) { + auto* L = lua::L_newstate(); + lua::pushstring(L, "stored"); + int ref = lua::L_ref(L, lua::REGISTRYINDEX); + EXPECT_NE(ref, lua::NOREF); + EXPECT_NE(ref, lua::REFNIL); + + lua::rawgeti(L, lua::REGISTRYINDEX, ref); + EXPECT_STREQ(lua::tostring(L, -1), "stored"); + + lua::L_unref(L, lua::REGISTRYINDEX, ref); + lua::close(L); +} + +TEST(AuxTest, NewMetatable) { + auto* L = lua::L_newstate(); + int created = lua::L_newmetatable(L, "MyType"); + EXPECT_NE(created, 0); + lua::pop(L, 1); + + created = lua::L_newmetatable(L, "MyType"); + EXPECT_EQ(created, 0); + lua::close(L); +} + +TEST(AuxTest, LLen) { + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + lua::L_dostring(L, "t = {1, 2, 3, 4, 5}"); + lua::getglobal(L, "t"); + EXPECT_EQ(lua::L_len(L, -1), 5); + lua::close(L); +} + +TEST(AuxTest, GSub) { + auto* L = lua::L_newstate(); + auto* result = lua::L_gsub(L, "hello world", "world", "mcpp"); + EXPECT_STREQ(result, "hello mcpp"); + EXPECT_STREQ(lua::tostring(L, -1), "hello mcpp"); + lua::close(L); +} + +TEST(AuxTest, GetSubtable) { + auto* L = lua::L_newstate(); + lua::pushglobaltable(L); + int existed = lua::L_getsubtable(L, -1, "mymodule"); + EXPECT_EQ(existed, 0); + EXPECT_NE(lua::istable(L, -1), 0); + + existed = lua::L_getsubtable(L, -2, "mymodule"); + EXPECT_NE(existed, 0); + lua::close(L); +} + +TEST(AuxTest, Traceback) { + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + lua::L_traceback(L, L, "test traceback", 0); + EXPECT_NE(lua::isstring(L, -1), 0); + lua::close(L); +} + +TEST(AuxTest, Where) { + auto* L = lua::L_newstate(); + lua::L_where(L, 0); + EXPECT_NE(lua::isstring(L, -1), 0); + lua::close(L); +} + +TEST(AuxTest, LTolstring) { + auto* L = lua::L_newstate(); + lua::pushinteger(L, 42); + unsigned long long len { 0 }; + auto* s = lua::L_tolstring(L, -1, &len); + EXPECT_STREQ(s, "42"); + EXPECT_GT(len, 0u); + lua::close(L); +} + +// ============================================================================ +// Buffer Operations +// ============================================================================ + +TEST(BufferTest, BasicBuffer) { + auto* L = lua::L_newstate(); + lua::L_Buffer buf; + lua::L_buffinit(L, &buf); + lua::L_addstring(&buf, "hello"); + lua::L_addstring(&buf, " "); + lua::L_addstring(&buf, "world"); + lua::L_pushresult(&buf); + EXPECT_STREQ(lua::tostring(L, -1), "hello world"); + lua::close(L); +} + +TEST(BufferTest, AddLString) { + auto* L = lua::L_newstate(); + lua::L_Buffer buf; + lua::L_buffinit(L, &buf); + lua::L_addlstring(&buf, "hello world", 5); + lua::L_pushresult(&buf); + EXPECT_STREQ(lua::tostring(L, -1), "hello"); + lua::close(L); +} + +// ============================================================================ +// GC +// ============================================================================ + +TEST(GCTest, CollectAndCount) { + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + lua::gc(L, lua::GCCOLLECT); + auto count = lua::gc(L, lua::GCCOUNT); + EXPECT_GE(count, 0); + lua::close(L); +} + +TEST(GCTest, IsRunning) { + auto* L = lua::L_newstate(); + auto running = lua::gc(L, lua::GCISRUNNING); + EXPECT_NE(running, 0); + lua::close(L); +} + +// ============================================================================ +// Thread +// ============================================================================ + +TEST(ThreadTest, NewThread) { + auto* L = lua::L_newstate(); + auto* T = lua::newthread(L); + EXPECT_NE(T, nullptr); + EXPECT_NE(lua::isthread(L, -1), 0); + lua::close(L); +} + +TEST(ThreadTest, PushThread) { + auto* L = lua::L_newstate(); + int isMain = lua::pushthread(L); + EXPECT_NE(isMain, 0); + lua::close(L); +} + +TEST(ThreadTest, Status) { + auto* L = lua::L_newstate(); + EXPECT_EQ(lua::status(L), lua::OK); + lua::close(L); +} + +TEST(ThreadTest, XMove) { + auto* L = lua::L_newstate(); + auto* T = lua::newthread(L); + lua::pushinteger(L, 42); + lua::xmove(L, T, 1); + EXPECT_EQ(lua::tointeger(T, -1), 42); + lua::close(L); +} + +// ============================================================================ +// Userdata +// ============================================================================ + +TEST(UserdataTest, NewUserdata) { + auto* L = lua::L_newstate(); + auto* ptr = lua::newuserdata(L, sizeof(int)); + EXPECT_NE(ptr, nullptr); + EXPECT_NE(lua::isuserdata(L, -1), 0); + *static_cast(ptr) = 42; + EXPECT_EQ(*static_cast(lua::touserdata(L, -1)), 42); + lua::close(L); +} + +TEST(UserdataTest, NewUserdataUV) { + auto* L = lua::L_newstate(); + auto* ptr = lua::newuserdatauv(L, sizeof(double), 2); + EXPECT_NE(ptr, nullptr); + lua::close(L); +} + +// ============================================================================ +// Standard Libraries +// ============================================================================ + +TEST(StdLibTest, OpenBase) { + auto* L = lua::L_newstate(); + lua::L_requiref(L, "_G", lua::open_base, 1); + lua::pop(L, 1); + lua::L_dostring(L, "x = type(42)"); + lua::getglobal(L, "x"); + EXPECT_STREQ(lua::tostring(L, -1), "number"); + lua::close(L); +} + +TEST(StdLibTest, OpenMath) { + auto* L = lua::L_newstate(); + lua::L_requiref(L, "_G", lua::open_base, 1); + lua::pop(L, 1); + lua::L_requiref(L, "math", lua::open_math, 1); + lua::pop(L, 1); + lua::L_dostring(L, "x = math.floor(3.7)"); + lua::getglobal(L, "x"); + EXPECT_EQ(lua::tointeger(L, -1), 3); + lua::close(L); +} + +TEST(StdLibTest, OpenString) { + auto* L = lua::L_newstate(); + lua::L_requiref(L, "_G", lua::open_base, 1); + lua::pop(L, 1); + lua::L_requiref(L, "string", lua::open_string, 1); + lua::pop(L, 1); + lua::L_dostring(L, "x = string.upper('hello')"); + lua::getglobal(L, "x"); + EXPECT_STREQ(lua::tostring(L, -1), "HELLO"); + lua::close(L); +} + +TEST(StdLibTest, OpenTable) { + auto* L = lua::L_newstate(); + lua::L_requiref(L, "_G", lua::open_base, 1); + lua::pop(L, 1); + lua::L_requiref(L, "table", lua::open_table, 1); + lua::pop(L, 1); + lua::L_dostring(L, "t = {3, 1, 2}; table.sort(t); x = t[1]"); + lua::getglobal(L, "x"); + EXPECT_EQ(lua::tointeger(L, -1), 1); + lua::close(L); +} + +// ============================================================================ +// Complex Scenarios +// ============================================================================ + +TEST(ScenarioTest, LuaCalculator) { + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + + lua::L_dostring(L, + "function calc(op, a, b) \n" + " if op == '+' then return a + b \n" + " elseif op == '-' then return a - b \n" + " elseif op == '*' then return a * b \n" + " elseif op == '/' then return a / b \n" + " end \n" + "end \n" + ); + + lua::getglobal(L, "calc"); + lua::pushstring(L, "+"); + lua::pushnumber(L, 10.5); + lua::pushnumber(L, 20.5); + EXPECT_EQ(lua::pcall(L, 3, 1, 0), lua::OK); + EXPECT_DOUBLE_EQ(lua::tonumber(L, -1), 31.0); + lua::pop(L, 1); + + lua::getglobal(L, "calc"); + lua::pushstring(L, "*"); + lua::pushinteger(L, 6); + lua::pushinteger(L, 7); + EXPECT_EQ(lua::pcall(L, 3, 1, 0), lua::OK); + EXPECT_EQ(lua::tointeger(L, -1), 42); + + lua::close(L); +} + +TEST(ScenarioTest, NestedTables) { + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + + lua::newtable(L); + lua::newtable(L); + lua::pushinteger(L, 42); + lua::setfield(L, -2, "value"); + lua::setfield(L, -2, "inner"); + lua::setglobal(L, "outer"); + + lua::L_dostring(L, "result = outer.inner.value"); + lua::getglobal(L, "result"); + EXPECT_EQ(lua::tointeger(L, -1), 42); + lua::close(L); +} + +TEST(ScenarioTest, SetFuncs) { + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + + static const lua::L_Reg mylib[] = { + {"add", [](lua::State* L) -> int { + lua::pushinteger(L, lua::tointeger(L, 1) + lua::tointeger(L, 2)); + return 1; + }}, + {"mul", [](lua::State* L) -> int { + lua::pushinteger(L, lua::tointeger(L, 1) * lua::tointeger(L, 2)); + return 1; + }}, + {nullptr, nullptr} + }; + + lua::newtable(L); + lua::L_setfuncs(L, mylib, 0); + lua::setglobal(L, "mylib"); + + lua::L_dostring(L, "r1 = mylib.add(3, 4)"); + lua::getglobal(L, "r1"); + EXPECT_EQ(lua::tointeger(L, -1), 7); + lua::pop(L, 1); + + lua::L_dostring(L, "r2 = mylib.mul(5, 6)"); + lua::getglobal(L, "r2"); + EXPECT_EQ(lua::tointeger(L, -1), 30); + + lua::close(L); +} + +TEST(ScenarioTest, ErrorHandling) { + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + + lua::CFunction safe_div = [](lua::State* L) -> int { + auto b = lua::tonumber(L, 2); + if (b == 0.0) { + lua::pushnil(L); + lua::pushstring(L, "division by zero"); + return 2; + } + lua::pushnumber(L, lua::tonumber(L, 1) / b); + return 1; + }; + + lua::register_func(L, "safe_div", safe_div); + lua::L_dostring(L, "r = safe_div(10, 0)"); + lua::getglobal(L, "r"); + EXPECT_NE(lua::isnil(L, -1), 0); + + lua::L_dostring(L, "r = safe_div(10, 2)"); + lua::getglobal(L, "r"); + EXPECT_DOUBLE_EQ(lua::tonumber(L, -1), 5.0); + + lua::close(L); +} + +// ============================================================================ +// Debug Interface +// ============================================================================ + +TEST(DebugTest, GetStack) { + auto* L = lua::L_newstate(); + lua::Debug ar; + int result = lua::getstack(L, 0, &ar); + EXPECT_EQ(result, 0); + lua::close(L); +} + +TEST(DebugTest, HookSetAndGet) { + auto* L = lua::L_newstate(); + + lua::Hook hook = [](lua::State*, lua::Debug*) {}; + lua::sethook(L, hook, lua::MASKCALL | lua::MASKRET, 0); + EXPECT_EQ(lua::gethook(L), hook); + EXPECT_NE(lua::gethookmask(L) & lua::MASKCALL, 0); + + lua::sethook(L, nullptr, 0, 0); + lua::close(L); +} + +// ============================================================================ +// Extra Space / Warn +// ============================================================================ + +TEST(MiscTest, GetExtraSpace) { + auto* L = lua::L_newstate(); + auto* extra = lua::getextraspace(L); + EXPECT_NE(extra, nullptr); + lua::close(L); +} + +TEST(MiscTest, AtPanic) { + auto* L = lua::L_newstate(); + lua::CFunction panic_fn = [](lua::State*) -> int { return 0; }; + lua::atpanic(L, panic_fn); + lua::close(L); } int main(int argc, char** argv) { diff --git a/tests/xmake.lua b/tests/xmake.lua index 9a52dc4..9bac83b 100644 --- a/tests/xmake.lua +++ b/tests/xmake.lua @@ -3,10 +3,11 @@ add_rules("mode.debug", "mode.release") set_languages("c++23") add_requires("gtest") +add_requires("lua") -target("templates_test") +target("capi_lua_test") set_kind("binary") add_files("*.cpp") - add_deps("mcpplibs-templates") - add_packages("gtest") + add_deps("mcpplibs-capi-lua") + add_packages("gtest", "lua") set_policy("build.c++.modules", true) diff --git a/xmake.lua b/xmake.lua index cebee86..f4e5c81 100644 --- a/xmake.lua +++ b/xmake.lua @@ -2,9 +2,13 @@ add_rules("mode.release", "mode.debug") set_languages("c++23") -target("mcpplibs-templates") +add_requires("lua") + +target("mcpplibs-capi-lua") set_kind("static") - add_files("src/*.cppm", { public = true, install = true }) + add_files("src/capi/*.cppm", { public = true, install = true }) + add_files("src/capi/*.cpp") + add_packages("lua", { public = true }) set_policy("build.c++.modules", true) if not is_host("macosx") then From 971b71cb48647be0098c4cb537082de77bc7e541 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 26 Feb 2026 20:57:13 +0000 Subject: [PATCH 2/3] docs: add mcpplibs-capi-lua agent skill - SKILL.md: quick start, naming map, core API cheat sheet, common patterns, build config, caveats (callback signatures, extern C, inline limitation) - reference.md: complete API reference with all 15 types, 50+ constants, 100+ functions organized by category with C API correspondence - Update skills README to list both available skills Co-authored-by: SPeak --- .agents/skills/README.md | 9 +- .agents/skills/mcpplibs-capi-lua/SKILL.md | 390 ++++++++++++++++++ .agents/skills/mcpplibs-capi-lua/reference.md | 364 ++++++++++++++++ 3 files changed, 760 insertions(+), 3 deletions(-) create mode 100644 .agents/skills/mcpplibs-capi-lua/SKILL.md create mode 100644 .agents/skills/mcpplibs-capi-lua/reference.md diff --git a/.agents/skills/README.md b/.agents/skills/README.md index d21b545..6a35e47 100644 --- a/.agents/skills/README.md +++ b/.agents/skills/README.md @@ -1,12 +1,13 @@ -# mcpp-style-ref Agent Skills +# mcpplibs Agent Skills -用于指导 Agent 在编写或审查 Modern/Module C++ 代码时遵循 mcpp-style-ref 规范的技能。 +用于指导 Agent 在 mcpplibs 生态中开发的技能集合。 ## 可用技能 | 技能 | 说明 | |------|------| | [mcpp-style-ref](mcpp-style-ref/SKILL.md) | 面向 mcpp 项目的 Modern/Module C++ (C++23) 命名、模块化与实践规则 | +| [mcpplibs-capi-lua](mcpplibs-capi-lua/SKILL.md) | Lua C API 的 C++23 模块化绑定 — 类型映射、API 速查、常见模式与构建配置 | ## 使用方式 @@ -15,10 +16,12 @@ ```bash mkdir -p .cursor/skills ln -s ../../skills/mcpp-style-ref .cursor/skills/mcpp-style-ref +ln -s ../../skills/mcpplibs-capi-lua .cursor/skills/mcpplibs-capi-lua ``` 或安装为个人技能: ```bash -ln -s /path/to/mcpp-style-ref/skills/mcpp-style-ref ~/.cursor/skills/mcpp-style-ref +ln -s /path/to/skills/mcpp-style-ref ~/.cursor/skills/mcpp-style-ref +ln -s /path/to/skills/mcpplibs-capi-lua ~/.cursor/skills/mcpplibs-capi-lua ``` diff --git a/.agents/skills/mcpplibs-capi-lua/SKILL.md b/.agents/skills/mcpplibs-capi-lua/SKILL.md new file mode 100644 index 0000000..ef59ecb --- /dev/null +++ b/.agents/skills/mcpplibs-capi-lua/SKILL.md @@ -0,0 +1,390 @@ +--- +name: mcpplibs-capi-lua +description: 使用 mcpplibs.capi.lua 模块(Lua C API 的 C++23 模块化绑定)。适用于编写使用 Lua 的 C++23 代码、嵌入 Lua 脚本引擎、注册 C++ 函数到 Lua、操作 Lua 栈和表,或用户提及 Lua binding、capi.lua、嵌入 Lua 时。 +--- + +# mcpplibs-capi-lua + +Lua 5.4 C API 的 C++23 模块化绑定。`import mcpplibs.capi.lua;` 即可使用完整 Lua C API,无需 `#include`。 + +## 快速开始 + +```cpp +import std; +import mcpplibs.capi.lua; + +namespace lua = mcpplibs::capi::lua; + +int main() { + auto* L = lua::L_newstate(); + lua::L_openlibs(L); + lua::L_dostring(L, "print('hello from Lua!')"); + lua::close(L); + return 0; +} +``` + +## 命名映射 + +命名空间 `mcpplibs::capi::lua` 内去掉 C API 前缀: + +| C API | 模块内 | 说明 | +|---|---|---| +| `lua_State` | `State` | 去 `lua_` | +| `lua_gettop(L)` | `gettop(L)` | 去 `lua_` | +| `luaL_newstate()` | `L_newstate()` | 去 `lua` 保留 `L_` | +| `LUA_OK` | `OK` | 去 `LUA_` | +| `luaopen_base(L)` | `open_base(L)` | 去 `luaopen_` 改 `open_` | +| `lua_typename(L,t)` | `type_name(L,t)` | 避免与 `type()` 冲突 | +| `lua_register(L,n,f)` | `register_func(L,n,f)` | 避免关键字冲突 | +| `lua_upvalueindex(i)` | `upvalueindex(i)` | 去 `lua_` | + +## 类型 + +```cpp +lua::State // lua_State +lua::Number // lua_Number (double) +lua::Integer // lua_Integer (long long) +lua::Unsigned // lua_Unsigned +lua::CFunction // int (*)(lua_State*) +lua::KFunction // lua_KFunction +lua::KContext // lua_KContext +lua::Alloc // lua_Alloc +lua::Reader // lua_Reader +lua::Writer // lua_Writer +lua::Hook // lua_Hook +lua::Debug // lua_Debug +lua::L_Reg // luaL_Reg — { name, func } 注册表 +lua::L_Buffer // luaL_Buffer +``` + +## 常量 + +```cpp +// 状态码 +lua::OK, lua::YIELD, lua::ERRRUN, lua::ERRSYNTAX, lua::ERRMEM, lua::ERRERR, lua::ERRFILE + +// 类型标签 +lua::TNONE, lua::TNIL, lua::TBOOLEAN, lua::TLIGHTUSERDATA, lua::TNUMBER, +lua::TSTRING, lua::TTABLE, lua::TFUNCTION, lua::TUSERDATA, lua::TTHREAD + +// 算术运算 +lua::OPADD, lua::OPSUB, lua::OPMUL, lua::OPDIV, lua::OPIDIV, lua::OPMOD, lua::OPPOW, +lua::OPBAND, lua::OPBOR, lua::OPBXOR, lua::OPSHL, lua::OPSHR, lua::OPUNM, lua::OPBNOT + +// 比较运算 +lua::OPEQ, lua::OPLT, lua::OPLE + +// 其他 +lua::MULTRET, lua::REGISTRYINDEX, lua::MINSTACK +lua::RIDX_MAINTHREAD, lua::RIDX_GLOBALS, lua::NOREF, lua::REFNIL + +// GC +lua::GCSTOP, lua::GCRESTART, lua::GCCOLLECT, lua::GCCOUNT, lua::GCCOUNTB, +lua::GCSTEP, lua::GCSETPAUSE, lua::GCSETSTEPMUL, lua::GCISRUNNING, lua::GCGEN, lua::GCINC + +// Hook +lua::MASKCALL, lua::MASKRET, lua::MASKLINE, lua::MASKCOUNT +lua::HOOKCALL, lua::HOOKRET, lua::HOOKLINE, lua::HOOKCOUNT, lua::HOOKTAILCALL +``` + +## 核心 API 速查 + +### 状态管理 + +```cpp +auto* L = lua::L_newstate(); // 创建状态 +lua::L_openlibs(L); // 打开标准库 +lua::close(L); // 关闭状态 +lua::version(L); // Lua 版本号 (504) +``` + +### 栈操作 + +```cpp +lua::gettop(L) // 栈顶索引(元素数量) +lua::settop(L, idx) // 设置栈顶 +lua::pop(L, n) // 弹出 n 个元素 +lua::pushvalue(L, idx) // 复制到栈顶 +lua::rotate(L, idx, n) // 旋转栈元素 +lua::copy(L, from, to) // 复制值 +lua::insert(L, idx) // 插入(栈顶移到 idx) +lua::remove(L, idx) // 移除 idx 处元素 +lua::replace(L, idx) // 替换(栈顶覆盖 idx) +lua::checkstack(L, n) // 确保栈有 n 个空位 +lua::absindex(L, idx) // 转绝对索引 +lua::xmove(from, to, n) // 线程间移动值 +``` + +### 入栈 + +```cpp +lua::pushnil(L) +lua::pushnumber(L, 3.14) +lua::pushinteger(L, 42) +lua::pushstring(L, "hello") +lua::pushlstring(L, ptr, len) +lua::pushboolean(L, 1) +lua::pushcfunction(L, fn) // fn: lua::CFunction +lua::pushcclosure(L, fn, nup) // 带 nup 个 upvalue +lua::pushlightuserdata(L, ptr) +lua::pushglobaltable(L) +lua::pushfstring(L, fmt, ...) // 格式化字符串 +``` + +### 取值 + +```cpp +lua::tonumber(L, idx) // → Number +lua::tointeger(L, idx) // → Integer +lua::toboolean(L, idx) // → int (0/1) +lua::tostring(L, idx) // → const char* +lua::tolstring(L, idx, &len) // → const char* + 长度 +lua::tocfunction(L, idx) // → CFunction +lua::touserdata(L, idx) // → void* +lua::tothread(L, idx) // → State* +lua::topointer(L, idx) // → const void* +lua::rawlen(L, idx) // 原始长度 +lua::tonumberx(L, idx, &isnum) // 带成功标志 +lua::tointegerx(L, idx, &isnum) // 带成功标志 +``` + +### 类型检查 + +```cpp +lua::type(L, idx) // → int (TNIL, TNUMBER, ...) +lua::type_name(L, tp) // → "nil", "number", ... +lua::L_typename(L, idx) // type_name(L, type(L, idx)) +lua::isnumber(L, idx) +lua::isstring(L, idx) +lua::isinteger(L, idx) +lua::isfunction(L, idx) +lua::istable(L, idx) +lua::isnil(L, idx) +lua::isboolean(L, idx) +lua::iscfunction(L, idx) +lua::isuserdata(L, idx) +lua::islightuserdata(L, idx) +lua::isthread(L, idx) +lua::isnone(L, idx) +lua::isnoneornil(L, idx) +``` + +### 表操作 + +```cpp +lua::newtable(L) // 创建空表 +lua::createtable(L, narr, nrec) // 预分配 + +lua::setfield(L, idx, "key") // t[key] = 栈顶,弹出值 +lua::getfield(L, idx, "key") // 压入 t[key] + +lua::settable(L, idx) // t[栈顶-1] = 栈顶,弹出 k+v +lua::gettable(L, idx) // 栈顶为 key,替换为 t[key] + +lua::seti(L, idx, n) // t[n] = 栈顶 +lua::geti(L, idx, n) // 压入 t[n] + +lua::rawset(L, idx) // 不触发元方法 +lua::rawget(L, idx) +lua::rawseti(L, idx, n) +lua::rawgeti(L, idx, n) + +lua::setmetatable(L, idx) // 设置元表 +lua::getmetatable(L, idx) // 获取元表 +lua::next(L, idx) // 遍历(需先 pushnil) +``` + +### 全局变量 + +```cpp +lua::setglobal(L, "name") // _G[name] = 栈顶 +lua::getglobal(L, "name") // 压入 _G[name] +``` + +### 脚本执行 + +```cpp +lua::L_dostring(L, "code") // 加载并执行字符串 +lua::L_dofile(L, "path.lua") // 加载并执行文件 +lua::L_loadstring(L, "code") // 加载为函数(不执行) +lua::L_loadfile(L, "path.lua") // 加载文件为函数 +lua::pcall(L, nargs, nresults, 0) // 安全调用(有错误处理) +lua::call(L, nargs, nresults) // 调用(无错误处理) +``` + +### 注册 C++ 函数 + +```cpp +// 简单注册 +lua::register_func(L, "add", my_add); + +// 带 upvalue 的闭包 +lua::pushinteger(L, 100); +lua::pushcclosure(L, my_closure, 1); // 1 个 upvalue +lua::setglobal(L, "add_100"); + +// 访问 upvalue +auto val = lua::tointeger(L, lua::upvalueindex(1)); + +// 注册模块(多个函数) +static const lua::L_Reg mylib[] = { + {"add", my_add}, + {"sub", my_sub}, + {nullptr, nullptr} +}; +lua::newtable(L); +lua::L_setfuncs(L, mylib, 0); +lua::setglobal(L, "mylib"); +``` + +### 辅助库 + +```cpp +lua::L_ref(L, lua::REGISTRYINDEX) // 创建引用 +lua::L_unref(L, lua::REGISTRYINDEX, ref) // 释放引用 +lua::L_newmetatable(L, "TypeName") // 创建/获取命名元表 +lua::L_setmetatable(L, "TypeName") // 设置栈顶对象的元表 +lua::L_checkudata(L, arg, "TypeName") // 检查参数为 userdata +lua::L_checkinteger(L, arg) // 检查参数为整数 +lua::L_checknumber(L, arg) // 检查参数为数字 +lua::L_checkstring(L, arg) // 检查参数为字符串 +lua::L_error(L, "msg") // 抛出错误 +lua::L_len(L, idx) // #t +lua::L_gsub(L, s, pat, rep) // 字符串替换 +lua::L_traceback(L, L1, msg, level) // 调用栈回溯 +lua::L_requiref(L, name, openf, glb) // 注册模块 +``` + +### Buffer + +```cpp +lua::L_Buffer buf; +lua::L_buffinit(L, &buf); +lua::L_addstring(&buf, "hello"); +lua::L_addlstring(&buf, ptr, len); +lua::L_pushresult(&buf); // 将结果压入栈 +``` + +### 标准库 + +```cpp +lua::open_base(L) lua::open_math(L) +lua::open_string(L) lua::open_table(L) +lua::open_io(L) lua::open_os(L) +lua::open_debug(L) lua::open_package(L) +lua::open_coroutine(L) lua::open_utf8(L) +``` + +### GC / 协程 / 调试 + +```cpp +lua::gc(L, lua::GCCOLLECT) // 完整 GC +lua::gc(L, lua::GCCOUNT) // 内存 KB + +lua::status(L) // 线程状态 +lua::resume(L, from, narg, &nres) // 恢复协程 + +lua::sethook(L, hook, lua::MASKCALL, 0) // 设置调试钩子 +lua::gethook(L) // 获取钩子 +``` + +## 常见模式 + +### 执行 Lua 并获取结果 + +```cpp +auto* L = lua::L_newstate(); +lua::L_openlibs(L); +lua::L_dostring(L, "x = 6 * 7"); +lua::getglobal(L, "x"); +auto result = lua::tointeger(L, -1); // 42 +lua::pop(L, 1); +lua::close(L); +``` + +### 调用 Lua 函数 + +```cpp +lua::L_dostring(L, "function add(a,b) return a+b end"); +lua::getglobal(L, "add"); +lua::pushinteger(L, 10); +lua::pushinteger(L, 20); +if (lua::pcall(L, 2, 1, 0) == lua::OK) { + auto sum = lua::tointeger(L, -1); // 30 + lua::pop(L, 1); +} +``` + +### C++ 回调函数 + +```cpp +// 回调签名必须用 lua::State* (不是 lua_State*) +static int my_add(lua::State* L) { + auto a = lua::tonumber(L, 1); + auto b = lua::tonumber(L, 2); + lua::pushnumber(L, a + b); + return 1; // 返回值个数 +} +lua::register_func(L, "add", my_add); +``` + +### 创建和遍历表 + +```cpp +// 创建 +lua::newtable(L); +lua::pushinteger(L, 42); +lua::setfield(L, -2, "x"); +lua::setglobal(L, "point"); + +// 遍历 +lua::getglobal(L, "t"); +lua::pushnil(L); +while (lua::next(L, -2) != 0) { + // key 在 -2,value 在 -1 + lua::pop(L, 1); // 弹出 value,保留 key 供 next 使用 +} +``` + +## 构建配置 + +### xmake + +```lua +add_requires("lua") + +target("myapp") + set_kind("binary") + set_languages("c++23") + add_files("main.cpp") + add_deps("mcpplibs-capi-lua") + add_packages("lua") + set_policy("build.c++.modules", true) +``` + +### 构建与运行 + +```bash +xmake build +xmake run capi_lua_test # 测试(97 tests) +xmake run basic # 基本示例 +xmake run table # 表操作示例 +xmake run function # 函数注册示例 +xmake run eval # 脚本求值示例 +``` + +## 注意事项 + +1. **回调函数签名**:必须使用 `lua::State*` 而非 `lua_State*`,因为 `lua_State` 在模块外不可见 +2. **`tostring` 陷阱**:对非字符串值调用 `lua::tostring()` 返回 `nullptr`,需先用 `lua::isstring()` 检查 +3. **接口+实现分离**:模块使用 `.cppm`(声明)+ `.cpp`(定义),不能用 `inline`(GCC C++ 模块限制) +4. **extern "C" 包装**:C 头文件通过 `src/capi/lua_headers.h` 包装 `extern "C"`,解决 GCC 模块链接问题 +5. **Lua 包依赖传递**:使用此库的 target 需要同时 `add_deps("mcpplibs-capi-lua")` 和 `add_packages("lua")` + +## 更多资源 + +- 完整 API 参考:[reference.md](reference.md) +- 设计文档:[docs/pr/design.md](../../../docs/pr/design.md) +- Lua 5.4 官方手册:[lua.org/manual/5.4](https://www.lua.org/manual/5.4/) +- mcpp-style-ref:[mcpp-style-ref](../mcpp-style-ref/SKILL.md) diff --git a/.agents/skills/mcpplibs-capi-lua/reference.md b/.agents/skills/mcpplibs-capi-lua/reference.md new file mode 100644 index 0000000..e3a6595 --- /dev/null +++ b/.agents/skills/mcpplibs-capi-lua/reference.md @@ -0,0 +1,364 @@ +# mcpplibs.capi.lua 完整 API 参考 + +`import mcpplibs.capi.lua;` — 命名空间 `mcpplibs::capi::lua` + +## 一、类型别名 + +| 模块内名称 | C API 原名 | 说明 | +|---|---|---| +| `State` | `lua_State` | Lua 虚拟机状态 | +| `Number` | `lua_Number` | 浮点数(通常 `double`) | +| `Integer` | `lua_Integer` | 整数(通常 `long long`) | +| `Unsigned` | `lua_Unsigned` | 无符号整数 | +| `CFunction` | `lua_CFunction` | C 函数指针 `int (*)(lua_State*)` | +| `KFunction` | `lua_KFunction` | 延续函数指针 | +| `KContext` | `lua_KContext` | 延续上下文 | +| `Alloc` | `lua_Alloc` | 分配器函数指针 | +| `Reader` | `lua_Reader` | 读取器函数指针 | +| `Writer` | `lua_Writer` | 写入器函数指针 | +| `WarnFunction` | `lua_WarnFunction` | 警告处理函数 | +| `Hook` | `lua_Hook` | 调试钩子函数 | +| `Debug` | `lua_Debug` | 调试信息结构体 | +| `L_Reg` | `luaL_Reg` | 函数注册表 `{ name, func }` | +| `L_Buffer` | `luaL_Buffer` | 字符串缓冲区 | + +--- + +## 二、常量 + +### 状态码 + +| 模块内 | C API 原名 | 值 | 说明 | +|---|---|---|---| +| `OK` | `LUA_OK` | 0 | 无错误 | +| `YIELD` | `LUA_YIELD` | 1 | 线程挂起 | +| `ERRRUN` | `LUA_ERRRUN` | 2 | 运行时错误 | +| `ERRSYNTAX` | `LUA_ERRSYNTAX` | 3 | 语法错误 | +| `ERRMEM` | `LUA_ERRMEM` | 4 | 内存分配错误 | +| `ERRERR` | `LUA_ERRERR` | 5 | 错误处理函数出错 | +| `ERRFILE` | `LUA_ERRFILE` | 6 | 文件相关错误 | + +### 类型标签 + +| 模块内 | C API 原名 | 值 | 说明 | +|---|---|---|---| +| `TNONE` | `LUA_TNONE` | -1 | 无效索引 | +| `TNIL` | `LUA_TNIL` | 0 | nil | +| `TBOOLEAN` | `LUA_TBOOLEAN` | 1 | 布尔值 | +| `TLIGHTUSERDATA` | `LUA_TLIGHTUSERDATA` | 2 | 轻量用户数据 | +| `TNUMBER` | `LUA_TNUMBER` | 3 | 数字 | +| `TSTRING` | `LUA_TSTRING` | 4 | 字符串 | +| `TTABLE` | `LUA_TTABLE` | 5 | 表 | +| `TFUNCTION` | `LUA_TFUNCTION` | 6 | 函数 | +| `TUSERDATA` | `LUA_TUSERDATA` | 7 | 完整用户数据 | +| `TTHREAD` | `LUA_TTHREAD` | 8 | 线程 | + +### 算术运算符 + +`OPADD`, `OPSUB`, `OPMUL`, `OPMOD`, `OPPOW`, `OPDIV`, `OPIDIV`, `OPBAND`, `OPBOR`, `OPBXOR`, `OPSHL`, `OPSHR`, `OPUNM`, `OPBNOT` + +### 比较运算符 + +`OPEQ`, `OPLT`, `OPLE` + +### 其他 + +| 模块内 | 说明 | +|---|---| +| `MULTRET` | 多返回值标记 (-1) | +| `REGISTRYINDEX` | 注册表伪索引 | +| `MINSTACK` | 最小栈保证大小 | +| `RIDX_MAINTHREAD` | 注册表中主线程的索引 | +| `RIDX_GLOBALS` | 注册表中全局表的索引 | +| `NOREF`, `REFNIL` | luaL_ref 特殊值 | + +### GC 选项 + +`GCSTOP`, `GCRESTART`, `GCCOLLECT`, `GCCOUNT`, `GCCOUNTB`, `GCSTEP`, `GCSETPAUSE`, `GCSETSTEPMUL`, `GCISRUNNING`, `GCGEN`, `GCINC` + +### Hook 掩码与事件 + +掩码:`MASKCALL`, `MASKRET`, `MASKLINE`, `MASKCOUNT` + +事件:`HOOKCALL`, `HOOKRET`, `HOOKLINE`, `HOOKCOUNT`, `HOOKTAILCALL` + +--- + +## 三、函数 + +### 3.1 状态管理 + +| 函数 | C API 原名 | 签名 | +|---|---|---| +| `newstate` | `lua_newstate` | `State* (Alloc f, void* ud)` | +| `close` | `lua_close` | `void (State* L)` | +| `newthread` | `lua_newthread` | `State* (State* L)` | +| `atpanic` | `lua_atpanic` | `CFunction (State* L, CFunction panicf)` | +| `version` | `lua_version` | `Number (State* L)` | + +### 3.2 栈操作 + +| 函数 | C API 原名 | 签名 | +|---|---|---| +| `absindex` | `lua_absindex` | `int (State* L, int idx)` | +| `gettop` | `lua_gettop` | `int (State* L)` | +| `settop` | `lua_settop` | `void (State* L, int idx)` | +| `pop` | `lua_pop` (宏) | `void (State* L, int n)` | +| `pushvalue` | `lua_pushvalue` | `void (State* L, int idx)` | +| `rotate` | `lua_rotate` | `void (State* L, int idx, int n)` | +| `copy` | `lua_copy` | `void (State* L, int fromidx, int toidx)` | +| `checkstack` | `lua_checkstack` | `int (State* L, int n)` | +| `xmove` | `lua_xmove` | `void (State* from, State* to, int n)` | +| `insert` | `lua_insert` (宏) | `void (State* L, int idx)` | +| `remove` | `lua_remove` (宏) | `void (State* L, int idx)` | +| `replace` | `lua_replace` (宏) | `void (State* L, int idx)` | + +### 3.3 入栈函数 + +| 函数 | C API 原名 | 签名 | +|---|---|---| +| `pushnil` | `lua_pushnil` | `void (State* L)` | +| `pushnumber` | `lua_pushnumber` | `void (State* L, Number n)` | +| `pushinteger` | `lua_pushinteger` | `void (State* L, Integer n)` | +| `pushlstring` | `lua_pushlstring` | `const char* (State* L, const char* s, ull len)` | +| `pushstring` | `lua_pushstring` | `const char* (State* L, const char* s)` | +| `pushcclosure` | `lua_pushcclosure` | `void (State* L, CFunction fn, int n)` | +| `pushcfunction` | `lua_pushcfunction` (宏) | `void (State* L, CFunction f)` | +| `pushboolean` | `lua_pushboolean` | `void (State* L, int b)` | +| `pushlightuserdata` | `lua_pushlightuserdata` | `void (State* L, void* p)` | +| `pushthread` | `lua_pushthread` | `int (State* L)` | +| `pushglobaltable` | `lua_pushglobaltable` (宏) | `void (State* L)` | +| `pushfstring` | `lua_pushfstring` | 多个重载 | + +### 3.4 类型检查 + +| 函数 | C API 原名 | 签名 | +|---|---|---| +| `type` | `lua_type` | `int (State* L, int idx)` | +| `type_name` | `lua_typename` | `const char* (State* L, int tp)` | +| `isnumber` | `lua_isnumber` | `int (State* L, int idx)` | +| `isstring` | `lua_isstring` | `int (State* L, int idx)` | +| `iscfunction` | `lua_iscfunction` | `int (State* L, int idx)` | +| `isinteger` | `lua_isinteger` | `int (State* L, int idx)` | +| `isuserdata` | `lua_isuserdata` | `int (State* L, int idx)` | +| `isfunction` | `lua_isfunction` (宏) | `int (State* L, int idx)` | +| `istable` | `lua_istable` (宏) | `int (State* L, int idx)` | +| `islightuserdata` | `lua_islightuserdata` (宏) | `int (State* L, int idx)` | +| `isnil` | `lua_isnil` (宏) | `int (State* L, int idx)` | +| `isboolean` | `lua_isboolean` (宏) | `int (State* L, int idx)` | +| `isthread` | `lua_isthread` (宏) | `int (State* L, int idx)` | +| `isnone` | `lua_isnone` (宏) | `int (State* L, int idx)` | +| `isnoneornil` | `lua_isnoneornil` (宏) | `int (State* L, int idx)` | + +### 3.5 取值函数 + +| 函数 | C API 原名 | 签名 | +|---|---|---| +| `tonumberx` | `lua_tonumberx` | `Number (State* L, int idx, int* isnum)` | +| `tointegerx` | `lua_tointegerx` | `Integer (State* L, int idx, int* isnum)` | +| `tonumber` | `lua_tonumber` (宏) | `Number (State* L, int idx)` | +| `tointeger` | `lua_tointeger` (宏) | `Integer (State* L, int idx)` | +| `toboolean` | `lua_toboolean` | `int (State* L, int idx)` | +| `tolstring` | `lua_tolstring` | `const char* (State* L, int idx, ull* len)` | +| `tostring` | `lua_tostring` (宏) | `const char* (State* L, int idx)` | +| `tocfunction` | `lua_tocfunction` | `CFunction (State* L, int idx)` | +| `touserdata` | `lua_touserdata` | `void* (State* L, int idx)` | +| `tothread` | `lua_tothread` | `State* (State* L, int idx)` | +| `topointer` | `lua_topointer` | `const void* (State* L, int idx)` | +| `rawlen` | `lua_rawlen` | `Unsigned (State* L, int idx)` | + +### 3.6 算术与比较 + +| 函数 | C API 原名 | 签名 | +|---|---|---| +| `arith` | `lua_arith` | `void (State* L, int op)` | +| `rawequal` | `lua_rawequal` | `int (State* L, int idx1, int idx2)` | +| `compare` | `lua_compare` | `int (State* L, int idx1, int idx2, int op)` | + +### 3.7 表操作(读取) + +| 函数 | C API 原名 | 签名 | +|---|---|---| +| `getglobal` | `lua_getglobal` | `int (State* L, const char* name)` | +| `gettable` | `lua_gettable` | `int (State* L, int idx)` | +| `getfield` | `lua_getfield` | `int (State* L, int idx, const char* k)` | +| `geti` | `lua_geti` | `int (State* L, int idx, Integer n)` | +| `rawget` | `lua_rawget` | `int (State* L, int idx)` | +| `rawgeti` | `lua_rawgeti` | `int (State* L, int idx, Integer n)` | +| `rawgetp` | `lua_rawgetp` | `int (State* L, int idx, const void* p)` | +| `createtable` | `lua_createtable` | `void (State* L, int narr, int nrec)` | +| `newtable` | `lua_newtable` (宏) | `void (State* L)` | +| `newuserdatauv` | `lua_newuserdatauv` | `void* (State* L, ull sz, int nuvalue)` | +| `newuserdata` | `lua_newuserdata` (宏) | `void* (State* L, ull sz)` | +| `getmetatable` | `lua_getmetatable` | `int (State* L, int objindex)` | +| `getiuservalue` | `lua_getiuservalue` | `int (State* L, int idx, int n)` | + +### 3.8 表操作(写入) + +| 函数 | C API 原名 | 签名 | +|---|---|---| +| `setglobal` | `lua_setglobal` | `void (State* L, const char* name)` | +| `settable` | `lua_settable` | `void (State* L, int idx)` | +| `setfield` | `lua_setfield` | `void (State* L, int idx, const char* k)` | +| `seti` | `lua_seti` | `void (State* L, int idx, Integer n)` | +| `rawset` | `lua_rawset` | `void (State* L, int idx)` | +| `rawseti` | `lua_rawseti` | `void (State* L, int idx, Integer n)` | +| `rawsetp` | `lua_rawsetp` | `void (State* L, int idx, const void* p)` | +| `setmetatable` | `lua_setmetatable` | `int (State* L, int objindex)` | +| `setiuservalue` | `lua_setiuservalue` | `int (State* L, int idx, int n)` | + +### 3.9 调用 + +| 函数 | C API 原名 | 签名 | +|---|---|---| +| `callk` | `lua_callk` | `void (State* L, int nargs, int nresults, KContext ctx, KFunction k)` | +| `call` | `lua_call` (宏) | `void (State* L, int nargs, int nresults)` | +| `pcallk` | `lua_pcallk` | `int (State* L, int nargs, int nresults, int errfunc, KContext ctx, KFunction k)` | +| `pcall` | `lua_pcall` (宏) | `int (State* L, int nargs, int nresults, int errfunc)` | +| `load` | `lua_load` | `int (State* L, Reader reader, void* dt, const char* chunkname, const char* mode)` | +| `dump` | `lua_dump` | `int (State* L, Writer writer, void* data, int strip)` | + +### 3.10 协程 + +| 函数 | C API 原名 | 签名 | +|---|---|---| +| `yieldk` | `lua_yieldk` | `int (State* L, int nresults, KContext ctx, KFunction k)` | +| `resume` | `lua_resume` | `int (State* L, State* from, int narg, int* nres)` | +| `status` | `lua_status` | `int (State* L)` | +| `isyieldable` | `lua_isyieldable` | `int (State* L)` | + +### 3.11 GC + +| 函数 | C API 原名 | 签名 | +|---|---|---| +| `gc` | `lua_gc` | `int (State* L, int what)` | +| `gc` | `lua_gc` | `int (State* L, int what, int data)` | +| `gc` | `lua_gc` | `int (State* L, int what, int data, int data2)` | + +### 3.12 其他 + +| 函数 | C API 原名 | 签名 | +|---|---|---| +| `error` | `lua_error` | `int (State* L)` | +| `next` | `lua_next` | `int (State* L, int idx)` | +| `concat` | `lua_concat` | `void (State* L, int n)` | +| `len` | `lua_len` | `void (State* L, int idx)` | +| `stringtonumber` | `lua_stringtonumber` | `ull (State* L, const char* s)` | +| `getallocf` | `lua_getallocf` | `Alloc (State* L, void** ud)` | +| `setallocf` | `lua_setallocf` | `void (State* L, Alloc f, void* ud)` | +| `toclose` | `lua_toclose` | `void (State* L, int idx)` | +| `closeslot` | `lua_closeslot` | `void (State* L, int idx)` | +| `getextraspace` | `lua_getextraspace` (宏) | `void* (State* L)` | +| `setwarnf` | `lua_setwarnf` | `void (State* L, WarnFunction f, void* ud)` | +| `warning` | `lua_warning` | `void (State* L, const char* msg, int tocont)` | +| `register_func` | `lua_register` (宏) | `void (State* L, const char* name, CFunction f)` | +| `upvalueindex` | `lua_upvalueindex` (宏) | `int (int i)` | + +### 3.13 调试接口 + +| 函数 | C API 原名 | 签名 | +|---|---|---| +| `getstack` | `lua_getstack` | `int (State* L, int level, Debug* ar)` | +| `getinfo` | `lua_getinfo` | `int (State* L, const char* what, Debug* ar)` | +| `getlocal` | `lua_getlocal` | `const char* (State* L, const Debug* ar, int n)` | +| `setlocal` | `lua_setlocal` | `const char* (State* L, const Debug* ar, int n)` | +| `getupvalue` | `lua_getupvalue` | `const char* (State* L, int funcindex, int n)` | +| `setupvalue` | `lua_setupvalue` | `const char* (State* L, int funcindex, int n)` | +| `upvalueid` | `lua_upvalueid` | `void* (State* L, int fidx, int n)` | +| `upvaluejoin` | `lua_upvaluejoin` | `void (State* L, int fidx1, int n1, int fidx2, int n2)` | +| `sethook` | `lua_sethook` | `void (State* L, Hook func, int mask, int count)` | +| `gethook` | `lua_gethook` | `Hook (State* L)` | +| `gethookmask` | `lua_gethookmask` | `int (State* L)` | +| `gethookcount` | `lua_gethookcount` | `int (State* L)` | + +### 3.14 辅助库 (luaL_*) + +| 函数 | C API 原名 | 签名 | +|---|---|---| +| `L_newstate` | `luaL_newstate` | `State* ()` | +| `L_openlibs` | `luaL_openlibs` | `void (State* L)` | +| `L_loadstring` | `luaL_loadstring` | `int (State* L, const char* s)` | +| `L_loadfilex` | `luaL_loadfilex` | `int (State* L, const char* filename, const char* mode)` | +| `L_loadfile` | `luaL_loadfile` (宏) | `int (State* L, const char* filename)` | +| `L_loadbufferx` | `luaL_loadbufferx` | `int (State* L, const char* buff, ull sz, const char* name, const char* mode)` | +| `L_dostring` | `luaL_dostring` (宏) | `int (State* L, const char* s)` | +| `L_dofile` | `luaL_dofile` (宏) | `int (State* L, const char* filename)` | +| `L_getmetafield` | `luaL_getmetafield` | `int (State* L, int obj, const char* e)` | +| `L_callmeta` | `luaL_callmeta` | `int (State* L, int obj, const char* e)` | +| `L_tolstring` | `luaL_tolstring` | `const char* (State* L, int idx, ull* len)` | +| `L_argerror` | `luaL_argerror` | `int (State* L, int arg, const char* extramsg)` | +| `L_typeerror` | `luaL_typeerror` | `int (State* L, int arg, const char* tname)` | +| `L_checklstring` | `luaL_checklstring` | `const char* (State* L, int arg, ull* l)` | +| `L_optlstring` | `luaL_optlstring` | `const char* (State* L, int arg, const char* def, ull* l)` | +| `L_checknumber` | `luaL_checknumber` | `Number (State* L, int arg)` | +| `L_optnumber` | `luaL_optnumber` | `Number (State* L, int arg, Number def)` | +| `L_checkinteger` | `luaL_checkinteger` | `Integer (State* L, int arg)` | +| `L_optinteger` | `luaL_optinteger` | `Integer (State* L, int arg, Integer def)` | +| `L_checkstack` | `luaL_checkstack` | `void (State* L, int sz, const char* msg)` | +| `L_checktype` | `luaL_checktype` | `void (State* L, int arg, int t)` | +| `L_checkany` | `luaL_checkany` | `void (State* L, int arg)` | +| `L_newmetatable` | `luaL_newmetatable` | `int (State* L, const char* tname)` | +| `L_setmetatable` | `luaL_setmetatable` | `void (State* L, const char* tname)` | +| `L_testudata` | `luaL_testudata` | `void* (State* L, int ud, const char* tname)` | +| `L_checkudata` | `luaL_checkudata` | `void* (State* L, int ud, const char* tname)` | +| `L_where` | `luaL_where` | `void (State* L, int lvl)` | +| `L_checkoption` | `luaL_checkoption` | `int (State* L, int arg, const char* def, const char* const lst[])` | +| `L_ref` | `luaL_ref` | `int (State* L, int t)` | +| `L_unref` | `luaL_unref` | `void (State* L, int t, int ref)` | +| `L_len` | `luaL_len` | `Integer (State* L, int idx)` | +| `L_gsub` | `luaL_gsub` | `const char* (State* L, const char* s, const char* p, const char* r)` | +| `L_setfuncs` | `luaL_setfuncs` | `void (State* L, const L_Reg* l, int nup)` | +| `L_getsubtable` | `luaL_getsubtable` | `int (State* L, int idx, const char* fname)` | +| `L_traceback` | `luaL_traceback` | `void (State* L, State* L1, const char* msg, int level)` | +| `L_requiref` | `luaL_requiref` | `void (State* L, const char* modname, CFunction openf, int glb)` | +| `L_typename` | `luaL_typename` (宏) | `const char* (State* L, int idx)` | +| `L_checkstring` | `luaL_checkstring` (宏) | `const char* (State* L, int arg)` | +| `L_optstring` | `luaL_optstring` (宏) | `const char* (State* L, int arg, const char* def)` | +| `L_getmetatable` | `luaL_getmetatable` (宏) | `int (State* L, const char* n)` | +| `L_checkversion` | `luaL_checkversion` (宏) | `void (State* L)` | +| `L_error` | `luaL_error` | `int (State* L, const char* fmt)` | +| `L_error` | `luaL_error` | `int (State* L, const char* fmt, const char* arg1)` | + +### 3.15 Buffer 操作 + +| 函数 | C API 原名 | 签名 | +|---|---|---| +| `L_buffinit` | `luaL_buffinit` | `void (State* L, L_Buffer* B)` | +| `L_prepbuffsize` | `luaL_prepbuffsize` | `char* (L_Buffer* B, ull sz)` | +| `L_addlstring` | `luaL_addlstring` | `void (L_Buffer* B, const char* s, ull l)` | +| `L_addstring` | `luaL_addstring` | `void (L_Buffer* B, const char* s)` | +| `L_addvalue` | `luaL_addvalue` | `void (L_Buffer* B)` | +| `L_pushresult` | `luaL_pushresult` | `void (L_Buffer* B)` | +| `L_pushresultsize` | `luaL_pushresultsize` | `void (L_Buffer* B, ull sz)` | +| `L_buffinitsize` | `luaL_buffinitsize` | `char* (State* L, L_Buffer* B, ull sz)` | + +### 3.16 标准库 + +| 函数 | C API 原名 | 签名 | +|---|---|---| +| `open_base` | `luaopen_base` | `int (State* L)` | +| `open_coroutine` | `luaopen_coroutine` | `int (State* L)` | +| `open_table` | `luaopen_table` | `int (State* L)` | +| `open_io` | `luaopen_io` | `int (State* L)` | +| `open_os` | `luaopen_os` | `int (State* L)` | +| `open_string` | `luaopen_string` | `int (State* L)` | +| `open_utf8` | `luaopen_utf8` | `int (State* L)` | +| `open_math` | `luaopen_math` | `int (State* L)` | +| `open_debug` | `luaopen_debug` | `int (State* L)` | +| `open_package` | `luaopen_package` | `int (State* L)` | + +--- + +## 四、命名映射速查表 + +| C API 前缀 | 模块内规则 | 示例 | +|---|---|---| +| `lua_` → 去掉 | `lua_gettop` → `gettop` | | +| `luaL_` → `L_` | `luaL_newstate` → `L_newstate` | | +| `LUA_` → 去掉 | `LUA_OK` → `OK` | | +| `luaopen_` → `open_` | `luaopen_base` → `open_base` | | +| `lua_typename` → `type_name` | 避免与 `type()` 冲突 | | +| `lua_register` → `register_func` | 避免关键字冲突 | | +| `lua_upvalueindex` → `upvalueindex` | 去 `lua_` | | + +> `ull` = `unsigned long long` From 57c8a62ebd54ad6c33ae54cf5cd25f0838091af1 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 26 Feb 2026 21:09:37 +0000 Subject: [PATCH 3/3] fix(ci): build library first to avoid C++ modules race condition GCC C++ modules require std.gcm to exist before dependent targets compile. Build mcpplibs-capi-lua first (serialized) then build remaining targets in parallel. Co-authored-by: SPeak --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6bdea37..a8e5a50 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,6 +47,7 @@ jobs: - name: Build run: | xmake f -m release -y -vv + xmake build -y -vv mcpplibs-capi-lua xmake -y -vv -j$(nproc) - name: Test