初始化内容

This commit is contained in:
2026-09-16 14:07:40 +08:00
parent 6934b390bf
commit 4f643c1e7c
18350 changed files with 6088489 additions and 305 deletions
+73
View File
@@ -0,0 +1,73 @@
---
AccessModifierOffset: -4
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: DontAlign
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: InlineOnly
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: false
AfterStruct: true
AfterUnion: true
BeforeCatch: true
BeforeElse: true
IndentBraces: false
SplitEmptyFunction: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeComma
BreakStringLiterals: true
ColumnLimit: 100
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 4
UseTab: Never
...
+2
View File
@@ -0,0 +1,2 @@
paths:
- 'Source'
+1
View File
@@ -0,0 +1 @@
repo_token: amVxRIVnLlAXJBJo02AKMkxVHN0IeBArV
+1
View File
@@ -0,0 +1 @@
* text=auto
+34
View File
@@ -0,0 +1,34 @@
name: Amalgamate
on:
push:
branches:
- master
paths:
- "**/workflows/amalgamate.yml"
- "**/Source/**"
- "**/amalgamate.py"
jobs:
run:
name: Create Amalgamation
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: 3.9
- name: Create amalgamation file
run: python amalgamate.py
- name: Commit changes
uses: EndBug/add-and-commit@v10
with:
committer_name: GitHub Actions
committer_email: actions@github.com
message: Update amalgamation file
add: 'Distribution/LuaBridge/*.h'
+105
View File
@@ -0,0 +1,105 @@
name: ASAN
on:
push:
paths:
- "**/workflows/build_asan.yml"
- "**/Source/**"
- "**/Tests/**"
- "**/ThirdParty/**"
- "**/CMakeLists.txt"
- "**/.gitmodules"
- "!**/*.md"
- "!**/*.txt"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
BUILD_TYPE: RelWithDebInfo
jobs:
lua:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
lua:
- { version: "5.1", suffix: "51" }
- { version: "5.2", suffix: "52" }
- { version: "5.3", suffix: "53" }
- { version: "5.4", suffix: "54" }
- { version: "5.5", suffix: "55" }
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Install Dependencies
run: sudo apt-get -y install ninja-build
- name: Create Build Environments
run: |
cmake -E make_directory ${{runner.workspace}}/build17
cmake -E make_directory ${{runner.workspace}}/build20
cmake -E make_directory ${{runner.workspace}}/build23
- name: Configure C++17
working-directory: ${{runner.workspace}}/build17
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=17 -DLUABRIDGE_SANITIZE=address -G Ninja
- name: Build Lua ${{ matrix.lua.version }} (C++17)
working-directory: ${{runner.workspace}}/build17
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTests${{ matrix.lua.suffix }} \
LuaBridgeTests${{ matrix.lua.suffix }}LuaC \
LuaBridgeTests${{ matrix.lua.suffix }}Noexcept \
LuaBridgeTests${{ matrix.lua.suffix }}LuaCNoexcept
- name: Test Lua ${{ matrix.lua.version }} (C++17)
working-directory: ${{runner.workspace}}/build17
env:
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0
run: ctest --parallel $(nproc) -R "LuaBridgeTests${{ matrix.lua.suffix }}" --output-on-failure
- name: Configure C++20
working-directory: ${{runner.workspace}}/build20
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=20 -DLUABRIDGE_SANITIZE=address -G Ninja
- name: Build Lua ${{ matrix.lua.version }} (C++20)
working-directory: ${{runner.workspace}}/build20
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTests${{ matrix.lua.suffix }} \
LuaBridgeTests${{ matrix.lua.suffix }}LuaC \
LuaBridgeTests${{ matrix.lua.suffix }}Noexcept \
LuaBridgeTests${{ matrix.lua.suffix }}LuaCNoexcept
- name: Test Lua ${{ matrix.lua.version }} (C++20)
working-directory: ${{runner.workspace}}/build20
env:
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0
run: ctest --parallel $(nproc) -R "LuaBridgeTests${{ matrix.lua.suffix }}" --output-on-failure
- name: Configure C++23
working-directory: ${{runner.workspace}}/build23
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=23 -DLUABRIDGE_SANITIZE=address -G Ninja
- name: Build Lua ${{ matrix.lua.version }} (C++23)
working-directory: ${{runner.workspace}}/build23
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTests${{ matrix.lua.suffix }} \
LuaBridgeTests${{ matrix.lua.suffix }}LuaC \
LuaBridgeTests${{ matrix.lua.suffix }}Noexcept \
LuaBridgeTests${{ matrix.lua.suffix }}LuaCNoexcept
- name: Test Lua ${{ matrix.lua.version }} (C++23)
working-directory: ${{runner.workspace}}/build23
env:
ASAN_OPTIONS: detect_leaks=0:detect_odr_violation=0
run: ctest --parallel $(nproc) -R "LuaBridgeTests${{ matrix.lua.suffix }}" --output-on-failure
+269
View File
@@ -0,0 +1,269 @@
name: Linux
on:
push:
paths:
- "**/workflows/build_linux.yml"
- "**/Source/**"
- "**/Tests/**"
- "**/ThirdParty/**"
- "**/CMakeLists.txt"
- "**/.gitmodules"
- "!**/*.md"
- "!**/*.txt"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
BUILD_TYPE: Release
jobs:
lua:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
lua:
- { version: "5.1", suffix: "51" }
- { version: "5.2", suffix: "52" }
- { version: "5.3", suffix: "53" }
- { version: "5.4", suffix: "54" }
- { version: "5.5", suffix: "55" }
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Install Dependencies
run: sudo apt-get -y install ninja-build
- name: Create Build Environments
run: |
cmake -E make_directory ${{runner.workspace}}/build17
cmake -E make_directory ${{runner.workspace}}/build20
cmake -E make_directory ${{runner.workspace}}/build23
- name: Configure C++17
working-directory: ${{runner.workspace}}/build17
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=17 -G Ninja
- name: Build Lua ${{ matrix.lua.version }} (C++17)
working-directory: ${{runner.workspace}}/build17
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTests${{ matrix.lua.suffix }} \
LuaBridgeTests${{ matrix.lua.suffix }}LuaC \
LuaBridgeTests${{ matrix.lua.suffix }}Noexcept \
LuaBridgeTests${{ matrix.lua.suffix }}LuaCNoexcept
- name: Test Lua ${{ matrix.lua.version }} (C++17)
working-directory: ${{runner.workspace}}/build17
run: ctest --parallel $(nproc) -R "LuaBridgeTests${{ matrix.lua.suffix }}" --output-on-failure
- name: Configure C++20
working-directory: ${{runner.workspace}}/build20
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=20 -G Ninja
- name: Build Lua ${{ matrix.lua.version }} (C++20)
working-directory: ${{runner.workspace}}/build20
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTests${{ matrix.lua.suffix }} \
LuaBridgeTests${{ matrix.lua.suffix }}LuaC \
LuaBridgeTests${{ matrix.lua.suffix }}Noexcept \
LuaBridgeTests${{ matrix.lua.suffix }}LuaCNoexcept
- name: Test Lua ${{ matrix.lua.version }} (C++20)
working-directory: ${{runner.workspace}}/build20
run: ctest --parallel $(nproc) -R "LuaBridgeTests${{ matrix.lua.suffix }}" --output-on-failure
- name: Configure C++23
working-directory: ${{runner.workspace}}/build23
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=23 -G Ninja
- name: Build Lua ${{ matrix.lua.version }} (C++23)
working-directory: ${{runner.workspace}}/build23
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTests${{ matrix.lua.suffix }} \
LuaBridgeTests${{ matrix.lua.suffix }}LuaC \
LuaBridgeTests${{ matrix.lua.suffix }}Noexcept \
LuaBridgeTests${{ matrix.lua.suffix }}LuaCNoexcept
- name: Test Lua ${{ matrix.lua.version }} (C++23)
working-directory: ${{runner.workspace}}/build23
run: ctest --parallel $(nproc) -R "LuaBridgeTests${{ matrix.lua.suffix }}" --output-on-failure
luajit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get -y install ninja-build
- name: Create Build Environments
run: |
cmake -E make_directory ${{runner.workspace}}/build17
cmake -E make_directory ${{runner.workspace}}/build20
cmake -E make_directory ${{runner.workspace}}/build23
- name: Configure C++17
working-directory: ${{runner.workspace}}/build17
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=17 -G Ninja
- name: Build LuaJIT (C++17)
working-directory: ${{runner.workspace}}/build17
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTestsLuaJIT \
LuaBridgeTestsLuaJITNoexcept
- name: Test LuaJIT (C++17)
working-directory: ${{runner.workspace}}/build17
run: ctest --parallel $(nproc) -R "LuaBridgeTestsLuaJIT" --output-on-failure
- name: Configure C++20
working-directory: ${{runner.workspace}}/build20
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=20 -G Ninja
- name: Build LuaJIT (C++20)
working-directory: ${{runner.workspace}}/build20
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTestsLuaJIT \
LuaBridgeTestsLuaJITNoexcept
- name: Test LuaJIT (C++20)
working-directory: ${{runner.workspace}}/build20
run: ctest --parallel $(nproc) -R "LuaBridgeTestsLuaJIT" --output-on-failure
- name: Configure C++23
working-directory: ${{runner.workspace}}/build23
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=23 -G Ninja
- name: Build LuaJIT (C++23)
working-directory: ${{runner.workspace}}/build23
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTestsLuaJIT \
LuaBridgeTestsLuaJITNoexcept
- name: Test LuaJIT (C++23)
working-directory: ${{runner.workspace}}/build23
run: ctest --parallel $(nproc) -R "LuaBridgeTestsLuaJIT" --output-on-failure
luau:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Install Dependencies
run: sudo apt-get -y install ninja-build
- name: Create Build Environments
run: |
cmake -E make_directory ${{runner.workspace}}/build17
cmake -E make_directory ${{runner.workspace}}/build20
cmake -E make_directory ${{runner.workspace}}/build23
- name: Configure C++17
working-directory: ${{runner.workspace}}/build17
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=17 -G Ninja
- name: Build Luau (C++17)
working-directory: ${{runner.workspace}}/build17
run: cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target LuaBridgeTestsLuau
- name: Test Luau (C++17)
working-directory: ${{runner.workspace}}/build17
run: ctest --parallel $(nproc) -R "LuaBridgeTestsLuau" --output-on-failure
- name: Configure C++20
working-directory: ${{runner.workspace}}/build20
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=20 -G Ninja
- name: Build Luau (C++20)
working-directory: ${{runner.workspace}}/build20
run: cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target LuaBridgeTestsLuau
- name: Test Luau (C++20)
working-directory: ${{runner.workspace}}/build20
run: ctest --parallel $(nproc) -R "LuaBridgeTestsLuau" --output-on-failure
- name: Configure C++23
working-directory: ${{runner.workspace}}/build23
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=23 -G Ninja
- name: Build Luau (C++23)
working-directory: ${{runner.workspace}}/build23
run: cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target LuaBridgeTestsLuau
- name: Test Luau (C++23)
working-directory: ${{runner.workspace}}/build23
run: ctest --parallel $(nproc) -R "LuaBridgeTestsLuau" --output-on-failure
ravi:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Install Dependencies
run: sudo apt-get -y install libreadline-dev ninja-build
- name: Create Build Environments
run: |
cmake -E make_directory ${{runner.workspace}}/build17
cmake -E make_directory ${{runner.workspace}}/build20
cmake -E make_directory ${{runner.workspace}}/build23
- name: Configure C++17
working-directory: ${{runner.workspace}}/build17
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=17 -G Ninja
- name: Build Ravi (C++17)
working-directory: ${{runner.workspace}}/build17
run: cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target LuaBridgeTestsRavi
- name: Test Ravi (C++17)
working-directory: ${{runner.workspace}}/build17
run: ctest --parallel $(nproc) -R "LuaBridgeTestsRavi" --output-on-failure
- name: Configure C++20
working-directory: ${{runner.workspace}}/build20
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=20 -G Ninja
- name: Build Ravi (C++20)
working-directory: ${{runner.workspace}}/build20
run: cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target LuaBridgeTestsRavi
- name: Test Ravi (C++20)
working-directory: ${{runner.workspace}}/build20
run: ctest --parallel $(nproc) -R "LuaBridgeTestsRavi" --output-on-failure
- name: Configure C++23
working-directory: ${{runner.workspace}}/build23
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=23 -G Ninja
- name: Build Ravi (C++23)
working-directory: ${{runner.workspace}}/build23
run: cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target LuaBridgeTestsRavi
- name: Test Ravi (C++23)
working-directory: ${{runner.workspace}}/build23
run: ctest --parallel $(nproc) -R "LuaBridgeTestsRavi" --output-on-failure
+255
View File
@@ -0,0 +1,255 @@
name: macOS
on:
push:
paths:
- "**/workflows/build_macos.yml"
- "**/Source/**"
- "**/Tests/**"
- "**/ThirdParty/**"
- "**/CMakeLists.txt"
- "**/.gitmodules"
- "!**/*.md"
- "!**/*.txt"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
BUILD_TYPE: Release
jobs:
lua:
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
lua:
- { version: "5.1", suffix: "51" }
- { version: "5.2", suffix: "52" }
- { version: "5.3", suffix: "53" }
- { version: "5.4", suffix: "54" }
- { version: "5.5", suffix: "55" }
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Create Build Environments
run: |
cmake -E make_directory ${{runner.workspace}}/build17
cmake -E make_directory ${{runner.workspace}}/build20
cmake -E make_directory ${{runner.workspace}}/build23
- name: Configure C++17
working-directory: ${{runner.workspace}}/build17
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=17 -G Ninja
- name: Build Lua ${{ matrix.lua.version }} (C++17)
working-directory: ${{runner.workspace}}/build17
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTests${{ matrix.lua.suffix }} \
LuaBridgeTests${{ matrix.lua.suffix }}LuaC \
LuaBridgeTests${{ matrix.lua.suffix }}Noexcept \
LuaBridgeTests${{ matrix.lua.suffix }}LuaCNoexcept
- name: Test Lua ${{ matrix.lua.version }} (C++17)
working-directory: ${{runner.workspace}}/build17
run: ctest --parallel $(nproc) -R "LuaBridgeTests${{ matrix.lua.suffix }}" --output-on-failure
- name: Configure C++20
working-directory: ${{runner.workspace}}/build20
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=20 -G Ninja
- name: Build Lua ${{ matrix.lua.version }} (C++20)
working-directory: ${{runner.workspace}}/build20
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTests${{ matrix.lua.suffix }} \
LuaBridgeTests${{ matrix.lua.suffix }}LuaC \
LuaBridgeTests${{ matrix.lua.suffix }}Noexcept \
LuaBridgeTests${{ matrix.lua.suffix }}LuaCNoexcept
- name: Test Lua ${{ matrix.lua.version }} (C++20)
working-directory: ${{runner.workspace}}/build20
run: ctest --parallel $(nproc) -R "LuaBridgeTests${{ matrix.lua.suffix }}" --output-on-failure
- name: Configure C++23
working-directory: ${{runner.workspace}}/build23
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=23 -G Ninja
- name: Build Lua ${{ matrix.lua.version }} (C++23)
working-directory: ${{runner.workspace}}/build23
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTests${{ matrix.lua.suffix }} \
LuaBridgeTests${{ matrix.lua.suffix }}LuaC \
LuaBridgeTests${{ matrix.lua.suffix }}Noexcept \
LuaBridgeTests${{ matrix.lua.suffix }}LuaCNoexcept
- name: Test Lua ${{ matrix.lua.version }} (C++23)
working-directory: ${{runner.workspace}}/build23
run: ctest --parallel $(nproc) -R "LuaBridgeTests${{ matrix.lua.suffix }}" --output-on-failure
luajit:
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Create Build Environments
run: |
cmake -E make_directory ${{runner.workspace}}/build17
cmake -E make_directory ${{runner.workspace}}/build20
cmake -E make_directory ${{runner.workspace}}/build23
- name: Configure C++17
working-directory: ${{runner.workspace}}/build17
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=17 -G Ninja
- name: Build LuaJIT (C++17)
working-directory: ${{runner.workspace}}/build17
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTestsLuaJIT \
LuaBridgeTestsLuaJITNoexcept
- name: Test LuaJIT (C++17)
working-directory: ${{runner.workspace}}/build17
run: ctest --parallel $(nproc) -R "LuaBridgeTestsLuaJIT" --output-on-failure
- name: Configure C++20
working-directory: ${{runner.workspace}}/build20
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=20 -G Ninja
- name: Build LuaJIT (C++20)
working-directory: ${{runner.workspace}}/build20
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTestsLuaJIT \
LuaBridgeTestsLuaJITNoexcept
- name: Test LuaJIT (C++20)
working-directory: ${{runner.workspace}}/build20
run: ctest --parallel $(nproc) -R "LuaBridgeTestsLuaJIT" --output-on-failure
- name: Configure C++23
working-directory: ${{runner.workspace}}/build23
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=23 -G Ninja
- name: Build LuaJIT (C++23)
working-directory: ${{runner.workspace}}/build23
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTestsLuaJIT \
LuaBridgeTestsLuaJITNoexcept
- name: Test LuaJIT (C++23)
working-directory: ${{runner.workspace}}/build23
run: ctest --parallel $(nproc) -R "LuaBridgeTestsLuaJIT" --output-on-failure
luau:
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Create Build Environments
run: |
cmake -E make_directory ${{runner.workspace}}/build17
cmake -E make_directory ${{runner.workspace}}/build20
cmake -E make_directory ${{runner.workspace}}/build23
- name: Configure C++17
working-directory: ${{runner.workspace}}/build17
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=17 -G Ninja
- name: Build Luau (C++17)
working-directory: ${{runner.workspace}}/build17
run: cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target LuaBridgeTestsLuau
- name: Test Luau (C++17)
working-directory: ${{runner.workspace}}/build17
run: ctest --parallel $(nproc) -R "LuaBridgeTestsLuau" --output-on-failure
- name: Configure C++20
working-directory: ${{runner.workspace}}/build20
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=20 -G Ninja
- name: Build Luau (C++20)
working-directory: ${{runner.workspace}}/build20
run: cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target LuaBridgeTestsLuau
- name: Test Luau (C++20)
working-directory: ${{runner.workspace}}/build20
run: ctest --parallel $(nproc) -R "LuaBridgeTestsLuau" --output-on-failure
- name: Configure C++23
working-directory: ${{runner.workspace}}/build23
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=23 -G Ninja
- name: Build Luau (C++23)
working-directory: ${{runner.workspace}}/build23
run: cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target LuaBridgeTestsLuau
- name: Test Luau (C++23)
working-directory: ${{runner.workspace}}/build23
run: ctest --parallel $(nproc) -R "LuaBridgeTestsLuau" --output-on-failure
ravi:
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Create Build Environments
run: |
cmake -E make_directory ${{runner.workspace}}/build17
cmake -E make_directory ${{runner.workspace}}/build20
cmake -E make_directory ${{runner.workspace}}/build23
- name: Configure C++17
working-directory: ${{runner.workspace}}/build17
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=17 -G Ninja
- name: Build Ravi (C++17)
working-directory: ${{runner.workspace}}/build17
run: cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target LuaBridgeTestsRavi
- name: Test Ravi (C++17)
working-directory: ${{runner.workspace}}/build17
run: ctest --parallel $(nproc) -R "LuaBridgeTestsRavi" --output-on-failure
- name: Configure C++20
working-directory: ${{runner.workspace}}/build20
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=20 -G Ninja
- name: Build Ravi (C++20)
working-directory: ${{runner.workspace}}/build20
run: cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target LuaBridgeTestsRavi
- name: Test Ravi (C++20)
working-directory: ${{runner.workspace}}/build20
run: ctest --parallel $(nproc) -R "LuaBridgeTestsRavi" --output-on-failure
- name: Configure C++23
working-directory: ${{runner.workspace}}/build23
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=23 -G Ninja
- name: Build Ravi (C++23)
working-directory: ${{runner.workspace}}/build23
run: cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target LuaBridgeTestsRavi
- name: Test Ravi (C++23)
working-directory: ${{runner.workspace}}/build23
run: ctest --parallel $(nproc) -R "LuaBridgeTestsRavi" --output-on-failure
+68
View File
@@ -0,0 +1,68 @@
name: TSAN
on:
push:
paths:
- "**/workflows/build_tsan.yml"
- "**/Source/**"
- "**/Tests/**"
- "**/ThirdParty/**"
- "**/CMakeLists.txt"
- "**/.gitmodules"
- "!**/*.md"
- "!**/*.txt"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
BUILD_TYPE: RelWithDebInfo
jobs:
lua:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
lua:
- { version: "5.1", suffix: "51" }
- { version: "5.2", suffix: "52" }
- { version: "5.3", suffix: "53" }
- { version: "5.4", suffix: "54" }
- { version: "5.5", suffix: "55" }
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Install Dependencies
run: sudo apt-get -y install ninja-build
- name: Create Build Environment
run: cmake -E make_directory ${{runner.workspace}}/build
- name: Configure
working-directory: ${{runner.workspace}}/build
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DLUABRIDGE_SANITIZE=thread -G Ninja
- name: Build Lua ${{ matrix.lua.version }}
working-directory: ${{runner.workspace}}/build
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTests${{ matrix.lua.suffix }} \
LuaBridgeTests${{ matrix.lua.suffix }}LuaC \
LuaBridgeTests${{ matrix.lua.suffix }}Noexcept \
LuaBridgeTests${{ matrix.lua.suffix }}LuaCNoexcept
- name: Test Lua ${{ matrix.lua.version }}
working-directory: ${{runner.workspace}}/build/Tests
env:
TSAN_OPTIONS: halt_on_error=1:print_stacktrace=1
run: |
./LuaBridgeTests${{ matrix.lua.suffix }}
./LuaBridgeTests${{ matrix.lua.suffix }}LuaC
./LuaBridgeTests${{ matrix.lua.suffix }}Noexcept
./LuaBridgeTests${{ matrix.lua.suffix }}LuaCNoexcept
+105
View File
@@ -0,0 +1,105 @@
name: UBSAN
on:
push:
paths:
- "**/workflows/build_ubsan.yml"
- "**/Source/**"
- "**/Tests/**"
- "**/ThirdParty/**"
- "**/CMakeLists.txt"
- "**/.gitmodules"
- "!**/*.md"
- "!**/*.txt"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
BUILD_TYPE: RelWithDebInfo
jobs:
lua:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
lua:
- { version: "5.1", suffix: "51" }
- { version: "5.2", suffix: "52" }
- { version: "5.3", suffix: "53" }
- { version: "5.4", suffix: "54" }
- { version: "5.5", suffix: "55" }
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Install Dependencies
run: sudo apt-get -y install ninja-build
- name: Create Build Environments
run: |
cmake -E make_directory ${{runner.workspace}}/build17
cmake -E make_directory ${{runner.workspace}}/build20
cmake -E make_directory ${{runner.workspace}}/build23
- name: Configure C++17
working-directory: ${{runner.workspace}}/build17
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=17 -DLUABRIDGE_SANITIZE=undefined -G Ninja
- name: Build Lua ${{ matrix.lua.version }} (C++17)
working-directory: ${{runner.workspace}}/build17
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTests${{ matrix.lua.suffix }} \
LuaBridgeTests${{ matrix.lua.suffix }}LuaC \
LuaBridgeTests${{ matrix.lua.suffix }}Noexcept \
LuaBridgeTests${{ matrix.lua.suffix }}LuaCNoexcept
- name: Test Lua ${{ matrix.lua.version }} (C++17)
working-directory: ${{runner.workspace}}/build17
env:
UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1
run: ctest --parallel $(nproc) -R "LuaBridgeTests${{ matrix.lua.suffix }}" --output-on-failure
- name: Configure C++20
working-directory: ${{runner.workspace}}/build20
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=20 -DLUABRIDGE_SANITIZE=undefined -G Ninja
- name: Build Lua ${{ matrix.lua.version }} (C++20)
working-directory: ${{runner.workspace}}/build20
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTests${{ matrix.lua.suffix }} \
LuaBridgeTests${{ matrix.lua.suffix }}LuaC \
LuaBridgeTests${{ matrix.lua.suffix }}Noexcept \
LuaBridgeTests${{ matrix.lua.suffix }}LuaCNoexcept
- name: Test Lua ${{ matrix.lua.version }} (C++20)
working-directory: ${{runner.workspace}}/build20
env:
UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1
run: ctest --parallel $(nproc) -R "LuaBridgeTests${{ matrix.lua.suffix }}" --output-on-failure
- name: Configure C++23
working-directory: ${{runner.workspace}}/build23
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=23 -DLUABRIDGE_SANITIZE=undefined -G Ninja
- name: Build Lua ${{ matrix.lua.version }} (C++23)
working-directory: ${{runner.workspace}}/build23
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTests${{ matrix.lua.suffix }} \
LuaBridgeTests${{ matrix.lua.suffix }}LuaC \
LuaBridgeTests${{ matrix.lua.suffix }}Noexcept \
LuaBridgeTests${{ matrix.lua.suffix }}LuaCNoexcept
- name: Test Lua ${{ matrix.lua.version }} (C++23)
working-directory: ${{runner.workspace}}/build23
env:
UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1
run: ctest --parallel $(nproc) -R "LuaBridgeTests${{ matrix.lua.suffix }}" --output-on-failure
+297
View File
@@ -0,0 +1,297 @@
name: Windows
on:
push:
paths:
- "**/workflows/build_windows.yml"
- "**/Source/**"
- "**/Tests/**"
- "**/ThirdParty/**"
- "**/CMakeLists.txt"
- "**/.gitmodules"
- "!**/*.md"
- "!**/*.txt"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
BUILD_TYPE: Release
jobs:
lua:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
lua:
- { version: "5.1", suffix: "51" }
- { version: "5.2", suffix: "52" }
- { version: "5.3", suffix: "53" }
- { version: "5.4", suffix: "54" }
- { version: "5.5", suffix: "55" }
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Create Build Environments
run: |
cmake -E make_directory ${{runner.workspace}}/build17
cmake -E make_directory ${{runner.workspace}}/build20
cmake -E make_directory ${{runner.workspace}}/build23
- name: Configure C++17
shell: bash
working-directory: ${{runner.workspace}}/build17
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=17
- name: Build Lua ${{ matrix.lua.version }} (C++17)
working-directory: ${{runner.workspace}}/build17
shell: bash
run: |
cmake --build . --config $BUILD_TYPE --parallel 4 --target \
LuaBridgeTests${{ matrix.lua.suffix }} \
LuaBridgeTests${{ matrix.lua.suffix }}LuaC \
LuaBridgeTests${{ matrix.lua.suffix }}Noexcept \
LuaBridgeTests${{ matrix.lua.suffix }}LuaCNoexcept
- name: Test Lua ${{ matrix.lua.version }} (C++17)
working-directory: ${{runner.workspace}}/build17
shell: bash
run: ctest --parallel 4 -C $BUILD_TYPE -R "LuaBridgeTests${{ matrix.lua.suffix }}" --output-on-failure
- name: Configure C++20
shell: bash
working-directory: ${{runner.workspace}}/build20
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=20
- name: Build Lua ${{ matrix.lua.version }} (C++20)
working-directory: ${{runner.workspace}}/build20
shell: bash
run: |
cmake --build . --config $BUILD_TYPE --parallel 4 --target \
LuaBridgeTests${{ matrix.lua.suffix }} \
LuaBridgeTests${{ matrix.lua.suffix }}LuaC \
LuaBridgeTests${{ matrix.lua.suffix }}Noexcept \
LuaBridgeTests${{ matrix.lua.suffix }}LuaCNoexcept
- name: Test Lua ${{ matrix.lua.version }} (C++20)
working-directory: ${{runner.workspace}}/build20
shell: bash
run: ctest --parallel 4 -C $BUILD_TYPE -R "LuaBridgeTests${{ matrix.lua.suffix }}" --output-on-failure
- name: Configure C++23
shell: bash
working-directory: ${{runner.workspace}}/build23
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=23
- name: Build Lua ${{ matrix.lua.version }} (C++23)
working-directory: ${{runner.workspace}}/build23
shell: bash
run: |
cmake --build . --config $BUILD_TYPE --parallel 4 --target \
LuaBridgeTests${{ matrix.lua.suffix }} \
LuaBridgeTests${{ matrix.lua.suffix }}LuaC \
LuaBridgeTests${{ matrix.lua.suffix }}Noexcept \
LuaBridgeTests${{ matrix.lua.suffix }}LuaCNoexcept
- name: Test Lua ${{ matrix.lua.version }} (C++23)
working-directory: ${{runner.workspace}}/build23
shell: bash
run: ctest --parallel 4 -C $BUILD_TYPE -R "LuaBridgeTests${{ matrix.lua.suffix }}" --output-on-failure
luajit:
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Create Build Environments
run: |
cmake -E make_directory ${{runner.workspace}}/build17
cmake -E make_directory ${{runner.workspace}}/build20
cmake -E make_directory ${{runner.workspace}}/build23
- name: Configure C++17
shell: bash
working-directory: ${{runner.workspace}}/build17
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=17
- name: Build LuaJIT (C++17)
working-directory: ${{runner.workspace}}/build17
shell: bash
run: |
cmake --build . --config $BUILD_TYPE --parallel 4 --target \
LuaBridgeTestsLuaJIT \
LuaBridgeTestsLuaJITNoexcept
- name: Test LuaJIT (C++17)
working-directory: ${{runner.workspace}}/build17
shell: bash
run: ctest --parallel 4 -C $BUILD_TYPE -R "LuaBridgeTestsLuaJIT" --output-on-failure
- name: Configure C++20
shell: bash
working-directory: ${{runner.workspace}}/build20
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=20
- name: Build LuaJIT (C++20)
working-directory: ${{runner.workspace}}/build20
shell: bash
run: |
cmake --build . --config $BUILD_TYPE --parallel 4 --target \
LuaBridgeTestsLuaJIT \
LuaBridgeTestsLuaJITNoexcept
- name: Test LuaJIT (C++20)
working-directory: ${{runner.workspace}}/build20
shell: bash
run: ctest --parallel 4 -C $BUILD_TYPE -R "LuaBridgeTestsLuaJIT" --output-on-failure
- name: Configure C++23
shell: bash
working-directory: ${{runner.workspace}}/build23
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=23
- name: Build LuaJIT (C++23)
working-directory: ${{runner.workspace}}/build23
shell: bash
run: |
cmake --build . --config $BUILD_TYPE --parallel 4 --target \
LuaBridgeTestsLuaJIT \
LuaBridgeTestsLuaJITNoexcept
- name: Test LuaJIT (C++23)
working-directory: ${{runner.workspace}}/build23
shell: bash
run: ctest --parallel 4 -C $BUILD_TYPE -R "LuaBridgeTestsLuaJIT" --output-on-failure
luau:
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Create Build Environments
run: |
cmake -E make_directory ${{runner.workspace}}/build17
cmake -E make_directory ${{runner.workspace}}/build20
cmake -E make_directory ${{runner.workspace}}/build23
- name: Configure C++17
shell: bash
working-directory: ${{runner.workspace}}/build17
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=17
- name: Build Luau (C++17)
working-directory: ${{runner.workspace}}/build17
shell: bash
run: cmake --build . --config $BUILD_TYPE --parallel 4 --target LuaBridgeTestsLuau
- name: Test Luau (C++17)
working-directory: ${{runner.workspace}}/build17
shell: bash
run: ctest --parallel 4 -C $BUILD_TYPE -R "LuaBridgeTestsLuau" --output-on-failure
- name: Configure C++20
shell: bash
working-directory: ${{runner.workspace}}/build20
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=20
- name: Build Luau (C++20)
working-directory: ${{runner.workspace}}/build20
shell: bash
run: cmake --build . --config $BUILD_TYPE --parallel 4 --target LuaBridgeTestsLuau
- name: Test Luau (C++20)
working-directory: ${{runner.workspace}}/build20
shell: bash
run: ctest --parallel 4 -C $BUILD_TYPE -R "LuaBridgeTestsLuau" --output-on-failure
- name: Configure C++23
shell: bash
working-directory: ${{runner.workspace}}/build23
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=23
- name: Build Luau (C++23)
working-directory: ${{runner.workspace}}/build23
shell: bash
run: cmake --build . --config $BUILD_TYPE --parallel 4 --target LuaBridgeTestsLuau
- name: Test Luau (C++23)
working-directory: ${{runner.workspace}}/build23
shell: bash
run: ctest --parallel 4 -C $BUILD_TYPE -R "LuaBridgeTestsLuau" --output-on-failure
ravi:
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Create Build Environments
run: |
cmake -E make_directory ${{runner.workspace}}/build17
cmake -E make_directory ${{runner.workspace}}/build20
cmake -E make_directory ${{runner.workspace}}/build23
- name: Configure C++17
shell: bash
working-directory: ${{runner.workspace}}/build17
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=17
- name: Build Ravi (C++17)
working-directory: ${{runner.workspace}}/build17
shell: bash
run: cmake --build . --config $BUILD_TYPE --parallel 4 --target LuaBridgeTestsRavi
- name: Test Ravi (C++17)
working-directory: ${{runner.workspace}}/build17/Tests/Release
shell: bash
run: |
cp ../ravi/Release/libravi.dll .
./LuaBridgeTestsRavi.exe
- name: Configure C++20
shell: bash
working-directory: ${{runner.workspace}}/build20
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=20
- name: Build Ravi (C++20)
working-directory: ${{runner.workspace}}/build20
shell: bash
run: cmake --build . --config $BUILD_TYPE --parallel 4 --target LuaBridgeTestsRavi
- name: Test Ravi (C++20)
working-directory: ${{runner.workspace}}/build20/Tests/Release
shell: bash
run: |
cp ../ravi/Release/libravi.dll .
./LuaBridgeTestsRavi.exe
- name: Configure C++23
shell: bash
working-directory: ${{runner.workspace}}/build23
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=23
- name: Build Ravi (C++23)
working-directory: ${{runner.workspace}}/build23
shell: bash
run: cmake --build . --config $BUILD_TYPE --parallel 4 --target LuaBridgeTestsRavi
- name: Test Ravi (C++23)
working-directory: ${{runner.workspace}}/build23/Tests/Release
shell: bash
run: |
cp ../ravi/Release/libravi.dll .
./LuaBridgeTestsRavi.exe
+65
View File
@@ -0,0 +1,65 @@
name: CodeQL
on:
push:
branches: [ master ]
paths:
- "**/workflows/codeql-analysis.yml"
- "**/Source/**"
- "**/Tests/**"
- "**/ThirdParty/**"
- "**/CMakeLists.txt"
- "**/.gitmodules"
- "**/.codeql.yml"
- "!**/*.md"
- "!**/*.txt"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
BUILD_TYPE: Release
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'cpp' ]
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
submodules: true
- name: Install Ninja
run: |
sudo apt-get update
sudo apt-get -y install ninja-build libreadline-dev
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
config-file: ./.codeql.yml
- name: Create Build Environment
run: cmake -E make_directory ${{runner.workspace}}/build
- name: Configure CMake
shell: bash
working-directory: ${{runner.workspace}}/build
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja
- name: Build
working-directory: ${{runner.workspace}}/build
shell: bash
run: cmake --build . --config $BUILD_TYPE --parallel $(nproc)
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
+512
View File
@@ -0,0 +1,512 @@
name: Coverage
on:
push:
paths:
- "**/workflows/coverage.yml"
- "**/Source/**"
- "**/Tests/**"
- "**/ThirdParty/**"
- "**/CMakeLists.txt"
- "**/.gitmodules"
- "!**/*.md"
- "!**/*.txt"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
BUILD_TYPE: Debug
jobs:
lua:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
lua:
- { version: "5.1", suffix: "51" }
- { version: "5.2", suffix: "52" }
- { version: "5.3", suffix: "53" }
- { version: "5.4", suffix: "54" }
- { version: "5.5", suffix: "55" }
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Install lcov
run: sudo apt-get install -y lcov ninja-build
- name: Create Build Environments
run: |
cmake -E make_directory ${{runner.workspace}}/build17
cmake -E make_directory ${{runner.workspace}}/build20
cmake -E make_directory ${{runner.workspace}}/build23
cmake -E make_directory ${{runner.workspace}}/coverage
- name: Configure C++17
working-directory: ${{runner.workspace}}/build17
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=17 -DLUABRIDGE_COVERAGE=ON -G Ninja
- name: Build Lua ${{ matrix.lua.version }} (C++17)
working-directory: ${{runner.workspace}}/build17
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTests${{ matrix.lua.suffix }} \
LuaBridgeTests${{ matrix.lua.suffix }}LuaC \
LuaBridgeTests${{ matrix.lua.suffix }}Noexcept \
LuaBridgeTests${{ matrix.lua.suffix }}LuaCNoexcept
- name: Test Lua ${{ matrix.lua.version }} (C++17)
working-directory: ${{runner.workspace}}/build17
run: ctest --parallel $(nproc) -R "LuaBridgeTests${{ matrix.lua.suffix }}" --output-on-failure
- name: Coverage Lua ${{ matrix.lua.version }} (C++17)
working-directory: ${{runner.workspace}}/build17
run: |
lcov -c -d "${{runner.workspace}}/build17" --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 \
--ignore-errors mismatch,unused \
--include "*/LuaBridge/*" --exclude "*/Tests/*" --exclude "*/Distribution/*" --exclude "*/coverage_html/*" \
-o "${{runner.workspace}}/coverage/lua${{ matrix.lua.suffix }}_cxx17.info"
- name: Configure C++20
working-directory: ${{runner.workspace}}/build20
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=20 -DLUABRIDGE_COVERAGE=ON -G Ninja
- name: Build Lua ${{ matrix.lua.version }} (C++20)
working-directory: ${{runner.workspace}}/build20
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTests${{ matrix.lua.suffix }} \
LuaBridgeTests${{ matrix.lua.suffix }}LuaC \
LuaBridgeTests${{ matrix.lua.suffix }}Noexcept \
LuaBridgeTests${{ matrix.lua.suffix }}LuaCNoexcept
- name: Test Lua ${{ matrix.lua.version }} (C++20)
working-directory: ${{runner.workspace}}/build20
run: ctest --parallel $(nproc) -R "LuaBridgeTests${{ matrix.lua.suffix }}" --output-on-failure
- name: Coverage Lua ${{ matrix.lua.version }} (C++20)
working-directory: ${{runner.workspace}}/build20
run: |
lcov -c -d "${{runner.workspace}}/build20" --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 \
--ignore-errors mismatch,unused \
--include "*/LuaBridge/*" --exclude "*/Tests/*" --exclude "*/Distribution/*" --exclude "*/coverage_html/*" \
-o "${{runner.workspace}}/coverage/lua${{ matrix.lua.suffix }}_cxx20.info"
- name: Configure C++23
working-directory: ${{runner.workspace}}/build23
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=23 -DLUABRIDGE_COVERAGE=ON -G Ninja
- name: Build Lua ${{ matrix.lua.version }} (C++23)
working-directory: ${{runner.workspace}}/build23
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTests${{ matrix.lua.suffix }} \
LuaBridgeTests${{ matrix.lua.suffix }}LuaC \
LuaBridgeTests${{ matrix.lua.suffix }}Noexcept \
LuaBridgeTests${{ matrix.lua.suffix }}LuaCNoexcept
- name: Test Lua ${{ matrix.lua.version }} (C++23)
working-directory: ${{runner.workspace}}/build23
run: ctest --parallel $(nproc) -R "LuaBridgeTests${{ matrix.lua.suffix }}" --output-on-failure
- name: Coverage Lua ${{ matrix.lua.version }} (C++23)
working-directory: ${{runner.workspace}}/build23
run: |
lcov -c -d "${{runner.workspace}}/build23" --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 \
--ignore-errors mismatch,unused \
--include "*/LuaBridge/*" --exclude "*/Tests/*" --exclude "*/Distribution/*" --exclude "*/coverage_html/*" \
-o "${{runner.workspace}}/coverage/lua${{ matrix.lua.suffix }}_cxx23.info"
- name: Cache Lcov Files
uses: actions/cache@v5
with:
path: "${{runner.workspace}}/coverage/*.info"
key: lcov-lua${{ matrix.lua.suffix }}-${{runner.os}}-${{github.sha}}
luajit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Install lcov
run: sudo apt-get install -y lcov ninja-build
- name: Create Build Environments
run: |
cmake -E make_directory ${{runner.workspace}}/build17
cmake -E make_directory ${{runner.workspace}}/build20
cmake -E make_directory ${{runner.workspace}}/build23
cmake -E make_directory ${{runner.workspace}}/coverage
- name: Configure C++17
working-directory: ${{runner.workspace}}/build17
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=17 -DLUABRIDGE_COVERAGE=ON -G Ninja
- name: Build LuaJIT (C++17)
working-directory: ${{runner.workspace}}/build17
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTestsLuaJIT \
LuaBridgeTestsLuaJITNoexcept
- name: Test LuaJIT (C++17)
working-directory: ${{runner.workspace}}/build17
run: ctest --parallel $(nproc) -R "LuaBridgeTestsLuaJIT" --output-on-failure
- name: Coverage LuaJIT (C++17)
working-directory: ${{runner.workspace}}/build17
run: |
lcov -c -d "${{runner.workspace}}/build17" --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 \
--ignore-errors mismatch,unused \
--include "*/LuaBridge/*" --exclude "*/Tests/*" --exclude "*/Distribution/*" --exclude "*/coverage_html/*" \
-o "${{runner.workspace}}/coverage/luajit_cxx17.info"
- name: Configure C++20
working-directory: ${{runner.workspace}}/build20
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=20 -DLUABRIDGE_COVERAGE=ON -G Ninja
- name: Build LuaJIT (C++20)
working-directory: ${{runner.workspace}}/build20
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTestsLuaJIT \
LuaBridgeTestsLuaJITNoexcept
- name: Test LuaJIT (C++20)
working-directory: ${{runner.workspace}}/build20
run: ctest --parallel $(nproc) -R "LuaBridgeTestsLuaJIT" --output-on-failure
- name: Coverage LuaJIT (C++20)
working-directory: ${{runner.workspace}}/build20
run: |
lcov -c -d "${{runner.workspace}}/build20" --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 \
--ignore-errors mismatch,unused \
--include "*/LuaBridge/*" --exclude "*/Tests/*" --exclude "*/Distribution/*" --exclude "*/coverage_html/*" \
-o "${{runner.workspace}}/coverage/luajit_cxx20.info"
- name: Configure C++23
working-directory: ${{runner.workspace}}/build23
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=23 -DLUABRIDGE_COVERAGE=ON -G Ninja
- name: Build LuaJIT (C++23)
working-directory: ${{runner.workspace}}/build23
run: |
cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target \
LuaBridgeTestsLuaJIT \
LuaBridgeTestsLuaJITNoexcept
- name: Test LuaJIT (C++23)
working-directory: ${{runner.workspace}}/build23
run: ctest --parallel $(nproc) -R "LuaBridgeTestsLuaJIT" --output-on-failure
- name: Coverage LuaJIT (C++23)
working-directory: ${{runner.workspace}}/build23
run: |
lcov -c -d "${{runner.workspace}}/build23" --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 \
--ignore-errors mismatch,unused \
--include "*/LuaBridge/*" --exclude "*/Tests/*" --exclude "*/Distribution/*" --exclude "*/coverage_html/*" \
-o "${{runner.workspace}}/coverage/luajit_cxx23.info"
- name: Cache Lcov Files
uses: actions/cache@v5
with:
path: "${{runner.workspace}}/coverage/*.info"
key: lcov-luajit-${{runner.os}}-${{github.sha}}
luau:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Install lcov
run: sudo apt-get install -y lcov ninja-build
- name: Create Build Environments
run: |
cmake -E make_directory ${{runner.workspace}}/build17
cmake -E make_directory ${{runner.workspace}}/build20
cmake -E make_directory ${{runner.workspace}}/build23
cmake -E make_directory ${{runner.workspace}}/coverage
- name: Configure C++17
working-directory: ${{runner.workspace}}/build17
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=17 -DLUABRIDGE_COVERAGE=ON -G Ninja
- name: Build Luau (C++17)
working-directory: ${{runner.workspace}}/build17
run: cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target LuaBridgeTestsLuau
- name: Test Luau (C++17)
working-directory: ${{runner.workspace}}/build17
run: ctest --parallel $(nproc) -R "LuaBridgeTestsLuau" --output-on-failure
- name: Coverage Luau (C++17)
working-directory: ${{runner.workspace}}/build17
run: |
lcov -c -d "${{runner.workspace}}/build17" --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 \
--ignore-errors mismatch,unused \
--include "*/LuaBridge/*" --exclude "*/Tests/*" --exclude "*/Distribution/*" --exclude "*/coverage_html/*" \
-o "${{runner.workspace}}/coverage/luau_cxx17.info"
- name: Configure C++20
working-directory: ${{runner.workspace}}/build20
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=20 -DLUABRIDGE_COVERAGE=ON -G Ninja
- name: Build Luau (C++20)
working-directory: ${{runner.workspace}}/build20
run: cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target LuaBridgeTestsLuau
- name: Test Luau (C++20)
working-directory: ${{runner.workspace}}/build20
run: ctest --parallel $(nproc) -R "LuaBridgeTestsLuau" --output-on-failure
- name: Coverage Luau (C++20)
working-directory: ${{runner.workspace}}/build20
run: |
lcov -c -d "${{runner.workspace}}/build20" --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 \
--ignore-errors mismatch,unused \
--include "*/LuaBridge/*" --exclude "*/Tests/*" --exclude "*/Distribution/*" --exclude "*/coverage_html/*" \
-o "${{runner.workspace}}/coverage/luau_cxx20.info"
- name: Configure C++23
working-directory: ${{runner.workspace}}/build23
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=23 -DLUABRIDGE_COVERAGE=ON -G Ninja
- name: Build Luau (C++23)
working-directory: ${{runner.workspace}}/build23
run: cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target LuaBridgeTestsLuau
- name: Test Luau (C++23)
working-directory: ${{runner.workspace}}/build23
run: ctest --parallel $(nproc) -R "LuaBridgeTestsLuau" --output-on-failure
- name: Coverage Luau (C++23)
working-directory: ${{runner.workspace}}/build23
run: |
lcov -c -d "${{runner.workspace}}/build23" --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 \
--ignore-errors mismatch,unused \
--include "*/LuaBridge/*" --exclude "*/Tests/*" --exclude "*/Distribution/*" --exclude "*/coverage_html/*" \
-o "${{runner.workspace}}/coverage/luau_cxx23.info"
- name: Cache Lcov Files
uses: actions/cache@v5
with:
path: "${{runner.workspace}}/coverage/*.info"
key: lcov-luau-${{runner.os}}-${{github.sha}}
ravi:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Install lcov
run: sudo apt-get install -y lcov libreadline-dev ninja-build
- name: Create Build Environments
run: |
cmake -E make_directory ${{runner.workspace}}/build17
cmake -E make_directory ${{runner.workspace}}/build20
cmake -E make_directory ${{runner.workspace}}/build23
cmake -E make_directory ${{runner.workspace}}/coverage
- name: Configure C++17
working-directory: ${{runner.workspace}}/build17
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=17 -DLUABRIDGE_COVERAGE=ON -G Ninja
- name: Build Ravi (C++17)
working-directory: ${{runner.workspace}}/build17
run: cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target LuaBridgeTestsRavi
- name: Test Ravi (C++17)
working-directory: ${{runner.workspace}}/build17/Tests
run: LD_PRELOAD=$(gcc -print-file-name=libasan.so) ./LuaBridgeTestsRavi
- name: Coverage Ravi (C++17)
working-directory: ${{runner.workspace}}/build17
run: |
lcov -c -d "${{runner.workspace}}/build17" --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 \
--ignore-errors mismatch,unused \
--include "*/LuaBridge/*" --exclude "*/Tests/*" --exclude "*/Distribution/*" --exclude "*/coverage_html/*" \
-o "${{runner.workspace}}/coverage/ravi_cxx17.info"
- name: Configure C++20
working-directory: ${{runner.workspace}}/build20
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=20 -DLUABRIDGE_COVERAGE=ON -G Ninja
- name: Build Ravi (C++20)
working-directory: ${{runner.workspace}}/build20
run: cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target LuaBridgeTestsRavi
- name: Test Ravi (C++20)
working-directory: ${{runner.workspace}}/build20/Tests
run: LD_PRELOAD=$(gcc -print-file-name=libasan.so) ./LuaBridgeTestsRavi
- name: Coverage Ravi (C++20)
working-directory: ${{runner.workspace}}/build20
run: |
lcov -c -d "${{runner.workspace}}/build20" --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 \
--ignore-errors mismatch,unused \
--include "*/LuaBridge/*" --exclude "*/Tests/*" --exclude "*/Distribution/*" --exclude "*/coverage_html/*" \
-o "${{runner.workspace}}/coverage/ravi_cxx20.info"
- name: Configure C++23
working-directory: ${{runner.workspace}}/build23
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=23 -DLUABRIDGE_COVERAGE=ON -G Ninja
- name: Build Ravi (C++23)
working-directory: ${{runner.workspace}}/build23
run: cmake --build . --config $BUILD_TYPE --parallel $(nproc) --target LuaBridgeTestsRavi
- name: Test Ravi (C++23)
working-directory: ${{runner.workspace}}/build23/Tests
run: LD_PRELOAD=$(gcc -print-file-name=libasan.so) ./LuaBridgeTestsRavi
- name: Coverage Ravi (C++23)
working-directory: ${{runner.workspace}}/build23
run: |
lcov -c -d "${{runner.workspace}}/build23" --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 \
--ignore-errors mismatch,unused \
--include "*/LuaBridge/*" --exclude "*/Tests/*" --exclude "*/Distribution/*" --exclude "*/coverage_html/*" \
-o "${{runner.workspace}}/coverage/ravi_cxx23.info"
- name: Cache Lcov Files
uses: actions/cache@v5
with:
path: "${{runner.workspace}}/coverage/*.info"
key: lcov-ravi-${{runner.os}}-${{github.sha}}
coveralls:
runs-on: ubuntu-latest
needs: [lua, luajit, luau, ravi]
steps:
- uses: actions/checkout@v6
with:
submodules: true
- name: Install lcov
run: sudo apt-get install -y lcov
- name: Create Coverage Directory
run: cmake -E make_directory ${{runner.workspace}}/coverage
- name: Restore Lcov Files Lua 5.1
uses: actions/cache@v5
with:
path: "${{runner.workspace}}/coverage/*.info"
key: lcov-lua51-${{runner.os}}-${{github.sha}}
- name: Restore Lcov Files Lua 5.2
uses: actions/cache@v5
with:
path: "${{runner.workspace}}/coverage/*.info"
key: lcov-lua52-${{runner.os}}-${{github.sha}}
- name: Restore Lcov Files Lua 5.3
uses: actions/cache@v5
with:
path: "${{runner.workspace}}/coverage/*.info"
key: lcov-lua53-${{runner.os}}-${{github.sha}}
- name: Restore Lcov Files Lua 5.4
uses: actions/cache@v5
with:
path: "${{runner.workspace}}/coverage/*.info"
key: lcov-lua54-${{runner.os}}-${{github.sha}}
- name: Restore Lcov Files Lua 5.5
uses: actions/cache@v5
with:
path: "${{runner.workspace}}/coverage/*.info"
key: lcov-lua55-${{runner.os}}-${{github.sha}}
- name: Restore Lcov Files LuaJIT
uses: actions/cache@v5
with:
path: "${{runner.workspace}}/coverage/*.info"
key: lcov-luajit-${{runner.os}}-${{github.sha}}
- name: Restore Lcov Files Luau
uses: actions/cache@v5
with:
path: "${{runner.workspace}}/coverage/*.info"
key: lcov-luau-${{runner.os}}-${{github.sha}}
- name: Restore Lcov Files Ravi
uses: actions/cache@v5
with:
path: "${{runner.workspace}}/coverage/*.info"
key: lcov-ravi-${{runner.os}}-${{github.sha}}
- name: Merge Lcov Files
working-directory: ${{runner.workspace}}/coverage
run: |
lcov \
-a "lua51_cxx17.info" \
-a "lua51_cxx20.info" \
-a "lua51_cxx23.info" \
-a "lua52_cxx17.info" \
-a "lua52_cxx20.info" \
-a "lua52_cxx23.info" \
-a "lua53_cxx17.info" \
-a "lua53_cxx20.info" \
-a "lua53_cxx23.info" \
-a "lua54_cxx17.info" \
-a "lua54_cxx20.info" \
-a "lua54_cxx23.info" \
-a "lua55_cxx17.info" \
-a "lua55_cxx20.info" \
-a "lua55_cxx23.info" \
-a "luajit_cxx17.info" \
-a "luajit_cxx20.info" \
-a "luajit_cxx23.info" \
-a "luau_cxx17.info" \
-a "luau_cxx20.info" \
-a "luau_cxx23.info" \
-a "ravi_cxx17.info" \
-a "ravi_cxx20.info" \
-a "ravi_cxx23.info" \
-o "merged.info"
- name: Install lcov2xml
run: cargo install lcov2xml
- name: Convert to Cobertura XML
working-directory: ${{runner.workspace}}/coverage
run: lcov2xml merged.info -o cobertura.xml
#- name: Convert to Coverage TXT
# working-directory: ${{runner.workspace}}/build
# run: python3 ${{runner.workspace}}/cobertura.py coverage/cobertura.info coverage/coverage.txt
- name: Upload Cobertura XML
uses: actions/upload-artifact@v4
with:
name: cobertura-coverage
path: ${{runner.workspace}}/coverage/cobertura.xml
#- name: Upload Coverage TXT
# uses: actions/upload-artifact@v4
# with:
# name: text-coverage
# path: ${{runner.workspace}}/build/coverage/coverage.txt
- name: Coveralls
uses: coverallsapp/github-action@master
with:
path-to-lcov: ${{runner.workspace}}/coverage/merged.info
github-token: ${{ secrets.GITHUB_TOKEN }}
+53
View File
@@ -0,0 +1,53 @@
name: Sonar
on:
push:
paths:
- "**/workflows/sonar.yml"
- "**/Source/**"
- "**/Tests/**"
- "**/ThirdParty/**"
- "**/CMakeLists.txt"
- "**/.gitmodules"
- "**/sonar-project.properties"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
env:
BUILD_TYPE: Release
BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory
jobs:
build:
name: Build and Analyze
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: true
- name: Install sonar-scanner and build-wrapper
uses: SonarSource/sonarcloud-github-c-cpp@v2
- name: Create Build Environment
run: cmake -E make_directory ${{runner.workspace}}/build
- name: Configure
working-directory: ${{runner.workspace}}/build
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja
- name: Run build-wrapper
run: |
build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build ${{runner.workspace}}/build --config $BUILD_TYPE
- name: Run sonar-scanner
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: |
sonar-scanner --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}"
+19
View File
@@ -0,0 +1,19 @@
Documentation
*.swp
Makefile
CMakeCache.txt
CMakeFiles/
build*/
Build*/
*.dir/
*.sln
*.vcxproj
*.vcxproj.filters
*.vcxproj.user
.vs/
.vscode/
.idea/
.gitnexus/
.DS_Store
cmake-build-*/
tmpunwind.o
+9
View File
@@ -0,0 +1,9 @@
[submodule "ThirdParty/luau"]
path = ThirdParty/luau
url = https://github.com/Roblox/luau.git
[submodule "ThirdParty/googletest"]
path = ThirdParty/googletest
url = https://github.com/google/googletest.git
[submodule "ThirdParty/ravi"]
path = ThirdParty/ravi
url = https://github.com/dibyendumajumdar/ravi.git
+105
View File
@@ -0,0 +1,105 @@
cmake_minimum_required(VERSION 3.10)
cmake_policy (SET CMP0169 OLD)
include(FetchContent)
set(LUABRIDGE_BENCHMARK_WITH_SOL3 ON CACHE BOOL "Build Sol3 benchmark target")
set(LUABRIDGE_SOL2_GIT_REPOSITORY "https://github.com/ThePhD/sol2.git" CACHE STRING "sol2 repository URL")
set(LUABRIDGE_SOL2_GIT_TAG "v3.3.0" CACHE STRING "sol2 git tag or commit")
set(LUABRIDGE_BENCHMARK_WITH_LUABRIDGE ON CACHE BOOL "Build LuaBridge benchmark target")
set(LUABRIDGE_VANILLA_GIT_REPOSITORY "https://github.com/vinniefalco/LuaBridge.git" CACHE STRING "LuaBridge vanilla repository URL")
set(LUABRIDGE_VANILLA_GIT_TAG "master" CACHE STRING "LuaBridge vanilla git tag or commit")
set(LUABRIDGE_GOOGLE_BENCHMARK_GIT_REPOSITORY "https://github.com/google/benchmark.git" CACHE STRING "Google Benchmark repository URL")
set(LUABRIDGE_GOOGLE_BENCHMARK_GIT_TAG "v1.8.4" CACHE STRING "Google Benchmark git tag or commit")
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL "" FORCE)
FetchContent_Declare(
googlebenchmark
GIT_REPOSITORY ${LUABRIDGE_GOOGLE_BENCHMARK_GIT_REPOSITORY}
GIT_TAG ${LUABRIDGE_GOOGLE_BENCHMARK_GIT_TAG})
FetchContent_MakeAvailable(googlebenchmark)
if (LUABRIDGE_BENCHMARK_WITH_SOL3)
FetchContent_Declare(
sol2
GIT_REPOSITORY ${LUABRIDGE_SOL2_GIT_REPOSITORY}
GIT_TAG ${LUABRIDGE_SOL2_GIT_TAG})
FetchContent_GetProperties(sol2)
if (NOT sol2_POPULATED)
FetchContent_Populate(sol2)
endif()
# Work around a sol2 optional<T&>::emplace bug on recent Apple Clang toolchains.
set(SOL2_OPTIONAL_IMPL "${sol2_SOURCE_DIR}/include/sol/optional_implementation.hpp")
if (EXISTS "${SOL2_OPTIONAL_IMPL}")
file(READ "${SOL2_OPTIONAL_IMPL}" SOL2_OPTIONAL_IMPL_CONTENT)
set(SOL2_OPTIONAL_REFBLOCK_OLD "\t\ttemplate <class... Args>\n\t\tT& emplace(Args&&... args) noexcept {\n\t\t\tstatic_assert(std::is_constructible<T, Args&&...>::value, \"T must be constructible with Args\");\n\n\t\t\t*this = nullopt;\n\t\t\tthis->construct(std::forward<Args>(args)...);\n\t\t}\n")
string(FIND "${SOL2_OPTIONAL_IMPL_CONTENT}" "${SOL2_OPTIONAL_REFBLOCK_OLD}" SOL2_PATCH_NEEDLE_POS)
if (NOT SOL2_PATCH_NEEDLE_POS EQUAL -1)
set(SOL2_OPTIONAL_REFBLOCK_NEW "\t\ttemplate <class... Args>\n\t\tT& emplace(Args&&... args) noexcept {\n\t\t\tstatic_assert(std::is_constructible<T, Args&&...>::value, \"T must be constructible with Args\");\n\n\t\t\t*this = nullopt;\n\t\t\tint emplace_workaround[] = { 0, ((*this = std::forward<Args>(args)), 0)... };\n\t\t\t(void) emplace_workaround;\n\t\t\treturn *m_value;\n\t\t}\n")
string(REPLACE
"${SOL2_OPTIONAL_REFBLOCK_OLD}"
"${SOL2_OPTIONAL_REFBLOCK_NEW}"
SOL2_OPTIONAL_IMPL_CONTENT
"${SOL2_OPTIONAL_IMPL_CONTENT}")
file(WRITE "${SOL2_OPTIONAL_IMPL}" "${SOL2_OPTIONAL_IMPL_CONTENT}")
endif()
endif()
endif()
if (LUABRIDGE_BENCHMARK_WITH_LUABRIDGE)
FetchContent_Declare(
luabridge_vanilla
GIT_REPOSITORY ${LUABRIDGE_VANILLA_GIT_REPOSITORY}
GIT_TAG ${LUABRIDGE_VANILLA_GIT_TAG})
FetchContent_GetProperties(luabridge_vanilla)
if (NOT luabridge_vanilla_POPULATED)
FetchContent_Populate(luabridge_vanilla)
endif()
endif()
function(add_luabridge_benchmark_target target_name source_file)
add_executable(${target_name}
${source_file}
benchmark_common.cpp
../Tests/Lua/LuaLibrary5.4.8.cpp)
target_include_directories(${target_name} PRIVATE
${CMAKE_CURRENT_LIST_DIR}
${CMAKE_CURRENT_LIST_DIR}/..
${CMAKE_CURRENT_LIST_DIR}/../Tests
${CMAKE_CURRENT_LIST_DIR}/../Tests/Lua/Lua.5.4.8/src)
target_compile_definitions(${target_name} PRIVATE
LUABRIDGE_BENCHMARK_LUA54=1
LUABRIDGE_TEST_LUA_VERSION=504)
target_link_libraries(${target_name} PRIVATE
benchmark::benchmark
benchmark::benchmark_main)
endfunction()
add_luabridge_benchmark_target(LuaBridge3Benchmark benchmark_luabridge3.cpp)
target_include_directories(LuaBridge3Benchmark PRIVATE
${CMAKE_CURRENT_LIST_DIR}/../Source)
if (LUABRIDGE_BENCHMARK_WITH_LUABRIDGE)
add_luabridge_benchmark_target(LuaBridgeVanillaBenchmark benchmark_luabridge.cpp)
target_include_directories(LuaBridgeVanillaBenchmark PRIVATE
${luabridge_vanilla_SOURCE_DIR}/Source)
endif()
if (LUABRIDGE_BENCHMARK_WITH_SOL3)
add_luabridge_benchmark_target(Sol3Benchmark benchmark_sol3.cpp)
target_include_directories(Sol3Benchmark PRIVATE
${CMAKE_CURRENT_LIST_DIR}/../Source
${sol2_SOURCE_DIR}/include)
target_compile_definitions(Sol3Benchmark PRIVATE
SOL_ALL_SAFETIES_ON=1
SOL_NO_EXCEPTIONS=1
SOL_LUA_VERSION=504)
endif()
+84
View File
@@ -0,0 +1,84 @@
# Lua Binding Benchmarks
This directory contains Google Benchmark based executables for:
- LuaBridge3 (current workspace)
- LuaBridge vanilla (`https://github.com/vinniefalco/LuaBridge`)
- sol3 (`https://github.com/ThePhD/sol2`)
All benchmark executables are built with the same embedded Lua 5.4.8 runtime source (`Tests/Lua/LuaLibrary5.4.8.cpp`) for fair comparisons.
## Build
From project root:
```bash
cmake -S . -B Build -DCMAKE_BUILD_TYPE=Release -DLUABRIDGE_BENCHMARKS=ON
cmake --build Build --config Release --target LuaBridge3Benchmark LuaBridgeVanillaBenchmark
```
To also build Sol3 benchmark target:
```bash
cmake -S . -B Build -DCMAKE_BUILD_TYPE=Release -DLUABRIDGE_BENCHMARKS=ON -DLUABRIDGE_BENCHMARK_WITH_SOL3=ON
cmake --build Build --config Release --target Sol3Benchmark
```
## Dependency Sources (FetchContent)
Defaults:
- Google Benchmark: `https://github.com/google/benchmark.git` (`v1.8.4`)
- sol3: `https://github.com/ThePhD/sol2.git` (`v3.5.0`)
- LuaBridge vanilla: `https://github.com/vinniefalco/LuaBridge.git` (`master`)
You can override these at configure time:
```bash
cmake -S . -B Build \
-DCMAKE_BUILD_TYPE=Release \
-DLUABRIDGE_BENCHMARKS=ON \
-DLUABRIDGE_SOL2_GIT_REPOSITORY=https://github.com/ThePhD/sol2.git \
-DLUABRIDGE_SOL2_GIT_TAG=v3.5.0 \
-DLUABRIDGE_VANILLA_GIT_REPOSITORY=https://github.com/vinniefalco/LuaBridge.git \
-DLUABRIDGE_VANILLA_GIT_TAG=master \
-DLUABRIDGE_GOOGLE_BENCHMARK_GIT_REPOSITORY=https://github.com/google/benchmark.git \
-DLUABRIDGE_GOOGLE_BENCHMARK_GIT_TAG=v1.8.4
```
## Run Benchmarks
Each executable supports standard Google Benchmark CLI flags.
```bash
./Build/Benchmarks/LuaBridge3Benchmark --benchmark_out=Build/Benchmarks/luabridge3.json --benchmark_out_format=json
./Build/Benchmarks/LuaBridgeVanillaBenchmark --benchmark_out=Build/Benchmarks/luabridge_vanilla.json --benchmark_out_format=json
./Build/Benchmarks/Sol3Benchmark --benchmark_out=Build/Benchmarks/sol3.json --benchmark_out_format=json # if enabled
```
Recommended consistency flags for fair comparison:
```bash
--benchmark_min_time=0.1 --benchmark_repetitions=5
```
## Plot Results
The script `plot_benchmarks.py` merges one or more Google Benchmark JSON files and generates a grouped comparison chart.
```bash
python3 Benchmarks/plot_benchmarks.py \
--input Build/Benchmarks/luabridge3.json Build/Benchmarks/luabridge_vanilla.json Build/Benchmarks/sol3.json \
--output Build/Benchmarks/lua_bindings_comparison.png
```
Outputs:
- PNG chart (grouped bars, lower is better)
- Optional skipped/error report file next to the image (`*_skipped.txt`)
## Notes
- Some vanilla LuaBridge benchmarks are marked as skipped where the feature is unsupported.
- Sol3 target is optional (`LUABRIDGE_BENCHMARK_WITH_SOL3`) because current sol2 headers can fail to compile on some toolchains.
- If you need stricter reproducibility, pin all FetchContent dependencies to commits instead of branches.
+31
View File
@@ -0,0 +1,31 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2026, kunitoki
// Inspired from https://github.com/ThePhD/lua-bindings-shootout by ThePhD
// SPDX-License-Identifier: MIT
#include "benchmark_common.hpp"
#include <string>
namespace lbsbench {
void luaCheckOrThrow(lua_State* L, int status, std::string_view where)
{
if (status == LUA_OK)
return;
const char* message = lua_tostring(L, -1);
std::string error(where);
error += ": ";
error += (message ? message : "unknown lua error");
lua_pop(L, 1);
throw std::runtime_error(error);
}
void luaDoStringOrThrow(lua_State* L, std::string_view code, std::string_view where)
{
const int status = luaL_dostring(L, std::string(code).c_str());
luaCheckOrThrow(L, status, where);
}
} // namespace lbsbench
+232
View File
@@ -0,0 +1,232 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2026, kunitoki
// Inspired from https://github.com/ThePhD/lua-bindings-shootout by ThePhD
// SPDX-License-Identifier: MIT
#pragma once
#include "Lua/LuaLibrary.h"
#include <benchmark/benchmark.h>
#include <cstdint>
#include <memory>
#include <stdexcept>
#include <string>
#include <string_view>
namespace lbsbench {
inline constexpr double kMagicValue = 24.0;
struct Counter
{
int value = 0;
void inc()
{
++value;
}
int add(int x)
{
value += x;
return value;
}
int get() const
{
return value;
}
void set(int v)
{
value = v;
}
static int static_add(int a, int b)
{
return a + b;
}
};
struct Basic
{
double var = 0.0;
double get() const
{
return var;
}
void set(double v)
{
var = v;
}
};
struct BasicLarge
{
std::int64_t var = 0;
std::int64_t var0 = 0;
std::int64_t var1 = 0;
std::int64_t var2 = 0;
std::int64_t var3 = 0;
std::int64_t var4 = 0;
std::int64_t var5 = 0;
std::int64_t var6 = 0;
std::int64_t var7 = 0;
std::int64_t var8 = 0;
std::int64_t var9 = 0;
std::int64_t var10 = 0;
std::int64_t var11 = 0;
std::int64_t var12 = 0;
std::int64_t var13 = 0;
std::int64_t var14 = 0;
std::int64_t var15 = 0;
std::int64_t var16 = 0;
std::int64_t var17 = 0;
std::int64_t var18 = 0;
std::int64_t var19 = 0;
std::int64_t var20 = 0;
std::int64_t var21 = 0;
std::int64_t var22 = 0;
std::int64_t var23 = 0;
std::int64_t var24 = 0;
std::int64_t var25 = 0;
std::int64_t var26 = 0;
std::int64_t var27 = 0;
std::int64_t var28 = 0;
std::int64_t var29 = 0;
std::int64_t var30 = 0;
std::int64_t var31 = 0;
std::int64_t var32 = 0;
std::int64_t var33 = 0;
std::int64_t var34 = 0;
std::int64_t var35 = 0;
std::int64_t var36 = 0;
std::int64_t var37 = 0;
std::int64_t var38 = 0;
std::int64_t var39 = 0;
std::int64_t var40 = 0;
std::int64_t var41 = 0;
std::int64_t var42 = 0;
std::int64_t var43 = 0;
std::int64_t var44 = 0;
std::int64_t var45 = 0;
std::int64_t var46 = 0;
std::int64_t var47 = 0;
std::int64_t var48 = 0;
std::int64_t var49 = 0;
};
struct ComplexBaseA
{
double a = kMagicValue;
double a_func() const
{
return a;
}
};
struct ComplexBaseB
{
double b = kMagicValue;
double b_func() const
{
return b;
}
};
struct ComplexAB : ComplexBaseA, ComplexBaseB
{
double ab = kMagicValue;
double ab_func() const
{
return ab;
}
};
struct StatefulFunction
{
double operator()(double v) const
{
return v;
}
};
struct SharedObject : std::enable_shared_from_this<SharedObject>
{
double value = kMagicValue;
double get() const
{
return value;
}
};
struct Vec3Source
{
float x = 0.f, y = 0.f, z = 0.f;
Vec3Source() = default;
Vec3Source(float x_, float y_, float z_) : x(x_), y(y_), z(z_) {}
};
struct Vec3Target
{
float x = 0.f, y = 0.f, z = 0.f;
Vec3Target() = default;
Vec3Target(float x_, float y_, float z_) : x(x_), y(y_), z(z_) {}
};
struct ColorSource
{
float r = 0.f, g = 0.f, b = 0.f;
ColorSource() = default;
ColorSource(float r_, float g_, float b_) : r(r_), g(g_), b(b_) {}
};
inline float sumVec3(Vec3Target v)
{
return v.x + v.y + v.z;
}
inline float sumVec3Ref(const Vec3Target& v)
{
return v.x + v.y + v.z;
}
inline Basic* basic_return()
{
static Basic value{};
return &value;
}
inline double basic_get_var(Basic* b)
{
return b ? b->var : 0.0;
}
inline std::shared_ptr<SharedObject> shared_object_return()
{
static std::shared_ptr<SharedObject> obj = std::make_shared<SharedObject>();
return obj;
}
inline double shared_object_get_value(std::shared_ptr<SharedObject> obj)
{
return obj ? obj->get() : 0.0;
}
void luaCheckOrThrow(lua_State* L, int status, std::string_view where);
void luaDoStringOrThrow(lua_State* L, std::string_view code, std::string_view where);
inline void setSkipped(benchmark::State& state, std::string_view reason)
{
state.SkipWithError(std::string(reason).c_str());
}
} // namespace lbsbench
+514
View File
@@ -0,0 +1,514 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2026, kunitoki
// Inspired from https://github.com/ThePhD/lua-bindings-shootout by ThePhD
// SPDX-License-Identifier: MIT
#include "benchmark_common.hpp"
#include <LuaBridge/LuaBridge.h>
#include <benchmark/benchmark.h>
namespace {
using namespace lbsbench;
int vanilla_multi_return(lua_State* L)
{
const double i = lua_tonumber(L, 1);
luabridge::push(L, i);
luabridge::push(L, i * 2.0);
return 2;
}
void registerBasicGetterSetter(lua_State* L)
{
luabridge::getGlobalNamespace(L)
.beginClass<Basic>("c")
.addConstructor<void (*)()>()
.addProperty("val", &Basic::get, &Basic::set)
.endClass();
}
void registerCounter(lua_State* L)
{
luabridge::getGlobalNamespace(L)
.beginClass<Counter>("Counter")
.addConstructor<void (*)()>()
.addFunction("get", &Counter::get)
.addStaticFunction("static_add", &Counter::static_add)
.endClass();
}
lua_State* makeLua()
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);
return L;
}
void registerBasic(lua_State* L)
{
luabridge::getGlobalNamespace(L)
.beginClass<Basic>("c")
.addConstructor<void (*)()>()
.addFunction("set", &Basic::set)
.addFunction("get", &Basic::get)
.addData("var", &Basic::var)
.endClass();
}
void table_global_string_get_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luabridge::setGlobal(L, kMagicValue, "value");
double x = 0;
for (auto _ : state)
{
(void) _;
x += static_cast<double>(luabridge::getGlobal(L, "value"));
}
benchmark::DoNotOptimize(x);
}
void table_global_string_set_measure(benchmark::State& state)
{
lua_State* L = makeLua();
double v = 0;
for (auto _ : state)
{
(void) _;
v += kMagicValue;
luabridge::setGlobal(L, v, "value");
}
benchmark::DoNotOptimize(v);
}
void table_get_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luaDoStringOrThrow(L, "warble = { value = 24.0 }", "vanilla table_get setup");
luabridge::LuaRef t = luabridge::getGlobal(L, "warble");
double x = 0;
for (auto _ : state)
{
(void) _;
x += static_cast<double>(t["value"]);
}
benchmark::DoNotOptimize(x);
}
void table_set_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luaDoStringOrThrow(L, "warble = { value = 24.0 }", "vanilla table_set setup");
luabridge::LuaRef t = luabridge::getGlobal(L, "warble");
double v = 0;
for (auto _ : state)
{
(void) _;
v += kMagicValue;
t["value"] = v;
}
benchmark::DoNotOptimize(v);
}
void table_chained_get_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luaDoStringOrThrow(L, "ulahibe = { warble = { value = 24.0 } }", "vanilla chained_get setup");
double x = 0;
for (auto _ : state)
{
(void) _;
luabridge::LuaRef tw = luabridge::getGlobal(L, "ulahibe")["warble"];
x += static_cast<double>(tw["value"]);
}
benchmark::DoNotOptimize(x);
}
void table_chained_set_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luaDoStringOrThrow(L, "ulahibe = { warble = { value = 24.0 } }", "vanilla chained_set setup");
double v = 0;
for (auto _ : state)
{
(void) _;
v += kMagicValue;
luabridge::LuaRef tw = luabridge::getGlobal(L, "ulahibe")["warble"];
tw["value"] = v;
}
benchmark::DoNotOptimize(v);
}
void c_function_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luabridge::getGlobalNamespace(L).addFunction("f", +[](double v) { return v; });
luaDoStringOrThrow(L, "function invoke_f() return f(24.0) end", "vanilla c_function setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "invoke_f");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "vanilla invoke_f");
lua_pop(L, 1);
}
}
void lua_function_in_c_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luaDoStringOrThrow(L, "function f(i) return i end", "vanilla lua_function setup");
luabridge::LuaRef f = luabridge::getGlobal(L, "f");
double x = 0;
for (auto _ : state)
{
(void) _;
x += static_cast<double>(f(kMagicValue));
}
benchmark::DoNotOptimize(x);
}
void c_function_through_lua_in_c_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luabridge::getGlobalNamespace(L).addFunction("f", +[](double v) { return v; });
luabridge::LuaRef f = luabridge::getGlobal(L, "f");
double x = 0;
for (auto _ : state)
{
(void) _;
x += static_cast<double>(f(kMagicValue));
}
benchmark::DoNotOptimize(x);
}
void member_function_call_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerBasic(L);
luaDoStringOrThrow(L, "b = c()", "vanilla member setup");
luaDoStringOrThrow(L, "function call_member() b:set(b:get() + 1.0) end", "vanilla member closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "call_member");
luaCheckOrThrow(L, lua_pcall(L, 0, 0, 0), "vanilla call_member");
}
}
void userdata_variable_access_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerBasic(L);
luaDoStringOrThrow(L, "b = c()", "vanilla userdata setup");
luaDoStringOrThrow(L, "function access_var() b.var = b.var + 1.0 return b.var end", "vanilla userdata closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "access_var");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "vanilla access_var");
lua_pop(L, 1);
}
}
void userdata_variable_access_large_measure(benchmark::State& state)
{
setSkipped(state, "unsupported in LuaBridge vanilla benchmark parity mode");
}
void userdata_variable_access_last_measure(benchmark::State& state)
{
setSkipped(state, "unsupported in LuaBridge vanilla benchmark parity mode");
}
void stateful_function_object_measure(benchmark::State& state)
{
setSkipped(state, "unsupported in LuaBridge vanilla benchmark parity mode");
}
void multi_return_lua_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luabridge::getGlobalNamespace(L).addCFunction("f", vanilla_multi_return);
luaDoStringOrThrow(L, "function invoke_multi() local a,b=f(24.0) return a+b end", "vanilla multi_return_lua setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "invoke_multi");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "vanilla invoke_multi");
lua_pop(L, 1);
}
}
void multi_return_measure(benchmark::State& state)
{
setSkipped(state, "unsupported conceptual multi-return conversion in LuaBridge vanilla");
}
void derived_base_measure(benchmark::State& state)
{
setSkipped(state, "unsupported for multi inheritance in LuaBridge vanilla");
}
void return_userdata_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerBasic(L);
luabridge::getGlobalNamespace(L)
.addFunction("f", &basic_return)
.addFunction("h", &basic_get_var);
luaDoStringOrThrow(L, "function invoke_userdata() return h(f()) end", "vanilla return_userdata setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "invoke_userdata");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "vanilla invoke_userdata");
lua_pop(L, 1);
}
}
void optional_success_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luaDoStringOrThrow(L, "warble = { value = 24.0 }", "vanilla optional_success setup");
double x = 0;
for (auto _ : state)
{
(void) _;
luabridge::LuaRef tt = luabridge::getGlobal(L, "warble");
if (tt.isTable())
{
luabridge::LuaRef tv = tt["value"];
x += tv.isNumber() ? static_cast<double>(tv) : 1.0;
}
else
{
x += 1.0;
}
}
benchmark::DoNotOptimize(x);
}
void optional_half_failure_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luaDoStringOrThrow(L, "warble = { value = 'x' }", "vanilla optional_half_failure setup");
double x = 0;
for (auto _ : state)
{
(void) _;
luabridge::LuaRef tt = luabridge::getGlobal(L, "warble");
if (tt.isTable())
{
luabridge::LuaRef tv = tt["value"];
x += tv.isNumber() ? static_cast<double>(tv) : 1.0;
}
else
{
x += 1.0;
}
}
benchmark::DoNotOptimize(x);
}
void optional_failure_measure(benchmark::State& state)
{
lua_State* L = makeLua();
double x = 0;
for (auto _ : state)
{
(void) _;
luabridge::LuaRef tt = luabridge::getGlobal(L, "warble");
if (tt.isTable())
{
luabridge::LuaRef tv = tt["value"];
x += tv.isNumber() ? static_cast<double>(tv) : 1.0;
}
else
{
x += 1.0;
}
}
benchmark::DoNotOptimize(x);
}
void userdata_variable_write_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerBasic(L);
luaDoStringOrThrow(L, "b = c()", "vanilla userdata_write setup");
luaDoStringOrThrow(L, "function write_var() b.var = 24.0 end", "vanilla userdata_write closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "write_var");
luaCheckOrThrow(L, lua_pcall(L, 0, 0, 0), "vanilla write_var");
}
}
void userdata_property_getter_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerBasicGetterSetter(L);
luaDoStringOrThrow(L, "b = c()", "vanilla property_getter setup");
luaDoStringOrThrow(L, "function read_getter() return b.val end", "vanilla property_getter closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "read_getter");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "vanilla read_getter");
lua_pop(L, 1);
}
}
void userdata_property_setter_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerBasicGetterSetter(L);
luaDoStringOrThrow(L, "b = c()", "vanilla property_setter setup");
luaDoStringOrThrow(L, "function write_setter() b.val = 24.0 end", "vanilla property_setter closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "write_setter");
luaCheckOrThrow(L, lua_pcall(L, 0, 0, 0), "vanilla write_setter");
}
}
void lambda_capture_measure(benchmark::State& state)
{
lua_State* L = makeLua();
double extra = kMagicValue;
luabridge::getGlobalNamespace(L).addFunction("f", std::function<double(double)>([extra](double v) { return v + extra; }));
luaDoStringOrThrow(L, "function invoke_lambda() return f(24.0) end", "vanilla lambda_capture setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "invoke_lambda");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "vanilla invoke_lambda");
lua_pop(L, 1);
}
}
void shared_ptr_return_measure(benchmark::State& state)
{
setSkipped(state, "unsupported shared_ptr container in LuaBridge vanilla");
}
void shared_ptr_pass_measure(benchmark::State& state)
{
setSkipped(state, "unsupported shared_ptr container in LuaBridge vanilla");
}
void static_member_function_call_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerCounter(L);
luaDoStringOrThrow(L, "function invoke_static() return Counter.static_add(10, 32) end", "vanilla static_member_function setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "invoke_static");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "vanilla invoke_static");
lua_pop(L, 1);
}
}
void derived_method_call_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luabridge::getGlobalNamespace(L)
.beginClass<ComplexBaseA>("ComplexBaseA")
.addFunction("a_func", &ComplexBaseA::a_func)
.addProperty("a", &ComplexBaseA::a)
.endClass()
.deriveClass<ComplexAB, ComplexBaseA>("ComplexAB")
.addConstructor<void (*)()>()
.addFunction("ab_func", &ComplexAB::ab_func)
.addProperty("ab", &ComplexAB::ab)
.endClass();
luaDoStringOrThrow(L, "obj = ComplexAB()", "vanilla derived_method setup");
luaDoStringOrThrow(L, "function call_derived() return obj:ab_func() end", "vanilla derived_method closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "call_derived");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "vanilla call_derived");
lua_pop(L, 1);
}
}
void implicit_inheritance_measure(benchmark::State& state)
{
setSkipped(state, "unsupported for multi inheritance in LuaBridge vanilla");
}
} // namespace
BENCHMARK(table_global_string_get_measure)->Name("table_global_string_get_measure");
BENCHMARK(table_global_string_set_measure)->Name("table_global_string_set_measure");
BENCHMARK(table_get_measure)->Name("table_get_measure");
BENCHMARK(table_set_measure)->Name("table_set_measure");
BENCHMARK(table_chained_get_measure)->Name("table_chained_get_measure");
BENCHMARK(table_chained_set_measure)->Name("table_chained_set_measure");
BENCHMARK(c_function_measure)->Name("c_function_measure");
BENCHMARK(c_function_through_lua_in_c_measure)->Name("c_function_through_lua_in_c_measure");
BENCHMARK(lua_function_in_c_measure)->Name("lua_function_in_c_measure");
BENCHMARK(member_function_call_measure)->Name("member_function_call_measure");
BENCHMARK(userdata_variable_access_measure)->Name("userdata_variable_access_measure");
BENCHMARK(userdata_variable_access_large_measure)->Name("userdata_variable_access_large_measure");
BENCHMARK(userdata_variable_access_last_measure)->Name("userdata_variable_access_last_measure");
BENCHMARK(multi_return_lua_measure)->Name("multi_return_lua_measure");
BENCHMARK(multi_return_measure)->Name("multi_return_measure");
BENCHMARK(stateful_function_object_measure)->Name("stateful_function_object_measure");
BENCHMARK(derived_base_measure)->Name("derived_base_measure");
BENCHMARK(return_userdata_measure)->Name("return_userdata_measure");
BENCHMARK(optional_success_measure)->Name("optional_success_measure");
BENCHMARK(optional_half_failure_measure)->Name("optional_half_failure_measure");
BENCHMARK(optional_failure_measure)->Name("optional_failure_measure");
BENCHMARK(implicit_inheritance_measure)->Name("implicit_inheritance_measure");
BENCHMARK(userdata_variable_write_measure)->Name("userdata_variable_write_measure");
BENCHMARK(userdata_property_getter_measure)->Name("userdata_property_getter_measure");
BENCHMARK(userdata_property_setter_measure)->Name("userdata_property_setter_measure");
BENCHMARK(lambda_capture_measure)->Name("lambda_capture_measure");
BENCHMARK(shared_ptr_return_measure)->Name("shared_ptr_return_measure");
BENCHMARK(shared_ptr_pass_measure)->Name("shared_ptr_pass_measure");
BENCHMARK(static_member_function_call_measure)->Name("static_member_function_call_measure");
BENCHMARK(derived_method_call_measure)->Name("derived_method_call_measure");
+805
View File
@@ -0,0 +1,805 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2026, kunitoki
// Inspired from https://github.com/ThePhD/lua-bindings-shootout by ThePhD
// SPDX-License-Identifier: MIT
#include "benchmark_common.hpp"
#include "LuaBridge/LuaBridge.h"
#include <benchmark/benchmark.h>
#include <cmath>
#include <tuple>
namespace luabridge {
template <>
struct StackConversion<lbsbench::Vec3Target>
{
static constexpr bool enabled = true;
};
template <>
struct StackConverter<lbsbench::Vec3Target, lbsbench::Vec3Source>
{
static lbsbench::Vec3Target convert(const lbsbench::Vec3Source& s)
{
return {s.x, s.y, s.z};
}
};
template <>
struct StackConverter<lbsbench::Vec3Target, lbsbench::ColorSource>
{
static lbsbench::Vec3Target convert(const lbsbench::ColorSource& s)
{
return {s.r, s.g, s.b};
}
};
} // namespace luabridge
namespace {
using namespace lbsbench;
std::tuple<double, double> lb3_multi_return(double value)
{
return { value, value * 2.0 };
}
void registerBasic(lua_State* L)
{
luabridge::getGlobalNamespace(L)
.beginClass<Basic>("c")
.addConstructor<void (*)()>()
.addFunction("set", &Basic::set)
.addFunction("get", &Basic::get)
.addProperty("var", &Basic::var)
.endClass();
}
void registerBasicLarge(lua_State* L)
{
luabridge::getGlobalNamespace(L)
.beginClass<BasicLarge>("cl")
.addConstructor<void (*)()>()
.addProperty("var", &BasicLarge::var)
.addProperty("var0", &BasicLarge::var0)
.addProperty("var1", &BasicLarge::var1)
.addProperty("var2", &BasicLarge::var2)
.addProperty("var3", &BasicLarge::var3)
.addProperty("var4", &BasicLarge::var4)
.addProperty("var5", &BasicLarge::var5)
.addProperty("var6", &BasicLarge::var6)
.addProperty("var7", &BasicLarge::var7)
.addProperty("var8", &BasicLarge::var8)
.addProperty("var9", &BasicLarge::var9)
.addProperty("var10", &BasicLarge::var10)
.addProperty("var11", &BasicLarge::var11)
.addProperty("var12", &BasicLarge::var12)
.addProperty("var13", &BasicLarge::var13)
.addProperty("var14", &BasicLarge::var14)
.addProperty("var15", &BasicLarge::var15)
.addProperty("var16", &BasicLarge::var16)
.addProperty("var17", &BasicLarge::var17)
.addProperty("var18", &BasicLarge::var18)
.addProperty("var19", &BasicLarge::var19)
.addProperty("var20", &BasicLarge::var20)
.addProperty("var21", &BasicLarge::var21)
.addProperty("var22", &BasicLarge::var22)
.addProperty("var23", &BasicLarge::var23)
.addProperty("var24", &BasicLarge::var24)
.addProperty("var25", &BasicLarge::var25)
.addProperty("var26", &BasicLarge::var26)
.addProperty("var27", &BasicLarge::var27)
.addProperty("var28", &BasicLarge::var28)
.addProperty("var29", &BasicLarge::var29)
.addProperty("var30", &BasicLarge::var30)
.addProperty("var31", &BasicLarge::var31)
.addProperty("var32", &BasicLarge::var32)
.addProperty("var33", &BasicLarge::var33)
.addProperty("var34", &BasicLarge::var34)
.addProperty("var35", &BasicLarge::var35)
.addProperty("var36", &BasicLarge::var36)
.addProperty("var37", &BasicLarge::var37)
.addProperty("var38", &BasicLarge::var38)
.addProperty("var39", &BasicLarge::var39)
.addProperty("var40", &BasicLarge::var40)
.addProperty("var41", &BasicLarge::var41)
.addProperty("var42", &BasicLarge::var42)
.addProperty("var43", &BasicLarge::var43)
.addProperty("var44", &BasicLarge::var44)
.addProperty("var45", &BasicLarge::var45)
.addProperty("var46", &BasicLarge::var46)
.addProperty("var47", &BasicLarge::var47)
.addProperty("var48", &BasicLarge::var48)
.addProperty("var49", &BasicLarge::var49)
.endClass();
}
void registerBasicRW(lua_State* L)
{
luabridge::getGlobalNamespace(L)
.beginClass<Basic>("c")
.addConstructor<void (*)()>()
.addPropertyReadWrite("var", &Basic::var)
.endClass();
}
void registerBasicGetterSetter(lua_State* L)
{
luabridge::getGlobalNamespace(L)
.beginClass<Basic>("c")
.addConstructor<void (*)()>()
.addProperty("val", &Basic::get, &Basic::set)
.endClass();
}
void registerCounter(lua_State* L)
{
luabridge::getGlobalNamespace(L)
.beginClass<Counter>("Counter")
.addConstructor<void (*)()>()
.addFunction("get", &Counter::get)
.addStaticFunction("static_add", &Counter::static_add)
.endClass();
}
void registerSharedObject(lua_State* L)
{
luabridge::getGlobalNamespace(L)
.beginClass<SharedObject>("SharedObject")
.addConstructorFrom<std::shared_ptr<SharedObject>, void(*)()>()
.addFunction("get", &SharedObject::get)
.endClass()
.addFunction("get_shared", &shared_object_return)
.addFunction("use_shared", &shared_object_get_value);
}
lua_State* makeLua()
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);
luabridge::registerMainThread(L);
#if LUABRIDGE_HAS_EXCEPTIONS
luabridge::enableExceptions(L);
#endif
return L;
}
void table_global_string_get_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luabridge::setGlobal(L, kMagicValue, "value");
double x = 0;
for (auto _ : state)
{
(void) _;
x += static_cast<double>(luabridge::getGlobal(L, "value"));
}
benchmark::DoNotOptimize(x);
}
void table_global_string_set_measure(benchmark::State& state)
{
lua_State* L = makeLua();
double v = 0;
for (auto _ : state)
{
(void) _;
v += kMagicValue;
luabridge::setGlobal(L, v, "value");
}
benchmark::DoNotOptimize(v);
}
void table_get_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luaDoStringOrThrow(L, "warble = { value = 24.0 }", "table_get setup");
luabridge::LuaRef t = luabridge::getGlobal(L, "warble");
double x = 0;
for (auto _ : state)
{
(void) _;
x += static_cast<double>(t["value"]);
}
benchmark::DoNotOptimize(x);
}
void table_set_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luaDoStringOrThrow(L, "warble = { value = 24.0 }", "table_set setup");
luabridge::LuaRef t = luabridge::getGlobal(L, "warble");
double v = 0;
for (auto _ : state)
{
(void) _;
v += kMagicValue;
t["value"] = v;
}
benchmark::DoNotOptimize(v);
}
void table_chained_get_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luaDoStringOrThrow(L, "ulahibe = { warble = { value = 24.0 } }", "table_chained_get setup");
double x = 0;
for (auto _ : state)
{
(void) _;
luabridge::LuaRef tw = luabridge::getGlobal(L, "ulahibe")["warble"];
x += static_cast<double>(tw["value"]);
}
benchmark::DoNotOptimize(x);
}
void table_chained_set_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luaDoStringOrThrow(L, "ulahibe = { warble = { value = 24.0 } }", "table_chained_set setup");
double v = 0;
for (auto _ : state)
{
(void) _;
v += kMagicValue;
luabridge::LuaRef tw = luabridge::getGlobal(L, "ulahibe")["warble"];
tw["value"] = v;
}
benchmark::DoNotOptimize(v);
}
void c_function_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luabridge::getGlobalNamespace(L).addFunction("f", +[](double v) { return v; });
luaDoStringOrThrow(L, "function invoke_f() return f(24.0) end", "c_function setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "invoke_f");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_f");
lua_pop(L, 1);
}
}
void lua_function_in_c_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luaDoStringOrThrow(L, "function f(i) return i end", "lua_function setup");
luabridge::LuaRef f = luabridge::getGlobal(L, "f");
double x = 0;
for (auto _ : state)
{
(void) _;
x += f.call<double>(kMagicValue).valueOr(0.0);
}
benchmark::DoNotOptimize(x);
}
void c_function_through_lua_in_c_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luabridge::getGlobalNamespace(L).addFunction("f", +[](double v) { return v; });
luabridge::LuaRef f = luabridge::getGlobal(L, "f");
double x = 0;
for (auto _ : state)
{
(void) _;
x += f.call<double>(kMagicValue).valueOr(0.0);
}
benchmark::DoNotOptimize(x);
}
void member_function_call_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerBasic(L);
luaDoStringOrThrow(L, "b = c()", "member_function setup");
luaDoStringOrThrow(L, "function call_member() b:set(b:get() + 1.0) end", "member_function closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "call_member");
luaCheckOrThrow(L, lua_pcall(L, 0, 0, 0), "call_member");
}
}
void userdata_variable_access_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerBasic(L);
luaDoStringOrThrow(L, "b = c()", "userdata_variable_access setup");
luaDoStringOrThrow(L, "function access_var() return b.var end", "userdata_variable_access closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "access_var");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "access_var");
lua_pop(L, 1);
}
}
void userdata_variable_access_large_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerBasicLarge(L);
luaDoStringOrThrow(L, "b = cl()", "userdata_variable_access_large setup");
luaDoStringOrThrow(L, "function access_var_large() return b.var0 end", "userdata_variable_access_large closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "access_var_large");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "access_var_large");
lua_pop(L, 1);
}
}
void userdata_variable_access_last_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerBasicLarge(L);
luaDoStringOrThrow(L, "b = cl()", "userdata_variable_access_last setup");
luaDoStringOrThrow(L, "function access_var_last() return b.var49 end", "userdata_variable_access_last closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "access_var_last");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "access_var_last");
lua_pop(L, 1);
}
}
void stateful_function_object_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luabridge::getGlobalNamespace(L).addFunction("f", StatefulFunction{});
luabridge::LuaRef f = luabridge::getGlobal(L, "f");
double x = 0;
for (auto _ : state)
{
(void) _;
x += f.call<double>(kMagicValue).valueOr(0.0);
}
benchmark::DoNotOptimize(x);
}
void multi_return_lua_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luabridge::getGlobalNamespace(L).addFunction("f", &lb3_multi_return);
luaDoStringOrThrow(L, "function invoke_multi() local a,b=f(24.0) return a+b end", "multi_return_lua setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "invoke_multi");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_multi");
lua_pop(L, 1);
}
}
void multi_return_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luabridge::getGlobalNamespace(L).addFunction("f", &lb3_multi_return);
luabridge::LuaRef f = luabridge::getGlobal(L, "f");
double x = 0;
for (auto _ : state)
{
(void) _;
auto result = f.call<std::tuple<double, double>>(kMagicValue).valueOr(std::make_tuple(0.0, 0.0));
x += std::get<0>(result);
x += std::get<1>(result);
}
benchmark::DoNotOptimize(x);
}
void derived_base_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luabridge::getGlobalNamespace(L)
.beginClass<ComplexBaseA>("ComplexBaseA")
.addFunction("a_func", &ComplexBaseA::a_func)
.addProperty("a", &ComplexBaseA::a)
.endClass()
.beginClass<ComplexBaseB>("ComplexBaseB")
.addFunction("b_func", &ComplexBaseB::b_func)
.addProperty("b", &ComplexBaseB::b)
.endClass()
.deriveClass<ComplexAB, ComplexBaseA, ComplexBaseB>("ComplexAB")
.addConstructor<void (*)()>()
.addFunction("ab_func", &ComplexAB::ab_func)
.addProperty("ab", &ComplexAB::ab)
.endClass();
luaDoStringOrThrow(L, "obj = ComplexAB()", "base_derived setup");
luaDoStringOrThrow(L, "function call_base() return obj:a_func() + obj:b_func() end", "base_derived closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "call_base");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "call_base");
lua_pop(L, 1);
}
}
void optional_success_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luaDoStringOrThrow(L, "warble = { value = 24.0 }", "optional_success setup");
double x = 0;
for (auto _ : state)
{
(void) _;
auto result = luabridge::tryGetGlobalField<double>(L, "warble", "value");
x += result ? *result : 1.0;
}
benchmark::DoNotOptimize(x);
}
void optional_half_failure_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luaDoStringOrThrow(L, "warble = { value = 'x' }", "optional_half_failure setup");
double x = 0;
for (auto _ : state)
{
(void) _;
auto result = luabridge::tryGetGlobalField<double>(L, "warble", "value");
x += result ? *result : 1.0;
}
benchmark::DoNotOptimize(x);
}
void optional_failure_measure(benchmark::State& state)
{
lua_State* L = makeLua();
double x = 0;
for (auto _ : state)
{
(void) _;
auto result = luabridge::tryGetGlobalField<double>(L, "warble", "value");
x += result ? *result : 1.0;
}
benchmark::DoNotOptimize(x);
}
void return_userdata_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerBasic(L);
luabridge::getGlobalNamespace(L)
.addFunction("f", &basic_return)
.addFunction("h", &basic_get_var);
luaDoStringOrThrow(L, "function invoke_userdata() return h(f()) end", "return_userdata setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "invoke_userdata");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_userdata");
lua_pop(L, 1);
}
}
void userdata_variable_write_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerBasicRW(L);
luaDoStringOrThrow(L, "b = c()", "userdata_variable_write setup");
luaDoStringOrThrow(L, "function write_var() b.var = 24.0 end", "userdata_variable_write closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "write_var");
luaCheckOrThrow(L, lua_pcall(L, 0, 0, 0), "write_var");
}
}
void userdata_property_getter_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerBasicGetterSetter(L);
luaDoStringOrThrow(L, "b = c()", "userdata_property_getter setup");
luaDoStringOrThrow(L, "function read_getter() return b.val end", "userdata_property_getter closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "read_getter");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "read_getter");
lua_pop(L, 1);
}
}
void userdata_property_setter_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerBasicGetterSetter(L);
luaDoStringOrThrow(L, "b = c()", "userdata_property_setter setup");
luaDoStringOrThrow(L, "function write_setter() b.val = 24.0 end", "userdata_property_setter closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "write_setter");
luaCheckOrThrow(L, lua_pcall(L, 0, 0, 0), "write_setter");
}
}
void lambda_capture_measure(benchmark::State& state)
{
lua_State* L = makeLua();
double extra = kMagicValue;
luabridge::getGlobalNamespace(L).addFunction("f", [extra](double v) { return v + extra; });
luaDoStringOrThrow(L, "function invoke_lambda() return f(24.0) end", "lambda_capture setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "invoke_lambda");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_lambda");
lua_pop(L, 1);
}
}
void shared_ptr_return_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerSharedObject(L);
luaDoStringOrThrow(L, "function invoke_shared() return get_shared():get() end", "shared_ptr_return setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "invoke_shared");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_shared");
lua_pop(L, 1);
}
}
void shared_ptr_pass_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerSharedObject(L);
luaDoStringOrThrow(L, "obj = SharedObject()", "shared_ptr_pass setup");
luaDoStringOrThrow(L, "function invoke_pass_shared() return use_shared(obj) end", "shared_ptr_pass closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "invoke_pass_shared");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_pass_shared");
lua_pop(L, 1);
}
}
void static_member_function_call_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerCounter(L);
luaDoStringOrThrow(L, "function invoke_static() return Counter.static_add(10, 32) end", "static_member_function setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "invoke_static");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_static");
lua_pop(L, 1);
}
}
void derived_method_call_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luabridge::getGlobalNamespace(L)
.beginClass<ComplexBaseA>("ComplexBaseA")
.addFunction("a_func", &ComplexBaseA::a_func)
.addProperty("a", &ComplexBaseA::a)
.endClass()
.beginClass<ComplexBaseB>("ComplexBaseB")
.addFunction("b_func", &ComplexBaseB::b_func)
.addProperty("b", &ComplexBaseB::b)
.endClass()
.deriveClass<ComplexAB, ComplexBaseA, ComplexBaseB>("ComplexAB")
.addConstructor<void (*)()>()
.addFunction("ab_func", &ComplexAB::ab_func)
.addProperty("ab", &ComplexAB::ab)
.endClass();
luaDoStringOrThrow(L, "obj = ComplexAB()", "derived_method setup");
luaDoStringOrThrow(L, "function call_derived() return obj:ab_func() end", "derived_method closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "call_derived");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "call_derived");
lua_pop(L, 1);
}
}
void implicit_inheritance_measure(benchmark::State& state)
{
lua_State* L = makeLua();
luabridge::getGlobalNamespace(L)
.beginClass<ComplexBaseA>("ComplexBaseA")
.addFunction("a_func", &ComplexBaseA::a_func)
.endClass()
.deriveClass<ComplexAB, ComplexBaseA>("ComplexAB")
.addConstructor<void (*)()>()
.addFunction("ab_func", &ComplexAB::ab_func)
.endClass()
.addFunction("call_a", +[](ComplexBaseA* obj) -> double { return obj->a_func(); });
luaDoStringOrThrow(L, "obj = ComplexAB()", "implicit_inheritance setup");
luaDoStringOrThrow(L, "function test_implicit() return call_a(obj) end", "implicit_inheritance closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "test_implicit");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "test_implicit");
lua_pop(L, 1);
}
}
void registerConverter(lua_State* L)
{
luabridge::getGlobalNamespace(L)
.beginClass<Vec3Source>("Vec3Source")
.addConstructor<void(float, float, float)>()
.addConverter<Vec3Target>()
.endClass()
.beginClass<ColorSource>("ColorSource")
.addConstructor<void(float, float, float)>()
.addConverter<Vec3Target>()
.endClass()
.beginClass<Vec3Target>("Vec3Target")
.addConstructor<void(float, float, float)>()
.endClass()
.addFunction("sumVec3", &sumVec3)
.addFunction("sumVec3Ref", &sumVec3Ref);
}
void converter_exact_type_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerConverter(L);
luaDoStringOrThrow(L, "obj = Vec3Target(1, 2, 3)", "converter_exact_type setup");
luaDoStringOrThrow(L, "function invoke_exact() return sumVec3(obj) end", "converter_exact_type closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "invoke_exact");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_exact");
lua_pop(L, 1);
}
}
void converter_phase3_value_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerConverter(L);
luaDoStringOrThrow(L, "obj = Vec3Source(1, 2, 3)", "converter_phase3_value setup");
luaDoStringOrThrow(L, "function invoke_conv_value() return sumVec3(obj) end", "converter_phase3_value closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "invoke_conv_value");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_conv_value");
lua_pop(L, 1);
}
}
void converter_phase3_ref_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerConverter(L);
luaDoStringOrThrow(L, "obj = Vec3Source(1, 2, 3)", "converter_phase3_ref setup");
luaDoStringOrThrow(L, "function invoke_conv_ref() return sumVec3Ref(obj) end", "converter_phase3_ref closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "invoke_conv_ref");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_conv_ref");
lua_pop(L, 1);
}
}
void converter_multi_registered_measure(benchmark::State& state)
{
lua_State* L = makeLua();
registerConverter(L);
luaDoStringOrThrow(L, "obj = ColorSource(0.5, 1, 0)", "converter_multi_registered setup");
luaDoStringOrThrow(L, "function invoke_conv_multi() return sumVec3(obj) end", "converter_multi_registered closure setup");
for (auto _ : state)
{
(void) _;
lua_getglobal(L, "invoke_conv_multi");
luaCheckOrThrow(L, lua_pcall(L, 0, 1, 0), "invoke_conv_multi");
lua_pop(L, 1);
}
}
} // namespace
BENCHMARK(table_global_string_get_measure)->Name("table_global_string_get_measure");
BENCHMARK(table_global_string_set_measure)->Name("table_global_string_set_measure");
BENCHMARK(table_get_measure)->Name("table_get_measure");
BENCHMARK(table_set_measure)->Name("table_set_measure");
BENCHMARK(table_chained_get_measure)->Name("table_chained_get_measure");
BENCHMARK(table_chained_set_measure)->Name("table_chained_set_measure");
BENCHMARK(c_function_measure)->Name("c_function_measure");
BENCHMARK(c_function_through_lua_in_c_measure)->Name("c_function_through_lua_in_c_measure");
BENCHMARK(lua_function_in_c_measure)->Name("lua_function_in_c_measure");
BENCHMARK(member_function_call_measure)->Name("member_function_call_measure");
BENCHMARK(userdata_variable_access_measure)->Name("userdata_variable_access_measure");
BENCHMARK(userdata_variable_access_large_measure)->Name("userdata_variable_access_large_measure");
BENCHMARK(userdata_variable_access_last_measure)->Name("userdata_variable_access_last_measure");
BENCHMARK(multi_return_lua_measure)->Name("multi_return_lua_measure");
BENCHMARK(multi_return_measure)->Name("multi_return_measure");
BENCHMARK(stateful_function_object_measure)->Name("stateful_function_object_measure");
BENCHMARK(derived_base_measure)->Name("derived_base_measure");
BENCHMARK(return_userdata_measure)->Name("return_userdata_measure");
BENCHMARK(optional_success_measure)->Name("optional_success_measure");
BENCHMARK(optional_half_failure_measure)->Name("optional_half_failure_measure");
BENCHMARK(optional_failure_measure)->Name("optional_failure_measure");
BENCHMARK(implicit_inheritance_measure)->Name("implicit_inheritance_measure");
BENCHMARK(userdata_variable_write_measure)->Name("userdata_variable_write_measure");
BENCHMARK(userdata_property_getter_measure)->Name("userdata_property_getter_measure");
BENCHMARK(userdata_property_setter_measure)->Name("userdata_property_setter_measure");
BENCHMARK(lambda_capture_measure)->Name("lambda_capture_measure");
BENCHMARK(shared_ptr_return_measure)->Name("shared_ptr_return_measure");
BENCHMARK(shared_ptr_pass_measure)->Name("shared_ptr_pass_measure");
BENCHMARK(static_member_function_call_measure)->Name("static_member_function_call_measure");
BENCHMARK(derived_method_call_measure)->Name("derived_method_call_measure");
BENCHMARK(converter_exact_type_measure)->Name("converter_exact_type_measure");
BENCHMARK(converter_phase3_value_measure)->Name("converter_phase3_value_measure");
BENCHMARK(converter_phase3_ref_measure)->Name("converter_phase3_ref_measure");
BENCHMARK(converter_multi_registered_measure)->Name("converter_multi_registered_measure");
+618
View File
@@ -0,0 +1,618 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2026, kunitoki
// Inspired from https://github.com/ThePhD/lua-bindings-shootout by ThePhD
// SPDX-License-Identifier: MIT
#include "benchmark_common.hpp"
#include <sol/sol.hpp>
#include <benchmark/benchmark.h>
#include <tuple>
namespace {
using namespace lbsbench;
std::tuple<double, double> sol3_multi_return(double value)
{
return { value, value * 2.0 };
}
void registerBasic(sol::state& lua)
{
lua.new_usertype<Basic>("c",
sol::constructors<Basic()>(),
"set", &Basic::set,
"get", &Basic::get,
"var", &Basic::var);
}
void registerBasicLarge(sol::state& lua)
{
lua.new_usertype<BasicLarge>("cl",
sol::constructors<BasicLarge()>(),
"var", &BasicLarge::var,
"var0", &BasicLarge::var0,
"var1", &BasicLarge::var1,
"var2", &BasicLarge::var2,
"var3", &BasicLarge::var3,
"var4", &BasicLarge::var4,
"var5", &BasicLarge::var5,
"var6", &BasicLarge::var6,
"var7", &BasicLarge::var7,
"var8", &BasicLarge::var8,
"var9", &BasicLarge::var9,
"var10", &BasicLarge::var10,
"var11", &BasicLarge::var11,
"var12", &BasicLarge::var12,
"var13", &BasicLarge::var13,
"var14", &BasicLarge::var14,
"var15", &BasicLarge::var15,
"var16", &BasicLarge::var16,
"var17", &BasicLarge::var17,
"var18", &BasicLarge::var18,
"var19", &BasicLarge::var19,
"var20", &BasicLarge::var20,
"var21", &BasicLarge::var21,
"var22", &BasicLarge::var22,
"var23", &BasicLarge::var23,
"var24", &BasicLarge::var24,
"var25", &BasicLarge::var25,
"var26", &BasicLarge::var26,
"var27", &BasicLarge::var27,
"var28", &BasicLarge::var28,
"var29", &BasicLarge::var29,
"var30", &BasicLarge::var30,
"var31", &BasicLarge::var31,
"var32", &BasicLarge::var32,
"var33", &BasicLarge::var33,
"var34", &BasicLarge::var34,
"var35", &BasicLarge::var35,
"var36", &BasicLarge::var36,
"var37", &BasicLarge::var37,
"var38", &BasicLarge::var38,
"var39", &BasicLarge::var39,
"var40", &BasicLarge::var40,
"var41", &BasicLarge::var41,
"var42", &BasicLarge::var42,
"var43", &BasicLarge::var43,
"var44", &BasicLarge::var44,
"var45", &BasicLarge::var45,
"var46", &BasicLarge::var46,
"var47", &BasicLarge::var47,
"var48", &BasicLarge::var48,
"var49", &BasicLarge::var49);
}
void registerBasicGetterSetter(sol::state& lua)
{
lua.new_usertype<Basic>("c",
sol::constructors<Basic()>(),
"val", sol::property(&Basic::get, &Basic::set));
}
void registerCounter(sol::state& lua)
{
lua.new_usertype<Counter>("Counter",
sol::constructors<Counter()>(),
"get", &Counter::get,
"static_add", &Counter::static_add);
}
void registerSharedObject(sol::state& lua)
{
lua.new_usertype<SharedObject>("SharedObject",
sol::call_constructor,
sol::factories([]() { return std::make_shared<SharedObject>(); }),
"get", &SharedObject::get);
lua.set_function("get_shared", &shared_object_return);
lua.set_function("use_shared", &shared_object_get_value);
}
void table_global_string_get_measure(benchmark::State& state)
{
sol::state lua;
lua["value"] = kMagicValue;
double x = 0;
for (auto _ : state)
{
(void) _;
x += lua["value"].get<double>();
}
benchmark::DoNotOptimize(x);
}
void table_global_string_set_measure(benchmark::State& state)
{
sol::state lua;
double v = 0;
for (auto _ : state)
{
(void) _;
v += kMagicValue;
lua["value"] = v;
}
benchmark::DoNotOptimize(v);
}
void table_get_measure(benchmark::State& state)
{
sol::state lua;
lua.script("warble = { value = 24.0 }");
sol::table t = lua["warble"];
double x = 0;
for (auto _ : state)
{
(void) _;
x += t["value"].get<double>();
}
benchmark::DoNotOptimize(x);
}
void table_set_measure(benchmark::State& state)
{
sol::state lua;
lua.script("warble = { value = 24.0 }");
sol::table t = lua["warble"];
double v = 0;
for (auto _ : state)
{
(void) _;
v += kMagicValue;
t["value"] = v;
}
benchmark::DoNotOptimize(v);
}
void table_chained_get_measure(benchmark::State& state)
{
sol::state lua;
lua.script("ulahibe = { warble = { value = 24.0 } }");
double x = 0;
for (auto _ : state)
{
(void) _;
x += lua["ulahibe"]["warble"]["value"].get<double>();
}
benchmark::DoNotOptimize(x);
}
void table_chained_set_measure(benchmark::State& state)
{
sol::state lua;
lua.script("ulahibe = { warble = { value = 24.0 } }");
double v = 0;
for (auto _ : state)
{
(void) _;
v += kMagicValue;
lua["ulahibe"]["warble"]["value"] = v;
}
benchmark::DoNotOptimize(v);
}
void c_function_measure(benchmark::State& state)
{
sol::state lua;
lua.set_function("f", +[](double value) { return value; });
lua.script("function invoke_f() return f(24.0) end");
for (auto _ : state)
{
(void) _;
lua["invoke_f"]();
}
}
void lua_function_in_c_measure(benchmark::State& state)
{
sol::state lua;
lua.script("function f(i) return i end");
sol::function f = lua["f"];
double x = 0;
for (auto _ : state)
{
(void) _;
x += f.call<double>(kMagicValue);
}
benchmark::DoNotOptimize(x);
}
void c_function_through_lua_in_c_measure(benchmark::State& state)
{
sol::state lua;
lua.set_function("f", +[](double value) { return value; });
sol::function f = lua["f"];
double x = 0;
for (auto _ : state)
{
(void) _;
x += f.call<double>(kMagicValue);
}
benchmark::DoNotOptimize(x);
}
void member_function_call_measure(benchmark::State& state)
{
sol::state lua;
registerBasic(lua);
lua.script("b = c.new()\nfunction call_member() b:set(b:get() + 1.0) end");
for (auto _ : state)
{
(void) _;
lua["call_member"]();
}
}
void userdata_variable_access_measure(benchmark::State& state)
{
sol::state lua;
registerBasic(lua);
lua.script("b = c.new()\nfunction access_var() b.var = b.var + 1.0 return b.var end");
for (auto _ : state)
{
(void) _;
lua["access_var"]();
}
}
void userdata_variable_access_large_measure(benchmark::State& state)
{
sol::state lua;
registerBasicLarge(lua);
lua.script("b = cl.new()\nfunction access_var_large() b.var0 = b.var0 + 1 return b.var0 end");
for (auto _ : state)
{
(void) _;
lua["access_var_large"]();
}
}
void userdata_variable_access_last_measure(benchmark::State& state)
{
sol::state lua;
registerBasicLarge(lua);
lua.script("b = cl.new()\nfunction access_var_last() b.var49 = b.var49 + 1 return b.var49 end");
for (auto _ : state)
{
(void) _;
lua["access_var_last"]();
}
}
void stateful_function_object_measure(benchmark::State& state)
{
sol::state lua;
lua.set_function("f", StatefulFunction{});
sol::function f = lua["f"];
double x = 0;
for (auto _ : state)
{
(void) _;
x += f.call<double>(kMagicValue);
}
benchmark::DoNotOptimize(x);
}
void multi_return_lua_measure(benchmark::State& state)
{
sol::state lua;
lua.set_function("f", &sol3_multi_return);
lua.script("function invoke_multi() local a,b=f(24.0) return a+b end");
for (auto _ : state)
{
(void) _;
lua["invoke_multi"]();
}
}
void multi_return_measure(benchmark::State& state)
{
sol::state lua;
lua.set_function("f", &sol3_multi_return);
sol::function f = lua["f"];
double x = 0;
for (auto _ : state)
{
(void) _;
std::tuple<double, double> values = f.call<double, double>(kMagicValue);
x += std::get<0>(values);
x += std::get<1>(values);
}
benchmark::DoNotOptimize(x);
}
void derived_base_measure(benchmark::State& state)
{
sol::state lua;
lua.new_usertype<ComplexBaseA>("ComplexBaseA",
"a_func", &ComplexBaseA::a_func,
"a", &ComplexBaseA::a);
lua.new_usertype<ComplexBaseB>("ComplexBaseB",
"b_func", &ComplexBaseB::b_func,
"b", &ComplexBaseB::b);
lua.new_usertype<ComplexAB>("ComplexAB",
sol::base_classes, sol::bases<ComplexBaseA, ComplexBaseB>(),
"ab_func", &ComplexAB::ab_func,
"ab", &ComplexAB::ab);
ComplexAB ab;
lua["b"] = &ab;
lua.script("function call_base() return b:a_func() + b:b_func() end");
for (auto _ : state)
{
(void) _;
lua["call_base"]();
}
}
void optional_success_measure(benchmark::State& state)
{
sol::state lua;
lua.script("warble = { value = 24.0 }");
double x = 0;
for (auto _ : state)
{
(void) _;
sol::optional<double> value = lua["warble"]["value"];
x += value.value_or(1.0);
}
benchmark::DoNotOptimize(x);
}
void optional_half_failure_measure(benchmark::State& state)
{
sol::state lua;
lua.script("warble = { value = 'x' }");
double x = 0;
for (auto _ : state)
{
(void) _;
sol::optional<double> value = lua["warble"]["value"];
x += value.value_or(1.0);
}
benchmark::DoNotOptimize(x);
}
void optional_failure_measure(benchmark::State& state)
{
sol::state lua;
double x = 0;
for (auto _ : state)
{
(void) _;
sol::optional<double> value = lua["warble"]["value"];
x += value.value_or(1.0);
}
benchmark::DoNotOptimize(x);
}
void return_userdata_measure(benchmark::State& state)
{
sol::state lua;
registerBasic(lua);
lua.set_function("f", &basic_return);
lua.set_function("h", &basic_get_var);
lua.script("function invoke_userdata() return h(f()) end");
for (auto _ : state)
{
(void) _;
lua["invoke_userdata"]();
}
}
void userdata_variable_write_measure(benchmark::State& state)
{
sol::state lua;
registerBasic(lua);
lua.script("b = c.new()\nfunction write_var() b.var = 24.0 end");
for (auto _ : state)
{
(void) _;
lua["write_var"]();
}
}
void userdata_property_getter_measure(benchmark::State& state)
{
sol::state lua;
registerBasicGetterSetter(lua);
lua.script("b = c.new()\nfunction read_getter() return b.val end");
for (auto _ : state)
{
(void) _;
lua["read_getter"]();
}
}
void userdata_property_setter_measure(benchmark::State& state)
{
sol::state lua;
registerBasicGetterSetter(lua);
lua.script("b = c.new()\nfunction write_setter() b.val = 24.0 end");
for (auto _ : state)
{
(void) _;
lua["write_setter"]();
}
}
void lambda_capture_measure(benchmark::State& state)
{
sol::state lua;
double extra = kMagicValue;
lua.set_function("f", [extra](double v) { return v + extra; });
lua.script("function invoke_lambda() return f(24.0) end");
for (auto _ : state)
{
(void) _;
lua["invoke_lambda"]();
}
}
void shared_ptr_return_measure(benchmark::State& state)
{
sol::state lua;
registerSharedObject(lua);
lua.script("function invoke_shared() return get_shared():get() end");
for (auto _ : state)
{
(void) _;
lua["invoke_shared"]();
}
}
void shared_ptr_pass_measure(benchmark::State& state)
{
sol::state lua;
registerSharedObject(lua);
lua.script("obj = SharedObject()\nfunction invoke_pass_shared() return use_shared(obj) end");
for (auto _ : state)
{
(void) _;
lua["invoke_pass_shared"]();
}
}
void static_member_function_call_measure(benchmark::State& state)
{
sol::state lua;
registerCounter(lua);
lua.script("function invoke_static() return Counter.static_add(10, 32) end");
for (auto _ : state)
{
(void) _;
lua["invoke_static"]();
}
}
void derived_method_call_measure(benchmark::State& state)
{
sol::state lua;
lua.new_usertype<ComplexBaseA>("ComplexBaseA",
"a_func", &ComplexBaseA::a_func,
"a", &ComplexBaseA::a);
lua.new_usertype<ComplexBaseB>("ComplexBaseB",
"b_func", &ComplexBaseB::b_func,
"b", &ComplexBaseB::b);
lua.new_usertype<ComplexAB>("ComplexAB",
sol::constructors<ComplexAB()>(),
sol::base_classes, sol::bases<ComplexBaseA, ComplexBaseB>(),
"ab_func", &ComplexAB::ab_func,
"ab", &ComplexAB::ab);
ComplexAB ab;
lua["obj"] = &ab;
lua.script("function call_derived() return obj:ab_func() end");
for (auto _ : state)
{
(void) _;
lua["call_derived"]();
}
}
void implicit_inheritance_measure(benchmark::State& state)
{
sol::state lua;
lua.new_usertype<ComplexBaseA>("ComplexBaseA",
"a_func", &ComplexBaseA::a_func);
lua.new_usertype<ComplexAB>("ComplexAB",
sol::constructors<ComplexAB()>(),
sol::base_classes, sol::bases<ComplexBaseA>(),
"ab_func", &ComplexAB::ab_func);
lua.set_function("call_a", +[](ComplexBaseA* obj) -> double { return obj->a_func(); });
lua.script("obj = ComplexAB.new()");
lua.script("function test_implicit() return call_a(obj) end");
for (auto _ : state)
{
(void) _;
lua["test_implicit"]();
}
}
} // namespace
BENCHMARK(table_global_string_get_measure)->Name("table_global_string_get_measure");
BENCHMARK(table_global_string_set_measure)->Name("table_global_string_set_measure");
BENCHMARK(table_get_measure)->Name("table_get_measure");
BENCHMARK(table_set_measure)->Name("table_set_measure");
BENCHMARK(table_chained_get_measure)->Name("table_chained_get_measure");
BENCHMARK(table_chained_set_measure)->Name("table_chained_set_measure");
BENCHMARK(c_function_measure)->Name("c_function_measure");
BENCHMARK(c_function_through_lua_in_c_measure)->Name("c_function_through_lua_in_c_measure");
BENCHMARK(lua_function_in_c_measure)->Name("lua_function_in_c_measure");
BENCHMARK(member_function_call_measure)->Name("member_function_call_measure");
BENCHMARK(userdata_variable_access_measure)->Name("userdata_variable_access_measure");
BENCHMARK(userdata_variable_access_large_measure)->Name("userdata_variable_access_large_measure");
BENCHMARK(userdata_variable_access_last_measure)->Name("userdata_variable_access_last_measure");
BENCHMARK(multi_return_lua_measure)->Name("multi_return_lua_measure");
BENCHMARK(multi_return_measure)->Name("multi_return_measure");
BENCHMARK(stateful_function_object_measure)->Name("stateful_function_object_measure");
BENCHMARK(derived_base_measure)->Name("derived_base_measure");
BENCHMARK(return_userdata_measure)->Name("return_userdata_measure");
BENCHMARK(optional_success_measure)->Name("optional_success_measure");
BENCHMARK(optional_half_failure_measure)->Name("optional_half_failure_measure");
BENCHMARK(optional_failure_measure)->Name("optional_failure_measure");
BENCHMARK(implicit_inheritance_measure)->Name("implicit_inheritance_measure");
BENCHMARK(userdata_variable_write_measure)->Name("userdata_variable_write_measure");
BENCHMARK(userdata_property_getter_measure)->Name("userdata_property_getter_measure");
BENCHMARK(userdata_property_setter_measure)->Name("userdata_property_setter_measure");
BENCHMARK(lambda_capture_measure)->Name("lambda_capture_measure");
BENCHMARK(shared_ptr_return_measure)->Name("shared_ptr_return_measure");
BENCHMARK(shared_ptr_pass_measure)->Name("shared_ptr_pass_measure");
BENCHMARK(static_member_function_call_measure)->Name("static_member_function_call_measure");
BENCHMARK(derived_method_call_measure)->Name("derived_method_call_measure");
+282
View File
@@ -0,0 +1,282 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
import argparse
import json
from collections import defaultdict
from pathlib import Path
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import numpy as np
# ── Label helpers ─────────────────────────────────────────────────────────────
_SUFFIX = "_measure"
def _clean_label(name: str) -> str:
if name.endswith(_SUFFIX):
name = name[: -len(_SUFFIX)]
return name.replace("_", " ")
# ── JSON loading ──────────────────────────────────────────────────────────────
def infer_library_name(path: str) -> str:
return Path(path).stem.replace("benchmark_", "")
def load_google_benchmark_json(path: str, library_name: str) -> dict:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
case_values: dict[str, float] = {}
case_stddev: dict[str, float] = {}
case_errors: dict[str, str] = {}
for entry in data.get("benchmarks", []):
name = entry.get("name", "")
run_type = entry.get("run_type", "")
# Prefer aggregate mean/stddev when available
if run_type == "aggregate":
base_name = entry.get("run_name", name)
agg = entry.get("aggregate_name")
t = entry.get("real_time", entry.get("cpu_time", 0.0))
if agg == "mean":
case_values[base_name] = t
elif agg == "stddev":
case_stddev[base_name] = t
continue
if run_type not in ("iteration", ""):
continue
if entry.get("error_occurred"):
case_errors[name] = entry.get("error_message", "error")
continue
if name not in case_values:
case_values[name] = entry.get("real_time", entry.get("cpu_time", 0.0))
return {"library": library_name, "values": case_values, "stddev": case_stddev, "errors": case_errors}
# ── Merge ─────────────────────────────────────────────────────────────────────
def merge_results(result_sets):
merged: dict[str, dict[str, float]] = defaultdict(dict)
stddev: dict[str, dict[str, float]] = defaultdict(dict)
errors: dict[str, dict[str, str]] = defaultdict(dict)
for result in result_sets:
lib = result["library"]
for case_name, value in result["values"].items():
merged[case_name][lib] = value
for case_name, sd in result.get("stddev", {}).items():
stddev[case_name][lib] = sd
for case_name, error in result["errors"].items():
errors[case_name][lib] = error
return merged, stddev, errors
# ── Plotting ──────────────────────────────────────────────────────────────────
# Dark theme colours
_BG = "#1E1E2E" # figure / axes background
_FG = "#CDD6F4" # text, ticks, labels
_GRID = "#313244" # grid lines
_SPINE = "#45475A" # axis spines
_UNSUP = "#585B70" # "unsupported" text
# Bright palette suited for dark backgrounds
_PALETTE = [
"#89B4FA", # blue
"#FAB387", # peach
"#A6E3A1", # green
"#F38BA8", # red
"#CBA6F7", # mauve
"#94E2D5", # teal
"#F9E2AF", # yellow
"#89DCEB", # sky
]
_LIB_ORDER = ["LuaBridge3Benchmark", "LuaBridgeVanillaBenchmark", "Sol3Benchmark"]
_LIB_ORDER_MAP = {lib: i for i, lib in enumerate(_LIB_ORDER)}
def plot_grouped_bars(merged: dict, stddev: dict, errors: dict, output_file: str, log_scale: bool = False) -> None:
case_names = sorted(merged.keys())
all_libs = {lib for cases in merged.values() for lib in cases}
libraries = sorted(all_libs, key=lambda l: _LIB_ORDER_MAP.get(l, len(_LIB_ORDER)))
if not case_names or not libraries:
raise RuntimeError("No benchmark samples found to plot")
n_cases = len(case_names)
n_libs = len(libraries)
clean_labels = [_clean_label(cn) for cn in case_names]
# ── Layout ────────────────────────────────────────────────────────────────
bar_h = 0.80
group_h = bar_h / n_libs
fig_h = max(10, n_cases * bar_h + 2)
with plt.rc_context({
"text.color": _FG,
"axes.labelcolor": _FG,
"xtick.color": _FG,
"ytick.color": _FG,
}):
fig, ax = plt.subplots(figsize=(14, fig_h))
fig.patch.set_facecolor(_BG)
ax.set_facecolor(_BG)
colors = {lib: _PALETTE[i % len(_PALETTE)] for i, lib in enumerate(libraries)}
y_positions = np.arange(n_cases, dtype=float)
# Max value including error bars — used to size x-axis
x_max_with_err = 1.0
for cn in case_names:
for lib in libraries:
val = merged[cn].get(lib, float("nan"))
if not np.isnan(val):
sd = stddev.get(cn, {}).get(lib, 0.0) or 0.0
x_max_with_err = max(x_max_with_err, val + sd)
for i, library in enumerate(libraries):
values = [merged[cn].get(library, float("nan")) for cn in case_names]
sds = [stddev.get(cn, {}).get(library, float("nan")) for cn in case_names]
bar_y = y_positions + (i - (n_libs - 1) / 2.0) * group_h
xerr_vals = [sd if not np.isnan(sd) else 0.0 for sd in sds]
has_errors = any(sd > 0 for sd in xerr_vals)
ax.barh(
bar_y,
values,
height=group_h * 0.85,
color=colors[library],
label=library,
xerr=xerr_vals if has_errors else None,
error_kw={"ecolor": _FG, "capsize": 3, "elinewidth": 1.2, "capthick": 1.2},
zorder=4,
)
for y, val, sd in zip(bar_y, values, sds):
if np.isnan(val):
ax.text(
0, y, " unsupported",
va="center", ha="left",
fontsize=10, color=_UNSUP, style="italic",
zorder=5,
)
else:
label = f" {val:.1f} ±{sd:.1f} ns" if not np.isnan(sd) and sd > 0 else f" {val:.1f} ns"
ax.text(
val, y, label,
va="center", ha="left",
fontsize=9, color=_FG,
zorder=5,
)
# ── Axes ──────────────────────────────────────────────────────────────
ax.set_yticks(y_positions)
ax.set_yticklabels(clean_labels, fontsize=12)
half_span = (n_libs - 1) / 2.0 * group_h + group_h * 0.425
ax.set_ylim(-half_span, n_cases - 1 + half_span)
ax.invert_yaxis()
if log_scale:
ax.set_xscale("log")
ax.set_xlabel("Time (ns, log scale)", fontsize=13)
else:
ax.set_xlabel("Time (ns)", fontsize=13)
ax.set_xlim(0, x_max_with_err * 1.20)
ax.xaxis.grid(True, color=_GRID, linestyle="--", alpha=1.0, zorder=2)
ax.set_axisbelow(True)
for spine in ax.spines.values():
spine.set_edgecolor(_SPINE)
ax.spines["top"].set_visible(True)
ax.spines["right"].set_visible(False)
ax.xaxis.set_tick_params(which="both", top=True, bottom=True, labeltop=True, labelbottom=True)
ax.tick_params(axis="x", which="both", color=_SPINE, labelsize=11)
# ── Legend & title ────────────────────────────────────────────────────
legend_handles = [mpatches.Patch(color=colors[lib], label=lib) for lib in libraries]
ax.legend(
handles=legend_handles,
loc="upper right",
fontsize=11,
framealpha=1.0,
facecolor=_SPINE,
edgecolor=_SPINE,
labelcolor=_FG,
)
ax.set_title(
"Lua Binding Benchmarks — lower is better (ns)",
fontsize=16, pad=10, fontweight="bold", color=_FG,
)
fig.subplots_adjust(left=0.22, right=0.97, top=0.97, bottom=0.03)
plt.savefig(output_file, dpi=150, facecolor=fig.get_facecolor())
plt.close()
# ── Text summary ──────────────────────────────────────────────────────────
txt_file = Path(output_file).with_suffix(".txt")
col_w = max(len(lib) for lib in libraries) + 2
label_w = max(len(lbl) for lbl in clean_labels) + 2
with open(txt_file, "w", encoding="utf-8") as f:
header = f"{'Benchmark':<{label_w}}" + "".join(f"{lib:>{col_w}}" for lib in libraries)
f.write(header + "\n")
f.write("-" * len(header) + "\n")
for cn, lbl in zip(case_names, clean_labels):
row = f"{lbl:<{label_w}}"
for lib in libraries:
val = merged[cn].get(lib)
cell = f"{val:>{col_w - 3}.1f} ns" if val is not None else f"{'n/a':>{col_w}}"
row += cell
f.write(row + "\n")
print(f"Saved: {txt_file}")
# ── Entry point ───────────────────────────────────────────────────────────────
def main():
parser = argparse.ArgumentParser(
description="Plot comparisons from Google Benchmark JSON files"
)
parser.add_argument(
"--input", nargs="+", required=True,
help="One or more Google Benchmark JSON files"
)
parser.add_argument(
"--output", default="Benchmarks/benchmark_comparison.png",
help="Output PNG file"
)
parser.add_argument(
"--log", action="store_true",
help="Use a logarithmic x-axis"
)
args = parser.parse_args()
result_sets = [
load_google_benchmark_json(path, infer_library_name(path))
for path in args.input
]
merged, stddev, errors = merge_results(result_sets)
Path(args.output).parent.mkdir(parents=True, exist_ok=True)
plot_grouped_bars(merged, stddev, errors, args.output, log_scale=args.log)
print(f"Saved: {args.output}")
if __name__ == "__main__":
main()
+2
View File
@@ -0,0 +1,2 @@
matplotlib
numpy
+181
View File
@@ -0,0 +1,181 @@
## Version 3.0
* Moved to C++17 as minimum supported standard C++ version.
* Reworked the whole library to be able to use it without c++ exceptions enabled.
* Lot of optimisations have been added, and now the library is *fast*, nearly in the same league as sol2 v3.x.
* Breaking Change: The method `Stack<T>::push` now takes a `std::error_code&` as last parameter and returns a `bool`.
* Breaking Change: The class `LuaException` has been reworked and it now take a `std::error_code` instead of a int.
* Breaking Change: The class `LuaException` is now thrown if a unregistered class is pushed via the Stack class, also when calling `LuaRef::operator()`, but only if exceptions are enabled.
* Breaking Change: `LuaRef::operator()` now returns the class `LuaResult`, where it is possible to obtain the call results or error message.
* Breaking Change: LuaBridge does not silently enable exceptions when calling `getGlobalNamespace`. Call `enableExceptions(lua_State*)` if you want to enable them explicitly.
* Breaking Change: Removed `RefCounterPtr`, maintaining the reference counts in a unsynchronized global table is not production quality.
* Breaking Change: Removed `Class<T>::addStaticData`, it was just an alias for `Class<T>::addStaticProperty`.
* Breaking Change: Removed `Class<T>::addCFunction`, it was just an alias for `Class<T>::addFunction`.
* Breaking Change: Removed `Class<T>::addStaticCFunction`, it was just an alias for `Class<T>::addStaticFunction`.
* Allow specifying a non virtual base class method when declaring class members (functions or variables) not exposed in the inherited class.
* Allow using capturing lambdas in `Namespace::addFunction`, `Namespace::addProperty`, `Class<T>::addFunction`, `Class<T>::addStaticFunction`, `Class<T>::addProperty` and `Class<T>::addStaticProperty`.
* Added multiple inheritance support: `deriveClass` now accepts more than one registered base class (e.g. `deriveClass<D, A, B>`).
* Added C++20 coroutine integration: `CppCoroutine<R>` type that can be registered via `Namespace::addCoroutine()` to expose C++ generators to Lua using `co_yield` and `co_return`.
* Added `LuaCoroutine` awaitable to resume a child Lua thread synchronously from inside a `CppCoroutine` body using `co_await`.
* Added `lua_resume_x` and `lua_isyieldable_x` portable helpers in `LuaBridge/detail/LuaHelpers.h`.
* Added `LUABRIDGE_HAS_CXX20_COROUTINES` feature-detection macro; opt out with `LUABRIDGE_DISABLE_CXX20_COROUTINES`.
* Added `ErrorCode::CoroutineYieldFromNonCoroutine` and `ErrorCode::CoroutineAlreadyDone` error codes.
* Added `Namespace::addVariable` to allow adding a modifiable value by copy into the namespace without incurring in function calls or metatables generation.
* Added `luabridge::callWithHandler` free function and `LuaRef::callWithHandler` member to provide a custom Lua message handler during `lua_pcall`.
* Added `luabridge::newFunction` free function and `LuaRef::newFunction` static method to wrap any C++ callable into a Lua function exposed as a `LuaRef`.
* Added `luabridge::getNamespaceFromStack` function to construct a namespace object from a table on the stack.
* Added `luabridge::registerMainThread` function especially useful when using lua 5.1 to register the main lua thread.
* Added `std::shared_ptr` support for types intrusively deriving from `std::enable_shared_from_this`.
* Added `Class<T>::addConstructor` support for specifying factory functor to do placement new of the object instance.
* Added `Class<T>::addDestructor` to register a custom `__destruct` metamethod hook invoked just before the C++ destructor runs.
* Added `Class<T>::addFunction` overload taking a `lua_CFunction` as if it were a member.
* Added `Class<T>::addIndexMetaMethod` to allow register `__index` metamethod fallback on a registered class.
* Added `Class<T>::addNewIndexMetaMethod` to allow register `__newindex` metamethod fallback on a registered class.
* Added `Class<T>::addStaticIndexMetaMethod` for fallback `__index` handling on the static class table.
* Added `Class<T>::addStaticNewIndexMetaMethod` for fallback `__newindex` handling on the static class table.
* Added `LuaRef::isValid` to check when the reference is a LUA_NOREF.
* Added `LuaRef::isCallable` to check when the reference is a function or has a `__call` metamethod.
* Added `LuaException::state` to return the `lua_State` associated with the exception.
* Added `void*` and `const void*` stack specializations mapped transparently to Lua lightuserdata.
* Added support for `std::byte` as stack value type.
* Added support for `std::string_view` as stack value type.
* Added support for `std::tuple` as stack value type.
* Added support for `std::optional` as stack value type.
* Added support for `std::set` as stack value type by using `LuaBridge/Set.h`.
* Added support to `LuaRef` for being hashed with `std::hash` (`LuaRef` properly usable in `std::unordered_map`).
* Added `LuaRef::append` and variadic `LuaRef::append(vs...)` to append values to a Lua sequence table using `lua_rawseti`.
* Added `LuaRef::call<R>()` now returns a strongly-typed `TypeResult<R>` instead of a generic `LuaResult`; `LuaRef::operator()` returns `TypeResult<void>`.
* Added `LUABRIDGE_STRICT_STACK_CONVERSIONS` compile-time flag to enforce strict type-safe stack conversions (`bool` requires `LUA_TBOOLEAN`, `std::string` requires `LUA_TSTRING`).
* Added `LuaFunction<Signature>` strongly-typed wrapper class for invoking Lua functions with compile-time argument and return-type checking.
* Added `TypeResult<T>::valueOr(default)` to extract the contained value or return a fallback when a cast fails.
* Added `allowOverridingMethods` class option to permit Lua scripts to override C++ methods registered in an extensible class.
* Added single header amalgamated distribution file, to simplify including in projects.
* Added more asserts for functions and property names.
* Renamed `luabridge::Nil` to `luabridge::LuaNil` to allow including LuaBridge in Obj-C sources.
* Removed the limitation of maximum 8 parameters in functions.
* Removed the limitation of maximum 8 parameters in constructors.
* Removed `Class<T>::addData`, it was just an alias for `Class<T>::addProperty`.
* Removed `TypeList` from loki, using parameter packs and `std::tuple` with `std::apply`.
* Removed juce traces from unit tests, simplified unit tests runs.
* Changed all generic functions in `LuaRef` and `TableItem` to accept arguments by const reference instead of by copy.
* Fixed `Stack<bool>::get` to properly require `LUA_TBOOLEAN` when `LUABRIDGE_STRICT_STACK_CONVERSIONS` is enabled; without the flag, legacy `lua_toboolean` semantics are preserved.
* Fixed floating-point `Stack<T>::push`, `get`, and `isInstance` to correctly allow NaN and Inf values to pass through without error.
* Fixed issue when `LuaRef::cast<>` fails with exceptions enabled, popping from the now empty stack could trigger the panic handler twice.
* Fixed unaligned access in user allocated member pointers in 64bit machines reported by ASAN.
* Fixed access of `LuaRef` in garbage collected `lua_thread`.
* Included testing against Luau VM
* Included testing against Ravi VM
* Bumped lua 5.2.x in unit tests from lua 5.2.0 to 5.2.4.
* Bumped lua 5.4.x in unit tests from lua 5.4.1 to 5.4.8.
* Added Lua 5.3.6 and 5.5.0 in unit tests.
* Run against PUC-Lua, Luau, LuaJIT and Ravi in CI.
* Converted the manual from html to markdown.
* Small improvements to code and doxygen comments readability.
* Support for `__fastcall` function pointers.
## Version 2.6
* Added namespace `addFunction()` accepting `std::function` (C++11 only).
* Added class `addStaticFunction()` accepting `std::function` (C++11 only).
* Update the Doxygen documentation.
* Add brief API reference into the manual.
* Hide non-public `luabridge` members into the `detail` namespace.
* Fix stack cleanup by `LuaRef::isInstance()` method.
## Version 2.5
* Introduce stack `isInstance()` method.
* Introduce LuaRef `isInstance()` method.
* Added a convenience `isInstance()` function template.
## Version 2.4.1
* Do not call the object destructor then its constructor throws.
## Version 2.4
* String stack get specialization doesn't change the stack value anymore.
* Added namespace `addProperty()` accepting C-functions.
* Introduce enableExceptions function.
## Version 2.3.2
* Fixed registration continuation for an already registered class.
## Version 2.3.1
* Fixed registration continuation issues.
## Version 2.3
* Added class `addFunction()` accepting proxy functions (C++11 only).
* Added class `addFunction()` accepting `std::function` (C++11 only).
* Added class `addProperty()` accepting functions with lua_State* parameter.
* Added class `addProperty()` accepting `std::function` (C++11 only).
* Added stack traits for `std::unordered_map` (`UnorderedMap.h`).
* Now using lightuserdata for function pointers.
## Version 2.2.2
* Performance optimization.
## Version 2.2.1
* Refactored namespace and class handling.
## Version 2.2
* Refactored stack operations.
* Handle exceptions in stack operations.
## Version 2.1.2
* Added `operator==` and `operator!=` for `RefCountedPtr` template.
## Version 2.1.1
* Support for `__stdcall` function pointers.
## Version 2.1
* Added stack traits for `std::vector` (`Vector.h`).
* Added stack traits for `std::list` (`List.h`).
* Added stack traits for `std::map` (`Map.h`).
* Added ability to use `LuaRef` objects as an `std::map` keys.
* Fixed some manual errata.
## Version 2.0
* Numerous bug fixes.
* Feature Requests from Github issues.
* Added `LuaRef` object.
* Rewritten documentation.
## Version 1.1.0
* Split code up into several files.
* Add Lua table and type representations (based on Nigel's code).
* Reformat documentation as external HTML file.
## Version 1.0.3
* Pass `nil` to Lua when a null pointer is passed for objects with shared lifetime.
## Version 1.0.2
* Option to hide metatables selectable at runtime, default to true.
* `addStaticMethod()` renamed to `addStaticFunction()` for consistency.
* `addMethod()` renamed to `addFunction()` for consistency.
* `addCFunction()` registrations.
* Convert null pointers to and from `nil`.
* Small performance increase in class pointer extraction.
## Version 1.0.1
* Backward compatibility with Lua 5.1.x.
## Version 1.0
* Explicit lifetime management models.
* Generalized containers.
* Single header distribution.
+42
View File
@@ -0,0 +1,42 @@
cmake_minimum_required (VERSION 3.10)
project (LuaBridge)
include (CMakeDependentOption)
if (NOT CMAKE_CXX_STANDARD)
set (CMAKE_CXX_STANDARD 20)
endif()
set (CMAKE_CXX_STANDARD_REQUIRED ON)
set (CMAKE_CXX_EXTENSIONS OFF)
find_program (FIND_EXECUTABLE find)
find_program (LCOV_EXECUTABLE lcov)
find_program (GENHTML_EXECUTABLE genhtml)
cmake_dependent_option (LUABRIDGE_TESTING "Build tests" ON "CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR" OFF)
cmake_dependent_option (LUABRIDGE_COVERAGE "Enable coverage" OFF "LUABRIDGE_TESTING;FIND_EXECUTABLE;LCOV_EXECUTABLE;GENHTML_EXECUTABLE" OFF)
cmake_dependent_option (LUABRIDGE_BENCHMARKS "Build benchmark executable" OFF "CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR" OFF)
set (LUABRIDGE_SANITIZE "" CACHE STRING "Sanitizer to enable (address, undefined, thread)")
add_subdirectory (Source)
if (LUABRIDGE_TESTING)
include (CTest)
enable_testing()
set (gtest_force_shared_crt ON CACHE BOOL "Use /MD and /MDd" FORCE)
add_subdirectory (ThirdParty/googletest)
add_subdirectory (Tests)
endif ()
if (LUABRIDGE_BENCHMARKS)
add_subdirectory (Benchmarks)
endif ()
add_custom_target (Documentation SOURCES
CHANGES.md
README.md
Manual.md
Doxyfile
)
+38
View File
@@ -0,0 +1,38 @@
Contributing to LuaBridge3
==========================
Thank you for considering contributing to LuaBridge3! We appreciate your interest in helping us make this library better.
The following is a set of guidelines for contributing to LuaBridge3. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
Getting Started
---------------
- Fork the repository and clone it locally.
- Create a branch for your changes: `git checkout -b my-branch-name`.
- Make your changes and commit them: `git commit -am 'Add some feature'`.
- Push your changes to your fork: `git push origin my-branch-name`.
- Open a pull request against the main branch of the original repository.
Testing
-------
We use Google Test for unit testing. Before submitting a pull request, please make sure that all tests pass by running all variants of them.
Documentation
-------------
We use Doxygen to provide inline documentation for this project. Please make sure that your code is documented using Doxygen comments. The documentation should explain what the code does, how to use it, and any other relevant information.
Pull Request Guidelines
-----------------------
- Pull requests should be focused on a single feature or bug fix.
- Please include a summary of the change and which issue is fixed or which feature is added.
- If the pull request adds functionality, please include tests for it.
- Code coverage should never decrease. If it does, consider writing more tests.
- Pull requests should be made against the main branch.
Code of Conduct
===============
Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.
License
=======
By contributing to LuaBridge3, you agree that your contributions will be licensed under the MIT License.
File diff suppressed because it is too large Load Diff
+326
View File
@@ -0,0 +1,326 @@
# Doxyfile 1.8.13
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = LuaBridge
PROJECT_NUMBER =
PROJECT_BRIEF =
PROJECT_LOGO =
OUTPUT_DIRECTORY =
CREATE_SUBDIRS = NO
ALLOW_UNICODE_NAMES = NO
OUTPUT_LANGUAGE = English
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = NO
ABBREVIATE_BRIEF =
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = YES
FULL_PATH_NAMES = NO
STRIP_FROM_PATH =
STRIP_FROM_INC_PATH =
SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = NO
QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 4
ALIASES =
TCL_SUBST =
OPTIMIZE_OUTPUT_FOR_C = NO
OPTIMIZE_OUTPUT_JAVA = NO
OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO
EXTENSION_MAPPING =
MARKDOWN_SUPPORT = YES
TOC_INCLUDE_HEADINGS = 0
AUTOLINK_SUPPORT = YES
BUILTIN_STL_SUPPORT = YES
CPP_CLI_SUPPORT = NO
SIP_SUPPORT = NO
IDL_PROPERTY_SUPPORT = YES
DISTRIBUTE_GROUP_DOC = NO
GROUP_NESTED_COMPOUNDS = NO
SUBGROUPING = YES
INLINE_GROUPED_CLASSES = NO
INLINE_SIMPLE_STRUCTS = NO
TYPEDEF_HIDES_STRUCT = NO
LOOKUP_CACHE_SIZE = 0
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL = NO
EXTRACT_PRIVATE = YES
EXTRACT_PACKAGE = NO
EXTRACT_STATIC = NO
EXTRACT_LOCAL_CLASSES = NO
EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
CASE_SENSE_NAMES = NO
HIDE_SCOPE_NAMES = NO
HIDE_COMPOUND_REFERENCE= NO
SHOW_INCLUDE_FILES = NO
SHOW_GROUPED_MEMB_INC = NO
FORCE_LOCAL_INCLUDES = NO
INLINE_INFO = NO
SORT_MEMBER_DOCS = NO
SORT_BRIEF_DOCS = NO
SORT_MEMBERS_CTORS_1ST = YES
SORT_GROUP_NAMES = YES
SORT_BY_SCOPE_NAME = YES
STRICT_PROTO_MATCHING = NO
GENERATE_TODOLIST = NO
GENERATE_TESTLIST = NO
GENERATE_BUGLIST = NO
GENERATE_DEPRECATEDLIST= NO
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = NO
SHOW_FILES = NO
SHOW_NAMESPACES = NO
FILE_VERSION_FILTER =
LAYOUT_FILE =
CITE_BIB_FILES =
#---------------------------------------------------------------------------
# Configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = YES
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = YES
WARN_AS_ERROR = NO
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
#---------------------------------------------------------------------------
# Configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = Source
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.c \
*.cpp \
*.h \
*.hpp
RECURSIVE = YES
EXCLUDE =
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXCLUDE_SYMBOLS = luabridge::detail::*
EXAMPLE_PATH =
EXAMPLE_PATTERNS = *
EXAMPLE_RECURSIVE = NO
IMAGE_PATH =
INPUT_FILTER =
FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE =
#---------------------------------------------------------------------------
# Configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = NO
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
REFERENCED_BY_RELATION = NO
REFERENCES_RELATION = NO
REFERENCES_LINK_SOURCE = YES
SOURCE_TOOLTIPS = YES
USE_HTAGS = NO
VERBATIM_HEADERS = NO
CLANG_ASSISTED_PARSING = NO
CLANG_OPTIONS =
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = NO
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = YES
HTML_OUTPUT = Documentation
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
HTML_EXTRA_STYLESHEET =
HTML_EXTRA_FILES =
HTML_COLORSTYLE_HUE = 240
HTML_COLORSTYLE_SAT = 64
HTML_COLORSTYLE_GAMMA = 80
HTML_TIMESTAMP = NO
HTML_DYNAMIC_SECTIONS = NO
HTML_INDEX_NUM_ENTRIES = 100
GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "Doxygen generated docs"
DOCSET_BUNDLE_ID = org.doxygen.Project
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
DOCSET_PUBLISHER_NAME = Publisher
GENERATE_HTMLHELP = NO
CHM_FILE =
HHC_LOCATION =
GENERATE_CHI = NO
CHM_INDEX_ENCODING =
BINARY_TOC = NO
TOC_EXPAND = NO
GENERATE_QHP = NO
QCH_FILE =
QHP_NAMESPACE = org.doxygen.Project
QHP_VIRTUAL_FOLDER = doc
QHP_CUST_FILTER_NAME =
QHP_CUST_FILTER_ATTRS =
QHP_SECT_FILTER_ATTRS =
QHG_LOCATION =
GENERATE_ECLIPSEHELP = NO
ECLIPSE_DOC_ID = org.doxygen.Project
DISABLE_INDEX = NO
GENERATE_TREEVIEW = YES
ENUM_VALUES_PER_LINE = 4
TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
USE_MATHJAX = NO
MATHJAX_FORMAT = HTML-CSS
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
MATHJAX_EXTENSIONS =
MATHJAX_CODEFILE =
SEARCHENGINE = YES
SERVER_BASED_SEARCH = NO
EXTERNAL_SEARCH = NO
SEARCHENGINE_URL =
SEARCHDATA_FILE = searchdata.xml
EXTERNAL_SEARCH_ID =
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# Configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = a4
EXTRA_PACKAGES =
LATEX_HEADER =
LATEX_FOOTER =
LATEX_EXTRA_STYLESHEET =
LATEX_EXTRA_FILES =
PDF_HYPERLINKS = YES
USE_PDFLATEX = YES
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
LATEX_SOURCE_CODE = NO
LATEX_BIB_STYLE = plain
LATEX_TIMESTAMP = NO
#---------------------------------------------------------------------------
# Configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
RTF_SOURCE_CODE = NO
#---------------------------------------------------------------------------
# Configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_SUBDIR =
MAN_LINKS = NO
#---------------------------------------------------------------------------
# Configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = NO
XML_OUTPUT = xml
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
PERLMOD_PRETTY = YES
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = NO
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = NO
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED = WIN32 \
= \
1
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration options related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
PERL_PATH = /bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = NO
MSCGEN_PATH =
DIA_PATH =
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = NO
DOT_NUM_THREADS = 0
DOT_FONTNAME = Helvetica
DOT_FONTSIZE = 10
DOT_FONTPATH =
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
GROUP_GRAPHS = YES
UML_LOOK = NO
UML_LIMIT_NUM_FIELDS = 10
TEMPLATE_RELATIONS = NO
INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
CALL_GRAPH = NO
CALLER_GRAPH = NO
GRAPHICAL_HIERARCHY = YES
DIRECTORY_GRAPH = YES
DOT_IMAGE_FORMAT = png
INTERACTIVE_SVG = NO
DOT_PATH =
DOTFILE_DIRS =
MSCFILE_DIRS =
DIAFILE_DIRS =
PLANTUML_JAR_PATH =
PLANTUML_CFG_FILE =
PLANTUML_INCLUDE_PATH =
DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
DOT_TRANSPARENT = NO
DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
DOT_CLEANUP = YES
+2
View File
@@ -0,0 +1,2 @@
!.gitignore
*.txt
Binary file not shown.

After

Width:  |  Height:  |  Size: 449 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

+27
View File
@@ -0,0 +1,27 @@
LuaBridge3 is published under the terms of the MIT License
http://www.opensource.org/licenses/mit-license.html
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
* Copyright 2020, kunitoki
* Copyright 2019, Dmitry Tarakanov
* Copyright 2012, Vinnie Falco
* Copyright 2008, Nigel Atkinson
* Copyright 2007, Nathan Reed
+3022
View File
File diff suppressed because it is too large Load Diff
+273
View File
@@ -0,0 +1,273 @@
<p float="left">
<a href="https://kunitoki.github.io/LuaBridge3">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./Images/logo-dark.png">
<img height="118" src="./Images/logo-bright.png">
</picture>
</a>
&nbsp;&nbsp;&nbsp;&nbsp;
<a href="https://lua.org">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./Images/lua-dark.png">
<img height="118" src="./Images/lua-bright.png">
</picture>
</a>
</p>
[![Coverage Status](https://coveralls.io/repos/github/kunitoki/LuaBridge3/badge.svg?branch=master&kill_cache=1)](https://coveralls.io/github/kunitoki/LuaBridge3?branch=master)
[![MacOS](https://github.com/kunitoki/LuaBridge3/actions/workflows/build_macos.yml/badge.svg?branch=master)](https://github.com/kunitoki/LuaBridge3/actions/workflows/build_macos.yml)
[![Windows](https://github.com/kunitoki/LuaBridge3/actions/workflows/build_windows.yml/badge.svg?branch=master)](https://github.com/kunitoki/LuaBridge3/actions/workflows/build_windows.yml)
[![Linux](https://github.com/kunitoki/LuaBridge3/actions/workflows/build_linux.yml/badge.svg?branch=master)](https://github.com/kunitoki/LuaBridge3/actions/workflows/build_linux.yml)
[![UBSAN](https://github.com/kunitoki/LuaBridge3/actions/workflows/build_ubsan.yml/badge.svg?branch=master)](https://github.com/kunitoki/LuaBridge3/actions/workflows/build_ubsan.yml)
[![ASAN](https://github.com/kunitoki/LuaBridge3/actions/workflows/build_asan.yml/badge.svg?branch=master)](https://github.com/kunitoki/LuaBridge3/actions/workflows/build_asan.yml)
<br/>
[![Lua 5.1](https://img.shields.io/badge/lua-5.1-lightblue?logo=lua)](https://www.lua.org/manual/5.1/readme.html)
[![Lua 5.2](https://img.shields.io/badge/lua-5.2-lightblue?logo=lua)](https://www.lua.org/manual/5.2/readme.html)
[![Lua 5.3](https://img.shields.io/badge/lua-5.3-lightblue?logo=lua)](https://www.lua.org/manual/5.3/readme.html)
[![Lua 5.4](https://img.shields.io/badge/lua-5.4-lightblue?logo=lua)](https://www.lua.org/manual/5.4/readme.html)
[![Lua 5.5](https://img.shields.io/badge/lua-5.5-lightblue?logo=lua)](https://www.lua.org/manual/5.5/readme.html)
[![LuaJIT](https://img.shields.io/badge/luajit-2.1-lightblue?logo=lua)](https://luajit.org/luajit.html)
[![Luau](https://img.shields.io/badge/luau-0.713-lightblue?logo=lua)](https://luau.org/)
[![Ravi](https://img.shields.io/badge/ravi-1.0beta11-lightblue?logo=lua)](https://ravilang.github.io/)
# LuaBridge 3.0
[LuaBridge3](https://github.com/kunitoki/LuaBridge3) is a lightweight and dependency-free library for mapping data,
functions, and classes back and forth between C++ and [Lua](http://wwww.lua.org) (a powerful,
fast, lightweight, embeddable scripting language). LuaBridge has been tested
and works with:
* [PUC-Lua](https://lua.org) 5.1.5, 5.2.4, 5.3.6, 5.4.8 and 5.5.0
* [LuaJit](https://luajit.org/) 2.1
* [Luau](https://luau-lang.org/) 0.713
* [Ravi](https://github.com/dibyendumajumdar/ravi) 1.0-beta11
## Features
LuaBridge3 is usable from a compliant C++17 compiler and offers the following features:
* [MIT Licensed](https://www.opensource.org/licenses/mit-license.html), no usage restrictions!
* Headers-only: No Makefile, no .cpp files, just one `#include` and one header file (optional) !
* Works with ANY lua version out there (PUC-Lua, LuaJIT, Luau, Ravi, you name it).
* Simple, light, and nothing else needed.
* Competitive performance with the fastest C++/Lua binding libraries available.
* Fast to compile (even in release mode), scaling linearly with the size of your binded code.
* No macros, settings, or configuration scripts needed.
* Supports different object lifetime management models.
* Convenient, type-safe access to the Lua stack.
* Automatic function parameter type binding.
* Functions and constructors overloading support.
* Easy access to Lua objects like tables and functions.
* Expose C++ classes allowing them to use the flexibility of lua property lookup.
* Interoperable with a wide range of C++ standard library types, including containers, smart pointers, and modern C++17/20/23 additions.
* Written in a clear and easy to debug style.
## Performance
LuaBridge3 has been heavily optimized and now competes directly with [sol2](https://github.com/ThePhD/sol2) — one of the fastest C++/Lua binding libraries — across most workloads.
Benchmarks measure the overhead each library adds on top of the plain Lua C API: the cost of abstracting and wrapping it for C++ use. All libraries are compiled together with the benchmark executable (no separate Lua DLL) so that inlining and link-time optimizations reflect real-world usage scenarios. Every library runs with its maximum safety settings enabled — the numbers represent what you actually ship, not a stripped-down, crash-prone configuration.
The benchmark suite covers common operations: global and table access, free and member function calls, userdata property access across class hierarchies, shared-pointer ownership, multi-return functions, lambda captures, custom type converters. Each case is run in isolation so the numbers are directly comparable between libraries.
![Benchmarks](./Images/benchmarks.png)
## Improvements Over Vanilla LuaBridge
LuaBridge3 offers a set of improvements compared to vanilla LuaBridge:
* The only binder library that works with PUC-Lua as well as LuaJIT, Luau and Ravi, wonderful for game development !
* Faster runtime execution for most common use cases, playing in the same league as the fastest binders in town.
* Can work with both c++ exceptions and without (Works with `-fno-exceptions` and `/EHsc-`).
* Full support for capturing lambdas in all namespace and class methods.
* Overloaded functions support in Namespace functions, Class constructors, functions and static functions.
* Multiple inheritance: `deriveClass<D, A, B, ...>` supports any number of registered base classes.
* Supports placement allocation or custom allocations/deallocations of C++ classes exposed to lua.
* Lightweight object creation: allow adding lua tables on the stack and register methods and metamethods in them.
* Instance metamethods fallbacks via `__index` and `__newindex` in exposed C++ classes.
* Static metamethod fallbacks via `__index` and `__newindex` in exposed C++ classes on the class static table.
* Custom destructor hook via `addDestructor` (`__destruct` metamethod) called just before the C++ destructor.
* Added `std::shared_ptr` to support shared C++/Lua lifetime for types deriving from `std::enable_shared_from_this`.
* `std::unique_ptr<T>` supported as an ownership container — Lua gets a non-owning view while C++ retains ownership.
* Supports conversion to and from `std::nullptr_t`, `std::byte`, `std::pair`, `std::tuple` and `std::reference_wrapper`.
* Supports conversion to and from C style arrays of any supported type.
* `void*` and `const void*` are transparently mapped to Lua lightuserdata.
* Transparent support of all signed and unsigned integer types up to `int64_t`.
* Consistent numeric handling and conversions (signed, unsigned and floats) across all lua versions.
* NaN and Inf values pass through floating-point stack conversions without error.
* Simplified registration of enum types via the `luabridge::Enum` stack wrapper.
* C++20 coroutine integration via `addCoroutine()` and `CppCoroutine<R>`; await Lua threads from C++ with `LuaCoroutine`.
* Opt-out handling of safe stack space checks (automatically avoids exhausting lua stack space when pushing values!).
* Optional strict stack conversions via `LUABRIDGE_STRICT_STACK_CONVERSIONS` (e.g. `bool` requires an actual boolean, not any truthy value).
* Error handler support in Lua calls via `LuaRef::callWithHandler` and `luabridge::callWithHandler`.
* `newFunction` free function wraps any C++ callable into a Lua function exposed as a `LuaRef`.
* `LuaFunction<Signature>` provides a strongly-typed callable wrapper around a Lua function.
* `TypeResult<T>::valueOr(default)` allows safe value extraction with an explicit fallback.
* Can safely register and use classes exposed across shared library boundaries.
### Standard Library Container Support
Optional headers enable transparent Lua↔C++ conversion for a wide range of STL containers. Include only what you need:
| Header | Type | Requirement |
|--------|------|-------------|
| `LuaBridge/Array.h` | `std::array<T,N>` | C++17 |
| `LuaBridge/Vector.h` | `std::vector<T>` | C++17 |
| `LuaBridge/Deque.h` | `std::deque<T>` | C++17 |
| `LuaBridge/ForwardList.h` | `std::forward_list<T>` | C++17 |
| `LuaBridge/List.h` | `std::list<T>` | C++17 |
| `LuaBridge/Map.h` | `std::map<K,V>` | C++17 |
| `LuaBridge/MultiMap.h` | `std::multimap<K,V>` | C++17 |
| `LuaBridge/Set.h` | `std::set<K>` | C++17 |
| `LuaBridge/UnorderedMap.h` | `std::unordered_map<K,V>` | C++17 |
| `LuaBridge/UnorderedMultiMap.h` | `std::unordered_multimap<K,V>` | C++17 |
| `LuaBridge/UnorderedSet.h` | `std::unordered_set<K>` | C++17 |
| `LuaBridge/Optional.h` | `std::optional<T>` | C++17 |
| `LuaBridge/Variant.h` | `std::variant<Ts...>` | C++17 |
| `LuaBridge/Any.h` | `std::any` (push-only) | C++17 |
| `LuaBridge/Span.h` | `std::span<T>` (push-only) | C++20 |
| `LuaBridge/StdExpected.h` | `std::expected<T,E>` | C++23 |
| `LuaBridge/FlatMap.h` | `std::flat_map<K,V>` | C++23 |
| `LuaBridge/FlatSet.h` | `std::flat_set<K>` | C++23 |
`std::filesystem::path` is automatically converted to/from a Lua string when C++17 filesystem is available — no additional header required.
### Modern C++ Feature Detection
LuaBridge3 auto-detects available C++ standard library features and activates the corresponding support without any manual configuration. Every feature can be force-disabled with a `LUABRIDGE_DISABLE_*` preprocessor flag if needed:
| Macro | Feature | Standard |
|-------|---------|----------|
| `LUABRIDGE_HAS_CXX17_FILESYSTEM` | `std::filesystem::path` ↔ string | C++17 |
| `LUABRIDGE_HAS_CXX17_ANY` | `std::any` push registry | C++17 |
| `LUABRIDGE_HAS_CXX20_SPAN` | `std::span` push support | C++20 |
| `LUABRIDGE_HAS_CXX20_RANGES` | `Iterator`/`Range` satisfy `std::ranges` concepts | C++20 |
| `LUABRIDGE_HAS_CXX20_COROUTINES` | `CppCoroutine<R>` / `LuaCoroutine` | C++20 |
| `LUABRIDGE_HAS_CXX23_EXPECTED` | `std::expected<T,E>` conversion | C++23 |
| `LUABRIDGE_HAS_CXX23_FLAT_CONTAINERS` | `std::flat_map` / `std::flat_set` | C++23 |
| `LUABRIDGE_HAS_CXX23_MOVE_ONLY_FUNCTION` | `std::move_only_function` as callable | C++23 |
## Documentation
Please read the [LuaBridge3 Reference Manual](https://kunitoki.github.io/LuaBridge3/Manual) for more details on the API.
## Release Notes
Plase read the [LuaBridge3 Release Notes](https://kunitoki.github.io/LuaBridge3/CHANGES) for more details
## Installing LuaBridge3 (vcpkg)
You can download and install LuaBridge3 using the [vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:
```Powershell or bash
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh # The name of the script should be "./bootstrap-vcpkg.bat" for Powershell
./vcpkg integrate install
./vcpkg install luabridge3
```
The LuaBridge3 port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository.
### Update vcpkg
To update the vcpkg port, we need to know the hash of the commit and the sha512 of its downloaded artifact.
Starting from the commit hash that needs to be published, download the archived artifact and get the sha512 of it:
```bash
COMMIT_HASH=$(git rev-parse HEAD)
wget https://github.com/kunitoki/LuaBridge3/archive/${COMMIT_HASH}.tar.gz
shasum -a 512 ${COMMIT_HASH}.tar.gz
# fbdf09e3bd0d4e55c27afa314ff231537b57653b7c3d96b51eac2a41de0c302ed093500298f341cb168695bae5d3094fb67e019e93620c11c7d6f8c86d3802e2 0e17140276d215e98764813078f48731125e4784.tar.gz
```
Now update the version in https://github.com/microsoft/vcpkg/blob/master/ports/luabridge3/vcpkg.json and the commit hash and sha512 in https://github.com/microsoft/vcpkg/blob/master/ports/luabridge3/portfile.cmake then commit the changes.
Enter into vcpkg folder and issue:
```bash
./vcpkg x-add-version --all
```
Commit the changed files and create a Pull Request for vcpkg.
## Unit Tests
Unit test build requires a CMake and C++17 compliant compiler.
These are the unit test targets:
* `LuaBridgeTests51` - uses Lua 5.1 in C++ mode
* `LuaBridgeTests51Noexcept` - uses Lua 5.1 in C++ mode without exceptions enabled
* `LuaBridgeTests51LuaC` - uses Lua 5.1 in C mode
* `LuaBridgeTests51LuaCNoexcept` - uses Lua 5.1 in C mode without exceptions enabled
* `LuaBridgeTests52` - uses Lua 5.2 in C++ mode
* `LuaBridgeTests52Noexcept` - uses Lua 5.2 in C++ mode without exceptions enabled
* `LuaBridgeTests52LuaC` - uses Lua 5.2 in C mode
* `LuaBridgeTests52LuaCNoexcept` - uses Lua 5.2 in C mode without exceptions enabled
* `LuaBridgeTests53` - uses Lua 5.3 in C++ mode
* `LuaBridgeTests53Noexcept` - uses Lua 5.3 in C++ mode without exceptions enabled
* `LuaBridgeTests53LuaC` - uses Lua 5.3 in C mode
* `LuaBridgeTests53LuaCNoexcept` - uses Lua 5.3 in C mode without exceptions enabled
* `LuaBridgeTests54` - uses Lua 5.4 in C++ mode
* `LuaBridgeTests54Noexcept` - uses Lua 5.4 in C++ mode without exceptions enabled
* `LuaBridgeTests54LuaC` - uses Lua 5.4 in C mode
* `LuaBridgeTests54LuaCNoexcept` - uses Lua 5.4 in C mode without exceptions enabled
* `LuaBridgeTests55` - uses Lua 5.5 in C++ mode
* `LuaBridgeTests55Noexcept` - uses Lua 5.5 in C++ mode without exceptions enabled
* `LuaBridgeTests55LuaC` - uses Lua 5.5 in C mode
* `LuaBridgeTests55LuaCNoexcept` - uses Lua 5.5 in C mode without exceptions enabled
* `LuaBridgeTestsLuaJIT` - uses LuaJIT 2.1
* `LuaBridgeTestsLuaJITNoexcept` - uses LuaJIT 2.1 without exceptions enabled
* `LuaBridgeTestsLuau` - uses Luau
* `LuaBridgeTestsRavi` - uses Ravi
(Luau compiler needs exceptions, so there are no test targets on Luau without exceptions)
(Ravi doesn't fully work without exceptions, so there are no test targets on Ravi without exceptions)
Generate Unix Makefiles and build on Linux:
```bash
git clone --recursive git@github.com:kunitoki/LuaBridge3.git
cmake -G "Unix Makefiles" -DCMAKE_CXX_STANDARD=20 -B Build . # Generates Unix Makefiles
cmake --build Build -DCMAKE_BUILD_TYPE=Debug
# or cmake --build Build -DCMAKE_BUILD_TYPE=Release
# or cmake --build Build -DCMAKE_BUILD_TYPE=RelWithDebInfo
popd
```
Generate XCode project and build on MacOS:
```bash
git clone --recursive git@github.com:kunitoki/LuaBridge3.git
cmake -G Xcode -DCMAKE_CXX_STANDARD=20 -B Build . # Generates XCode project build/LuaBridge.xcodeproj
cmake --build Build -DCMAKE_BUILD_TYPE=Debug
# or cmake --build Build -DCMAKE_BUILD_TYPE=Release
# or cmake --build Build -DCMAKE_BUILD_TYPE=RelWithDebInfo
```
Generate VS2019 solution on Windows:
```cmd
git clone --recursive git@github.com:kunitoki/LuaBridge3.git
cmake -G "Visual Studio 16" -DCMAKE_CXX_STANDARD=20 -B Build . # Generates MSVS solution build/LuaBridge.sln
cmake --build Build -DCMAKE_BUILD_TYPE=Debug
# or cmake --build Build -DCMAKE_BUILD_TYPE=Release
# or cmake --build Build -DCMAKE_BUILD_TYPE=RelWithDebInfo
```
## Official Repository
LuaBridge3 is published under the terms of the [MIT License](https://www.opensource.org/licenses/mit-license.html).
The original version of LuaBridge3 was written by Nathan Reed. The project has
been taken over by Vinnie Falco, who added new functionality, wrote the new
documentation, and incorporated contributions from Nigel Atkinson. Then it has
been forked from the original https://github.com/vinniefalco/LuaBridge into its
own LuaBridge3 repository by kunitoki, and development continued there.
For questions, comments, or bug reports feel free to open a Github issue
or contact kunitoki directly at the email address indicated below.
Copyright 2020, kunitoki (<kunitoki@gmail.com>)<br>
Copyright 2019, Dmitry Tarakanov<br>
Copyright 2012, Vinnie Falco (<vinnie.falco@gmail.com>)<br>
Copyright 2008, Nigel Atkinson<br>
Copyright 2007, Nathan Reed<br>
+54
View File
@@ -0,0 +1,54 @@
cmake_minimum_required (VERSION 3.10)
set (LUABRIDGE_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/Array.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/Dump.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/List.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/LuaBridge.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/Map.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/Set.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/UnorderedMap.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/Vector.h)
source_group ("LuaBridge" FILES ${LUABRIDGE_HEADERS})
set (LUABRIDGE_DETAIL_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/CFunctions.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/ClassInfo.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/Config.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/Coroutine.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/Enum.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/Errors.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/Expected.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/FlagSet.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/FuncTraits.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/Globals.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/Invoke.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/Iterator.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/LuaException.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/LuaHelpers.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/LuaRef.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/Namespace.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/Options.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/Overload.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/Result.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/ScopeGuard.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/Stack.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/TypeTraits.h
${CMAKE_CURRENT_SOURCE_DIR}/LuaBridge/detail/Userdata.h)
source_group ("LuaBridgeDetail" FILES ${LUABRIDGE_DETAIL_HEADERS})
add_library (LuaBridge INTERFACE)
target_sources (LuaBridge INTERFACE
${LUABRIDGE_HEADERS}
${LUABRIDGE_DETAIL_HEADERS})
target_include_directories (LuaBridge INTERFACE .)
#if (CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
install(DIRECTORY LuaBridge DESTINATION "include")
#endif ()
if (MSVC AND CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
add_custom_target (LuaBridgeSources SOURCES
${LUABRIDGE_HEADERS}
${LUABRIDGE_DETAIL_HEADERS})
endif ()
+82
View File
@@ -0,0 +1,82 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2026, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "detail/Stack.h"
#include "detail/Config.h"
#if LUABRIDGE_HAS_CXX17_ANY
#include <any>
#include <functional>
#include <typeindex>
#include <unordered_map>
namespace luabridge {
namespace detail {
using AnyPushFn = std::function<Result(lua_State*, const std::any&)>;
inline std::unordered_map<std::type_index, AnyPushFn>& anyPushRegistry()
{
static std::unordered_map<std::type_index, AnyPushFn> registry;
return registry;
}
} // namespace detail
//=================================================================================================
/**
* @brief Register a push handler for std::any holding type T.
*/
template <class T>
void registerAnyPush(lua_State*)
{
detail::anyPushRegistry()[std::type_index(typeid(T))] =
[](lua_State* L, const std::any& value) -> Result
{
return Stack<T>::push(L, std::any_cast<const T&>(value));
};
}
//=================================================================================================
/**
* @brief Stack specialization for `std::any` (push-only).
*/
template <>
struct Stack<std::any>
{
[[nodiscard]] static Result push(lua_State* L, const std::any& value)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
if (! value.has_value())
{
lua_pushnil(L);
return {};
}
auto& registry = detail::anyPushRegistry();
auto it = registry.find(std::type_index(value.type()));
if (it == registry.end())
return makeErrorCode(ErrorCode::InvalidTypeCast);
return it->second(L, value);
}
[[nodiscard]] static bool isInstance(lua_State*, int)
{
return false; // std::any cannot be detected from Lua side
}
};
} // namespace luabridge
#endif // LUABRIDGE_HAS_CXX17_ANY
+83
View File
@@ -0,0 +1,83 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2020, Dmitry Tarakanov
// SPDX-License-Identifier: MIT
#pragma once
#include "detail/Stack.h"
#include <array>
namespace luabridge {
//=================================================================================================
/**
* @brief Stack specialization for `std::array`.
*/
template <class T, std::size_t Size>
struct Stack<std::array<T, Size>>
{
using Type = std::array<T, Size>;
[[nodiscard]] static Result push(lua_State* L, const Type& array)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, static_cast<int>(Size), 0);
const int tableIndex = lua_gettop(L);
for (std::size_t i = 0; i < Size; ++i)
{
auto result = Stack<T>::push(L, array[i]);
if (! result)
return result;
lua_rawseti(L, tableIndex, static_cast<int>(i + 1));
}
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (!lua_istable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (get_length(L, index) != Size)
return makeErrorCode(ErrorCode::InvalidTableSizeInCast);
const StackRestore stackRestore(L);
Type array;
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
int arrayIndex = 0;
while (lua_next(L, absIndex) != 0)
{
auto item = Stack<T>::get(L, -1);
if (!item)
return makeErrorCode(ErrorCode::InvalidTypeCast);
array[arrayIndex++] = *item;
lua_pop(L, 1);
}
return array;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_istable(L, index) && get_length(L, index) == Size;
}
};
} // namespace luabridge
+80
View File
@@ -0,0 +1,80 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2026, kunitoki
// Copyright 2020, Dmitry Tarakanov
// SPDX-License-Identifier: MIT
#pragma once
#include "detail/Stack.h"
#include <deque>
namespace luabridge {
//=================================================================================================
/**
* @brief Stack specialization for `std::deque`.
*/
template <class T, class Allocator>
struct Stack<std::deque<T, Allocator>>
{
using Type = std::deque<T, Allocator>;
[[nodiscard]] static Result push(lua_State* L, const Type& deque)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, static_cast<int>(deque.size()), 0);
const int tableIndex = lua_gettop(L);
auto it = deque.cbegin();
for (std::size_t i = 1; it != deque.cend(); ++i, ++it)
{
auto result = Stack<T>::push(L, *it);
if (! result)
return result;
lua_rawseti(L, tableIndex, static_cast<int>(i));
}
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (!lua_istable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
const StackRestore stackRestore(L);
Type deque;
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
while (lua_next(L, absIndex) != 0)
{
auto item = Stack<T>::get(L, -1);
if (! item)
return makeErrorCode(ErrorCode::InvalidTypeCast);
deque.emplace_back(*item);
lua_pop(L, 1);
}
return deque;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_istable(L, index);
}
};
} // namespace luabridge
+156
View File
@@ -0,0 +1,156 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2019, Dmitry Tarakanov
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// Copyright 2007, Nathan Reed
// SPDX-License-Identifier: MIT
#pragma once
#include "detail/ClassInfo.h"
#include <iostream>
#include <string>
namespace luabridge {
namespace detail {
inline void putIndent(std::ostream& stream, unsigned level)
{
for (unsigned i = 0; i < level; ++i)
stream << " ";
}
} // namespace detail
//=================================================================================================
/**
* @brief Forward for dumpTable.
*/
inline void dumpTable(lua_State* L, int index, unsigned maxDepth = 1, unsigned level = 0, bool newLine = true, std::ostream& stream = std::cerr);
//=================================================================================================
/**
* @brief Dump a lua value on the stack.
*/
inline void dumpValue(lua_State* L, int index, unsigned maxDepth = 1, unsigned level = 0, bool newLine = true, std::ostream& stream = std::cerr)
{
const int stackTop = lua_gettop(L);
const int absIndex = (index > 0) ? index : (index < 0 ? stackTop + index + 1 : 0);
const int type = (absIndex < 1 || absIndex > stackTop) ? LUA_TNONE : lua_type(L, index);
switch (type)
{
case LUA_TNIL:
stream << "nil";
break;
case LUA_TBOOLEAN:
stream << (lua_toboolean(L, index) ? "true" : "false");
break;
case LUA_TNUMBER:
stream << lua_tonumber(L, index);
break;
case LUA_TSTRING:
stream << '"' << lua_tostring(L, index) << '"';
break;
case LUA_TFUNCTION:
if (lua_iscfunction(L, index))
stream << "cfunction@" << lua_topointer(L, index);
else
stream << "function@" << lua_topointer(L, index);
break;
case LUA_TTHREAD:
stream << "thread@" << lua_tothread(L, index);
break;
case LUA_TLIGHTUSERDATA:
stream << "lightuserdata@" << lua_touserdata(L, index);
break;
case LUA_TTABLE:
dumpTable(L, index, maxDepth, level, false, stream);
break;
case LUA_TUSERDATA:
stream << "userdata@" << lua_touserdata(L, index);
break;
default:
stream << lua_typename(L, type);
break;
}
if (newLine)
stream << '\n';
}
//=================================================================================================
/**
* @brief Dump a lua table on the stack.
*/
inline void dumpTable(lua_State* L, int index, unsigned maxDepth, unsigned level, bool newLine, std::ostream& stream)
{
stream << "table@" << lua_topointer(L, index);
if (level > maxDepth)
{
if (newLine)
stream << '\n';
return;
}
index = lua_absindex(L, index);
stream << " {";
int valuesCount = 0;
lua_pushnil(L); // Initial key
while (lua_next(L, index))
{
stream << '\n';
detail::putIndent(stream, level + 1);
dumpValue(L, -2, maxDepth, level + 1, false, stream); // Key
stream << ": ";
dumpValue(L, -1, maxDepth, level + 1, false, stream); // Value
stream << ",";
lua_pop(L, 1); // Value
++valuesCount;
}
if (valuesCount > 0)
{
stream << '\n';
detail::putIndent(stream, level);
}
stream << "}";
if (newLine)
stream << '\n';
}
//=================================================================================================
/**
* @brief Dump the current stack, optionally recursively.
*/
inline void dumpState(lua_State* L, unsigned maxDepth = 1, std::ostream& stream = std::cerr)
{
stream << "----------------------------------------------" << '\n';
int top = lua_gettop(L);
for (int i = 1; i <= top; ++i)
{
stream << "stack #" << i << " (" << -(top - i + 1) << "): ";
dumpValue(L, i, maxDepth, 0, true, stream);
}
}
} // namespace luabridge
+89
View File
@@ -0,0 +1,89 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "detail/Stack.h"
#if LUABRIDGE_HAS_CXX23_FLAT_CONTAINERS
#include <flat_map>
namespace luabridge {
//=================================================================================================
/**
* @brief Stack specialization for `std::flat_map`.
*/
template <class K, class V, class Compare, class KeyContainer, class MappedContainer>
struct Stack<std::flat_map<K, V, Compare, KeyContainer, MappedContainer>>
{
using Type = std::flat_map<K, V, Compare, KeyContainer, MappedContainer>;
[[nodiscard]] static Result push(lua_State* L, const Type& map)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, 0, static_cast<int>(map.size()));
for (auto it = map.begin(); it != map.end(); ++it)
{
auto result = Stack<K>::push(L, it->first);
if (! result)
return result;
result = Stack<V>::push(L, it->second);
if (! result)
return result;
lua_settable(L, -3);
}
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (!lua_istable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
const StackRestore stackRestore(L);
Type map;
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
while (lua_next(L, absIndex) != 0)
{
auto value = Stack<V>::get(L, -1);
if (! value)
return makeErrorCode(ErrorCode::InvalidTypeCast);
auto key = Stack<K>::get(L, -2);
if (! key)
return makeErrorCode(ErrorCode::InvalidTypeCast);
map.emplace(*key, *value);
lua_pop(L, 1);
}
return map;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_istable(L, index);
}
};
} // namespace luabridge
#endif // LUABRIDGE_HAS_CXX23_FLAT_CONTAINERS
+84
View File
@@ -0,0 +1,84 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "detail/Stack.h"
#if LUABRIDGE_HAS_CXX23_FLAT_CONTAINERS
#include <flat_set>
namespace luabridge {
//=================================================================================================
/**
* @brief Stack specialization for `std::flat_set`.
*/
template <class K, class Compare, class Container>
struct Stack<std::flat_set<K, Compare, Container>>
{
using Type = std::flat_set<K, Compare, Container>;
[[nodiscard]] static Result push(lua_State* L, const Type& set)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, 0, static_cast<int>(set.size()));
auto it = set.cbegin();
for (lua_Integer tableIndex = 1; it != set.cend(); ++tableIndex, ++it)
{
lua_pushinteger(L, tableIndex);
auto result = Stack<K>::push(L, *it);
if (! result)
return result;
lua_settable(L, -3);
}
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (!lua_istable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
const StackRestore stackRestore(L);
Type set;
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
while (lua_next(L, absIndex) != 0)
{
auto item = Stack<K>::get(L, -1);
if (! item)
return makeErrorCode(ErrorCode::InvalidTypeCast);
set.emplace(*item);
lua_pop(L, 1);
}
return set;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_istable(L, index);
}
};
} // namespace luabridge
#endif // LUABRIDGE_HAS_CXX23_FLAT_CONTAINERS
+82
View File
@@ -0,0 +1,82 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2026, kunitoki
// Copyright 2020, Dmitry Tarakanov
// SPDX-License-Identifier: MIT
#pragma once
#include "detail/Stack.h"
#include <forward_list>
namespace luabridge {
//=================================================================================================
/**
* @brief Stack specialization for `std::forward_list`.
*/
template <class T, class Allocator>
struct Stack<std::forward_list<T, Allocator>>
{
using Type = std::forward_list<T, Allocator>;
[[nodiscard]] static Result push(lua_State* L, const Type& list)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, 0, 0);
auto it = list.cbegin();
for (std::size_t tableIndex = 1; it != list.cend(); ++tableIndex, ++it)
{
lua_pushinteger(L, static_cast<int>(tableIndex));
auto result = Stack<T>::push(L, *it);
if (! result)
return result;
lua_settable(L, -3);
}
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (!lua_istable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
const StackRestore stackRestore(L);
Type list;
auto insertPos = list.before_begin();
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
while (lua_next(L, absIndex) != 0)
{
auto item = Stack<T>::get(L, -1);
if (! item)
return makeErrorCode(ErrorCode::InvalidTypeCast);
insertPos = list.insert_after(insertPos, *item);
lua_pop(L, 1);
}
return list;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_istable(L, index);
}
};
} // namespace luabridge
+80
View File
@@ -0,0 +1,80 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2020, Dmitry Tarakanov
// SPDX-License-Identifier: MIT
#pragma once
#include "detail/Stack.h"
#include <list>
namespace luabridge {
//=================================================================================================
/**
* @brief Stack specialization for `std::array`.
*/
template <class T, class Allocator>
struct Stack<std::list<T, Allocator>>
{
using Type = std::list<T, Allocator>;
[[nodiscard]] static Result push(lua_State* L, const Type& list)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, static_cast<int>(list.size()), 0);
const int tableIndex = lua_gettop(L);
auto it = list.cbegin();
for (std::size_t i = 1; it != list.cend(); ++i, ++it)
{
auto result = Stack<T>::push(L, *it);
if (! result)
return result;
lua_rawseti(L, tableIndex, static_cast<int>(i));
}
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (!lua_istable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
const StackRestore stackRestore(L);
Type list;
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
while (lua_next(L, absIndex) != 0)
{
auto item = Stack<T>::get(L, -1);
if (! item)
return makeErrorCode(ErrorCode::InvalidTypeCast);
list.emplace_back(*item);
lua_pop(L, 1);
}
return list;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_istable(L, index);
}
};
} // namespace luabridge
+40
View File
@@ -0,0 +1,40 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2020, Dmitry Tarakanov
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// Copyright 2007, Nathan Reed
// SPDX-License-Identifier: MIT
#pragma once
// All #include dependencies are listed here
// instead of in the individual header files.
#define LUABRIDGE_MAJOR_VERSION 3
#define LUABRIDGE_MINOR_VERSION 1
#define LUABRIDGE_VERSION 301
#include "detail/Config.h"
#include "detail/CFunctions.h"
#include "detail/ClassInfo.h"
#include "detail/Coroutine.h"
#include "detail/Enum.h"
#include "detail/Errors.h"
#include "detail/Expected.h"
#include "detail/FlagSet.h"
#include "detail/FuncTraits.h"
#include "detail/Globals.h"
#include "detail/Invoke.h"
#include "detail/Iterator.h"
#include "detail/LuaException.h"
#include "detail/LuaHelpers.h"
#include "detail/LuaRef.h"
#include "detail/Namespace.h"
#include "detail/Options.h"
#include "detail/Overload.h"
#include "detail/Result.h"
#include "detail/ScopeGuard.h"
#include "detail/Stack.h"
#include "detail/TypeTraits.h"
#include "detail/Userdata.h"
+86
View File
@@ -0,0 +1,86 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2018, Dmitry Tarakanov
// SPDX-License-Identifier: MIT
#pragma once
#include "detail/Stack.h"
#include <map>
namespace luabridge {
//=================================================================================================
/**
* @brief Stack specialization for `std::map`.
*/
template <class K, class V, class Compare, class Allocator>
struct Stack<std::map<K, V, Compare, Allocator>>
{
using Type = std::map<K, V, Compare, Allocator>;
[[nodiscard]] static Result push(lua_State* L, const Type& map)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, 0, static_cast<int>(map.size()));
for (auto it = map.begin(); it != map.end(); ++it)
{
auto result = Stack<K>::push(L, it->first);
if (! result)
return result;
result = Stack<V>::push(L, it->second);
if (! result)
return result;
lua_settable(L, -3);
}
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (!lua_istable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
const StackRestore stackRestore(L);
Type map;
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
while (lua_next(L, absIndex) != 0)
{
auto value = Stack<V>::get(L, -1);
if (! value)
return makeErrorCode(ErrorCode::InvalidTypeCast);
auto key = Stack<K>::get(L, -2);
if (! key)
return makeErrorCode(ErrorCode::InvalidTypeCast);
map.emplace(*key, *value);
lua_pop(L, 1);
}
return map;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_istable(L, index);
}
};
} // namespace luabridge
+107
View File
@@ -0,0 +1,107 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2026, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "detail/Stack.h"
#include <map>
namespace luabridge {
//=================================================================================================
/**
* @brief Stack specialization for `std::multimap`.
*/
template <class K, class V, class Compare, class Allocator>
struct Stack<std::multimap<K, V, Compare, Allocator>>
{
using Type = std::multimap<K, V, Compare, Allocator>;
[[nodiscard]] static Result push(lua_State* L, const Type& map)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, 0, 0);
auto it = map.begin();
while (it != map.end())
{
auto result = Stack<K>::push(L, it->first);
if (! result)
return result;
auto range = map.equal_range(it->first);
lua_createtable(L, static_cast<int>(std::distance(range.first, range.second)), 0);
int innerIndex = 1;
for (auto innerIt = range.first; innerIt != range.second; ++innerIt, ++innerIndex)
{
result = Stack<V>::push(L, innerIt->second);
if (! result)
return result;
lua_rawseti(L, -2, innerIndex);
}
lua_settable(L, -3);
it = range.second;
}
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (! lua_istable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
const StackRestore stackRestore(L);
Type map;
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
while (lua_next(L, absIndex) != 0)
{
auto key = Stack<K>::get(L, -2);
if (! key)
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (! lua_istable(L, -1))
return makeErrorCode(ErrorCode::InvalidTypeCast);
int innerAbsIndex = lua_absindex(L, -1);
lua_pushnil(L);
while (lua_next(L, innerAbsIndex) != 0)
{
auto value = Stack<V>::get(L, -1);
if (! value)
return makeErrorCode(ErrorCode::InvalidTypeCast);
map.emplace(*key, *value);
lua_pop(L, 1);
}
lua_pop(L, 1);
}
return map;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_istable(L, index);
}
};
} // namespace luabridge
+80
View File
@@ -0,0 +1,80 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "detail/Stack.h"
#include <set>
namespace luabridge {
//=================================================================================================
/**
* @brief Stack specialization for `std::set`.
*/
template <class K, class Compare, class Allocator>
struct Stack<std::set<K, Compare, Allocator>>
{
using Type = std::set<K, Compare, Allocator>;
[[nodiscard]] static Result push(lua_State* L, const Type& set)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, 0, static_cast<int>(set.size()));
auto it = set.cbegin();
for (lua_Integer tableIndex = 1; it != set.cend(); ++tableIndex, ++it)
{
lua_pushinteger(L, tableIndex);
auto result = Stack<K>::push(L, *it);
if (! result)
return result;
lua_settable(L, -3);
}
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (!lua_istable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
const StackRestore stackRestore(L);
Type set;
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
while (lua_next(L, absIndex) != 0)
{
auto item = Stack<K>::get(L, -1);
if (! item)
return makeErrorCode(ErrorCode::InvalidTypeCast);
set.emplace(*item);
lua_pop(L, 1);
}
return set;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_istable(L, index);
}
};
} // namespace luabridge
+66
View File
@@ -0,0 +1,66 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2026, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "detail/Stack.h"
#if LUABRIDGE_HAS_CXX20_SPAN
#include <span>
namespace luabridge {
//=================================================================================================
/**
* @brief Stack specialization for `std::span` (push-only).
*/
template <class T, std::size_t Extent>
struct Stack<std::span<T, Extent>>
{
using Type = std::span<T, Extent>;
[[nodiscard]] static Result push(lua_State* L, Type span)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, static_cast<int>(span.size()), 0);
const int tableIndex = lua_gettop(L);
int i = 1;
for (const auto& element : span)
{
auto result = Stack<std::remove_cv_t<T>>::push(L, element);
if (! result)
return result;
lua_rawseti(L, tableIndex, i++);
}
stackRestore.reset();
return {};
}
template <class U = T>
[[nodiscard]] static TypeResult<Type> get(lua_State*, int)
{
static_assert(sizeof(U) == 0,
"std::span cannot be retrieved from Lua — use std::vector<T> to retrieve sequences from Lua");
return makeErrorCode(ErrorCode::InvalidTypeCast);
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_istable(L, index);
}
};
} // namespace luabridge
#endif // LUABRIDGE_HAS_CXX20_SPAN
+69
View File
@@ -0,0 +1,69 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "detail/Stack.h"
#if LUABRIDGE_HAS_CXX23_EXPECTED
#include <expected>
namespace luabridge {
//=================================================================================================
/**
* @brief Stack specialization for `std::expected` (C++23).
*/
template <class T, class E>
struct Stack<std::expected<T, E>>
{
using Type = std::expected<T, E>;
[[nodiscard]] static Result push(lua_State* L, const Type& value)
{
if (value.has_value())
{
StackRestore stackRestore(L);
auto result = Stack<T>::push(L, *value);
if (! result)
return result;
stackRestore.reset();
return {};
}
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
lua_pushnil(L);
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
const auto type = lua_type(L, index);
if (type == LUA_TNIL || type == LUA_TNONE)
return makeErrorCode(ErrorCode::InvalidTypeCast);
auto result = Stack<T>::get(L, index);
if (! result)
return result.error();
return Type(*result);
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
const auto type = lua_type(L, index);
return (type != LUA_TNIL && type != LUA_TNONE) && Stack<T>::isInstance(L, index);
}
};
} // namespace luabridge
#endif // LUABRIDGE_HAS_CXX23_EXPECTED
+86
View File
@@ -0,0 +1,86 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2019, Dmitry Tarakanov
// SPDX-License-Identifier: MIT
#pragma once
#include "detail/Stack.h"
#include <unordered_map>
namespace luabridge {
//=================================================================================================
/**
* @brief Stack specialization for `std::unordered_map`.
*/
template <class K, class V, class Hash, class KeyEqual, class Allocator>
struct Stack<std::unordered_map<K, V, Hash, KeyEqual, Allocator>>
{
using Type = std::unordered_map<K, V, Hash, KeyEqual, Allocator>;
[[nodiscard]] static Result push(lua_State* L, const Type& map)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, 0, static_cast<int>(map.size()));
for (auto it = map.begin(); it != map.end(); ++it)
{
auto result = Stack<K>::push(L, it->first);
if (! result)
return result;
result = Stack<V>::push(L, it->second);
if (! result)
return result;
lua_settable(L, -3);
}
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (!lua_istable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
const StackRestore stackRestore(L);
Type map;
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
while (lua_next(L, absIndex) != 0)
{
auto value = Stack<V>::get(L, -1);
if (! value)
return makeErrorCode(ErrorCode::InvalidTypeCast);
auto key = Stack<K>::get(L, -2);
if (! key)
return makeErrorCode(ErrorCode::InvalidTypeCast);
map.emplace(*key, *value);
lua_pop(L, 1);
}
return map;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_istable(L, index);
}
};
} // namespace luabridge
+107
View File
@@ -0,0 +1,107 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2026, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "detail/Stack.h"
#include <unordered_map>
namespace luabridge {
//=================================================================================================
/**
* @brief Stack specialization for `std::unordered_multimap`.
*/
template <class K, class V, class Hash, class KeyEqual, class Allocator>
struct Stack<std::unordered_multimap<K, V, Hash, KeyEqual, Allocator>>
{
using Type = std::unordered_multimap<K, V, Hash, KeyEqual, Allocator>;
[[nodiscard]] static Result push(lua_State* L, const Type& map)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, 0, 0);
auto it = map.begin();
while (it != map.end())
{
auto result = Stack<K>::push(L, it->first);
if (! result)
return result;
auto range = map.equal_range(it->first);
lua_createtable(L, static_cast<int>(std::distance(range.first, range.second)), 0);
int innerIndex = 1;
for (auto innerIt = range.first; innerIt != range.second; ++innerIt, ++innerIndex)
{
result = Stack<V>::push(L, innerIt->second);
if (! result)
return result;
lua_rawseti(L, -2, innerIndex);
}
lua_settable(L, -3);
it = range.second;
}
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (! lua_istable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
const StackRestore stackRestore(L);
Type map;
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
while (lua_next(L, absIndex) != 0)
{
auto key = Stack<K>::get(L, -2);
if (! key)
return makeErrorCode(ErrorCode::InvalidTypeCast);
if (! lua_istable(L, -1))
return makeErrorCode(ErrorCode::InvalidTypeCast);
int innerAbsIndex = lua_absindex(L, -1);
lua_pushnil(L);
while (lua_next(L, innerAbsIndex) != 0)
{
auto value = Stack<V>::get(L, -1);
if (! value)
return makeErrorCode(ErrorCode::InvalidTypeCast);
map.emplace(*key, *value);
lua_pop(L, 1);
}
lua_pop(L, 1);
}
return map;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_istable(L, index);
}
};
} // namespace luabridge
+80
View File
@@ -0,0 +1,80 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2026, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "detail/Stack.h"
#include <unordered_set>
namespace luabridge {
//=================================================================================================
/**
* @brief Stack specialization for `std::unordered_set`.
*/
template <class K, class Hash, class KeyEqual, class Allocator>
struct Stack<std::unordered_set<K, Hash, KeyEqual, Allocator>>
{
using Type = std::unordered_set<K, Hash, KeyEqual, Allocator>;
[[nodiscard]] static Result push(lua_State* L, const Type& set)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, 0, static_cast<int>(set.size()));
auto it = set.cbegin();
for (lua_Integer tableIndex = 1; it != set.cend(); ++tableIndex, ++it)
{
lua_pushinteger(L, tableIndex);
auto result = Stack<K>::push(L, *it);
if (! result)
return result;
lua_settable(L, -3);
}
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (!lua_istable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
const StackRestore stackRestore(L);
Type set;
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
while (lua_next(L, absIndex) != 0)
{
auto item = Stack<K>::get(L, -1);
if (! item)
return makeErrorCode(ErrorCode::InvalidTypeCast);
set.emplace(*item);
lua_pop(L, 1);
}
return set;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_istable(L, index);
}
};
} // namespace luabridge
+94
View File
@@ -0,0 +1,94 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2026, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "detail/Stack.h"
#include <utility>
#include <variant>
namespace luabridge {
//=================================================================================================
/**
* @brief Stack specialization for `std::variant`.
*/
template <class... Args>
struct Stack<std::variant<Args...>>
{
using Type = std::variant<Args...>;
[[nodiscard]] static Result push(lua_State* L, const Type& variant)
{
return std::visit([L](const auto& value) { return Stack<std::decay_t<decltype(value)>>::push(L, value); }, variant);
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
return tryGet<Args...>(L, index);
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return (Stack<Args>::isInstance(L, index) || ...);
}
private:
template <class T, class... Rest>
[[nodiscard]] static TypeResult<Type> tryGet(lua_State* L, int index)
{
if (auto value = Stack<T>::get(L, index))
{
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
return Type{ std::in_place_type<T>, std::move(*value) };
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
}
if constexpr (sizeof...(Rest) > 0)
return tryGet<Rest...>(L, index);
return makeErrorCode(ErrorCode::InvalidTypeCast);
}
};
/**
* @brief Stack specialization for `std::monostate`.
*/
template <>
struct Stack<std::monostate>
{
using Type = std::monostate;
[[nodiscard]] static Result push(lua_State* L, const Type&)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 1))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
lua_pushnil(L);
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (lua_isnoneornil(L, index))
return Type{};
return makeErrorCode(ErrorCode::InvalidTypeCast);
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_isnoneornil(L, index);
}
};
} // namespace luabridge
+80
View File
@@ -0,0 +1,80 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2018, Dmitry Tarakanov
// SPDX-License-Identifier: MIT
#pragma once
#include "detail/Stack.h"
#include <vector>
namespace luabridge {
//=================================================================================================
/**
* @brief Stack specialization for `std::vector`.
*/
template <class T, class Allocator>
struct Stack<std::vector<T, Allocator>>
{
using Type = std::vector<T, Allocator>;
[[nodiscard]] static Result push(lua_State* L, const Type& vector)
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(L, 3))
return makeErrorCode(ErrorCode::LuaStackOverflow);
#endif
StackRestore stackRestore(L);
lua_createtable(L, static_cast<int>(vector.size()), 0);
const int tableIndex = lua_gettop(L);
for (std::size_t i = 0; i < vector.size(); ++i)
{
auto result = Stack<T>::push(L, vector[i]);
if (! result)
return result;
lua_rawseti(L, tableIndex, static_cast<int>(i + 1));
}
stackRestore.reset();
return {};
}
[[nodiscard]] static TypeResult<Type> get(lua_State* L, int index)
{
if (!lua_istable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
const StackRestore stackRestore(L);
Type vector;
vector.reserve(static_cast<std::size_t>(get_length(L, index)));
int absIndex = lua_absindex(L, index);
lua_pushnil(L);
while (lua_next(L, absIndex) != 0)
{
auto item = Stack<T>::get(L, -1);
if (! item)
return makeErrorCode(ErrorCode::InvalidTypeCast);
vector.emplace_back(*item);
lua_pop(L, 1);
}
return vector;
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_istable(L, index);
}
};
} // namespace luabridge
File diff suppressed because it is too large Load Diff
+259
View File
@@ -0,0 +1,259 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2020, Dmitry Tarakanov
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include <cstdint>
#include <memory>
#include <string_view>
#if defined __clang__ || defined __GNUC__
#define LUABRIDGE_PRETTY_FUNCTION __PRETTY_FUNCTION__
#define LUABRIDGE_PRETTY_FUNCTION_PREFIX '='
#define LUABRIDGE_PRETTY_FUNCTION_SUFFIX ']'
#elif defined _MSC_VER
#define LUABRIDGE_PRETTY_FUNCTION __FUNCSIG__
#define LUABRIDGE_PRETTY_FUNCTION_PREFIX '<'
#define LUABRIDGE_PRETTY_FUNCTION_SUFFIX '>'
#endif
namespace luabridge {
namespace detail {
[[nodiscard]] constexpr auto fnv1a(const char* s, std::size_t count) noexcept
{
uint32_t seed = 2166136261u;
for (std::size_t i = 0; i < count; ++i)
seed = static_cast<uint32_t>(static_cast<uint32_t>(seed ^ static_cast<uint8_t>(*s++)) * 16777619u);
if constexpr (sizeof(void*) == 8)
return static_cast<uint64_t>(seed);
else
return seed;
}
template <class T>
[[nodiscard]] static constexpr auto typeName(T* = nullptr) noexcept
{
constexpr std::string_view prettyName{ LUABRIDGE_PRETTY_FUNCTION };
constexpr auto first = prettyName.find_first_not_of(' ', prettyName.find_first_of(LUABRIDGE_PRETTY_FUNCTION_PREFIX) + 1);
return prettyName.substr(first, prettyName.find_last_of(LUABRIDGE_PRETTY_FUNCTION_SUFFIX) - first);
}
template <class T, auto = typeName<T>().find_first_of('.')>
[[nodiscard]] static constexpr auto typeHash(T* = nullptr) noexcept
{
constexpr auto stripped = typeName<T>();
return fnv1a(stripped.data(), stripped.size());
}
//=================================================================================================
/**
* @brief A unique key for the exceptions in the registry.
*/
[[nodiscard]] inline void* getExceptionsKey() noexcept
{
return reinterpret_cast<void*>(0xc7);
}
//=================================================================================================
/**
* @brief A unique key for a type name in a metatable.
*/
[[nodiscard]] inline const void* getTypeKey() noexcept
{
return reinterpret_cast<void*>(0x71);
}
//=================================================================================================
/**
* @brief The key of a const table in another metatable.
*/
[[nodiscard]] inline const void* getConstKey() noexcept
{
return reinterpret_cast<void*>(0xc07);
}
//=================================================================================================
/**
* @brief The key of a class table in another metatable.
*/
[[nodiscard]] inline const void* getClassKey() noexcept
{
return reinterpret_cast<void*>(0xc1a);
}
//=================================================================================================
/**
* @brief The key of a class options table in another metatable.
*/
[[nodiscard]] inline const void* getClassOptionsKey() noexcept
{
return reinterpret_cast<void*>(0xc2b);
}
//=================================================================================================
/**
* @brief The key of a type identity tag in class/const metatables.
*/
[[nodiscard]] inline const void* getTypeIdentityKey() noexcept
{
return reinterpret_cast<void*>(0xc2c);
}
//=================================================================================================
/**
* @brief The key of a propget table in another metatable.
*/
[[nodiscard]] inline const void* getPropgetKey() noexcept
{
return reinterpret_cast<void*>(0x6e7);
}
//=================================================================================================
/**
* @brief The key of a propset table in another metatable.
*/
[[nodiscard]] inline const void* getPropsetKey() noexcept
{
return reinterpret_cast<void*>(0x5e7);
}
//=================================================================================================
/**
* @brief The key of a static table in another metatable.
*/
[[nodiscard]] inline const void* getStaticKey() noexcept
{
return reinterpret_cast<void*>(0x57a);
}
//=================================================================================================
/**
* @brief The key of a parent table in another metatable.
*/
[[nodiscard]] inline const void* getParentKey() noexcept
{
return reinterpret_cast<void*>(0xdad);
}
//=================================================================================================
/**
* @brief The key of a cast offset table in a derived class metatable.
*
* Maps base class registry keys to byte offsets for pointer adjustment when converting
* a derived class pointer to a base class pointer in multiple inheritance scenarios.
*/
[[nodiscard]] inline const void* getCastTableKey() noexcept
{
return reinterpret_cast<void*>(0xca57);
}
//=================================================================================================
/**
* The key of the index fall back in another metatable.
*/
[[nodiscard]] inline const void* getIndexFallbackKey()
{
return reinterpret_cast<void*>(0x81ca);
}
[[nodiscard]] inline const void* getIndexExtensibleKey()
{
return reinterpret_cast<void*>(0x81cb);
}
//=================================================================================================
/**
* The key of the new index fall back in another metatable.
*/
[[nodiscard]] inline const void* getNewIndexFallbackKey()
{
return reinterpret_cast<void*>(0x8107);
}
[[nodiscard]] inline const void* getNewIndexExtensibleKey()
{
return reinterpret_cast<void*>(0x8108);
}
//=================================================================================================
/**
* @brief The key of a ConverterRegistry userdata in a class metatable.
*/
[[nodiscard]] inline const void* getConvertersKey() noexcept
{
return reinterpret_cast<void*>(0xc0de);
}
//=================================================================================================
/**
* The key of the static index fall back in another metatable.
*/
[[nodiscard]] inline const void* getStaticIndexFallbackKey()
{
return reinterpret_cast<void*>(0x81cc);
}
//=================================================================================================
/**
* The key of the static new index fall back in another metatable.
*/
[[nodiscard]] inline const void* getStaticNewIndexFallbackKey()
{
return reinterpret_cast<void*>(0x8109);
}
//=================================================================================================
/**
* @brief Get the key for the static table in the Lua registry.
*
* The static table holds the static data members, static properties, and static member functions for a class.
*/
template <class T>
[[nodiscard]] const void* getStaticRegistryKey() noexcept
{
static auto value = typeHash<T>();
return reinterpret_cast<void*>(value);
}
//=================================================================================================
/**
* @brief Get the key for the class table in the Lua registry.
*
* The class table holds the data members, properties, and member functions of a class. Read-only data and properties, and const
* member functions are also placed here (to save a lookup in the const table).
*/
template <class T>
[[nodiscard]] const void* getClassRegistryKey() noexcept
{
static auto value = typeHash<T>() ^ 1;
return reinterpret_cast<void*>(value);
}
//=================================================================================================
/**
* @brief Get the key for the const table in the Lua registry.
*
* The const table holds read-only data members and properties, and const member functions of a class.
*/
template <class T>
[[nodiscard]] const void* getConstRegistryKey() noexcept
{
static auto value = typeHash<T>() ^ 2;
return reinterpret_cast<void*>(value);
}
} // namespace detail
} // namespace luabridge
+258
View File
@@ -0,0 +1,258 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2020, Dmitry Tarakanov
// Copyright 2019, George Tokmaji
// SPDX-License-Identifier: MIT
#pragma once
#include <cassert>
#if __has_include(<version>)
#include <version>
#endif
#if !(__cplusplus >= 201703L || (defined(_MSC_VER) && _HAS_CXX17))
#error LuaBridge 3 requires a compliant C++17 compiler, or C++17 has not been enabled !
#endif
#if __cplusplus >= 202302L || (defined(_MSC_VER) && _HAS_CXX23)
#define LUABRIDGE_CXX23_OR_GREATER 1
#elif __cplusplus >= 202002L || (defined(_MSC_VER) && _HAS_CXX20)
#define LUABRIDGE_CXX20_OR_GREATER 1
#endif
#if defined(LUAU_FASTMATH_BEGIN)
#define LUABRIDGE_ON_LUAU 1
#elif defined(LUAJIT_VERSION)
#define LUABRIDGE_ON_LUAJIT 1
#elif defined(RAVI_OPTION_STRING2)
#define LUABRIDGE_ON_RAVI 1
#elif defined(LUA_VERSION_NUM)
#define LUABRIDGE_ON_LUA 1
#else
#error "Lua headers must be included prior to LuaBridge ones"
#endif
#if !defined(LUABRIDGE_HAS_EXCEPTIONS)
#if defined(_MSC_VER)
#if _CPPUNWIND || _HAS_EXCEPTIONS
#define LUABRIDGE_HAS_EXCEPTIONS 1
#else
#define LUABRIDGE_HAS_EXCEPTIONS 0
#endif
#elif defined(__clang__)
#if __EXCEPTIONS && __has_feature(cxx_exceptions)
#define LUABRIDGE_HAS_EXCEPTIONS 1
#else
#define LUABRIDGE_HAS_EXCEPTIONS 0
#endif
#elif defined(__GNUC__)
#if defined(__cpp_exceptions) || defined(__EXCEPTIONS)
#define LUABRIDGE_HAS_EXCEPTIONS 1
#else
#define LUABRIDGE_HAS_EXCEPTIONS 0
#endif
#endif
#endif
#if LUABRIDGE_HAS_EXCEPTIONS
#define LUABRIDGE_IF_EXCEPTIONS(...) __VA_ARGS__
#define LUABRIDGE_IF_NO_EXCEPTIONS(...)
#else
#define LUABRIDGE_IF_EXCEPTIONS(...)
#define LUABRIDGE_IF_NO_EXCEPTIONS(...) __VA_ARGS__
#endif
#if defined(__clang__) || defined(__GNUC__)
#define LUABRIDGE_NO_SANITIZE(x) __attribute__((no_sanitize(x)))
#else
#define LUABRIDGE_NO_SANITIZE(x)
#endif
#if defined(__OBJC__)
#define LUABRIDGE_ON_OBJECTIVE_C 1
#endif
/**
* @brief Enable safe stack checks to avoid lua stack overflow when pushing values on the stack.
*
* @note Default is enabled.
*/
#if !defined(LUABRIDGE_SAFE_STACK_CHECKS)
#define LUABRIDGE_SAFE_STACK_CHECKS 1
#endif
/**
* @brief Enable strict stack conversions to enforce exact type matching when getting values from the stack.
*
* When enabled:
* - `Stack<bool>::get` only accepts `LUA_TBOOLEAN` (nil is not convertible to bool).
* - Integer `Stack` specializations only accept Lua integer values (not floats with integer representation, on Lua 5.3+).
* - `Stack<std::string>::get` only accepts `LUA_TSTRING` (numbers are not coerced to strings).
*
* When disabled (default), a more permissive conversion is used:
* - `Stack<bool>::get` accepts `LUA_TBOOLEAN` and `LUA_TNIL` (nil converts to false).
* - Integer `Stack` specializations accept any `LUA_TNUMBER` that can be represented as the target integer type.
* - `Stack<std::string>::get` accepts `LUA_TSTRING` and `LUA_TNUMBER` (numbers are coerced to strings).
*
* @note Default is disabled.
*/
#if !defined(LUABRIDGE_STRICT_STACK_CONVERSIONS)
#define LUABRIDGE_STRICT_STACK_CONVERSIONS 0
#endif
/**
* @brief Enable safe exception handling when lua is compiled as `C` and exceptions raise during execution of registered `lua_CFunction`.
*
* This is a problem that manifests when exceptions are leaking a CFunction when lua is compiled as `C` because the library will then longjmp
* instead of correctly unwinding the exception into C++ land. If you have exceptions enabled and are compiling lua as `C` and you are getting random
* crashes when invoking CFunctions that throw, you have two options: or you catch exceptions in your CFunction and raise a `lua_error` instead
* or you enable this macro, which will add a safe indirection doing exceptions catching and raising when invoking your registered CFunction.
*
* @warning When enabled, some performance degradation is to be expected when invoking registered `lua_CFunction` through the library.
*
* @note Default is disabled, can only be enabled when `LUABRIDGE_HAS_EXCEPTIONS` is 1.
*/
#if !defined(LUABRIDGE_SAFE_LUA_C_EXCEPTION_HANDLING)
#define LUABRIDGE_SAFE_LUA_C_EXCEPTION_HANDLING 0
#endif
/**
* @brief Control raising when an unregistered class is used.
*
* @note Default is enabled when exceptions are enabled, disabled otherwise.
*/
#if !defined(LUABRIDGE_RAISE_UNREGISTERED_CLASS_USAGE)
#if LUABRIDGE_HAS_EXCEPTIONS
#define LUABRIDGE_RAISE_UNREGISTERED_CLASS_USAGE 1
#else
#define LUABRIDGE_RAISE_UNREGISTERED_CLASS_USAGE 0
#endif
#endif
/**
* @brief Control the assertion mechanism used by the library.
*
* @note By default, assertions are enabled in debug builds and disabled in release builds. Define LUABRIDGE_FORCE_ASSERT_RELEASE to enable assertions even in release builds.
*/
#if !defined(LUABRIDGE_ASSERT)
#if defined(NDEBUG) && !defined(LUABRIDGE_FORCE_ASSERT_RELEASE)
#define LUABRIDGE_ASSERT(expr) ((void)(expr))
#else
#define LUABRIDGE_ASSERT(expr) assert(expr)
#endif
#endif
/**
* @brief Enable C++17 filesystem library support.
*
* Requires C++17 and the filesystem header to be available.
* Define LUABRIDGE_DISABLE_CXX17_FILESYSTEM to force-disable even when available.
*/
#if !defined(LUABRIDGE_HAS_CXX17_FILESYSTEM)
#if !defined(LUABRIDGE_DISABLE_CXX17_FILESYSTEM) && __has_include(<filesystem>) && defined(__cpp_lib_filesystem)
#define LUABRIDGE_HAS_CXX17_FILESYSTEM 1
#else
#define LUABRIDGE_HAS_CXX17_FILESYSTEM 0
#endif
#endif
/**
* @brief Enable C++17 any library support.
*
* Requires C++17 and the any header to be available.
* Define LUABRIDGE_DISABLE_CXX17_ANY to force-disable even when available.
*/
#if !defined(LUABRIDGE_HAS_CXX17_ANY)
#if !defined(LUABRIDGE_DISABLE_CXX17_ANY) && __has_include(<any>) && defined(__cpp_lib_any)
#define LUABRIDGE_HAS_CXX17_ANY 1
#else
#define LUABRIDGE_HAS_CXX17_ANY 0
#endif
#endif
/**
* @brief Enable C++20 span library support.
*
* Requires C++20 and the span header to be available.
* Define LUABRIDGE_DISABLE_CXX20_SPAN to force-disable even when available.
*/
#if !defined(LUABRIDGE_HAS_CXX20_SPAN)
#if !defined(LUABRIDGE_DISABLE_CXX20_SPAN) && LUABRIDGE_CXX20_OR_GREATER && __has_include(<span>) && defined(__cpp_lib_span)
#define LUABRIDGE_HAS_CXX20_SPAN 1
#else
#define LUABRIDGE_HAS_CXX20_SPAN 0
#endif
#endif
/**
* @brief Enable C++20 ranges library support.
*
* Requires C++20 and the ranges header to be available.
* Define LUABRIDGE_DISABLE_CXX20_RANGES to force-disable even when available.
*/
#if !defined(LUABRIDGE_HAS_CXX20_RANGES)
#if !defined(LUABRIDGE_DISABLE_CXX20_RANGES) && LUABRIDGE_CXX20_OR_GREATER && defined(__cpp_lib_ranges)
#define LUABRIDGE_HAS_CXX20_RANGES 1
#else
#define LUABRIDGE_HAS_CXX20_RANGES 0
#endif
#endif
/**
* @brief Enable C++20 coroutine integration with Lua coroutines.
*
* Requires C++20 and Lua 5.2+ (lua_yieldk). Not supported on Lua 5.1, LuaJIT, or Luau.
* Define LUABRIDGE_DISABLE_CXX20_COROUTINES to force-disable even when C++20 is available.
*/
#if !defined(LUABRIDGE_HAS_CXX20_COROUTINES)
#if !defined(LUABRIDGE_DISABLE_CXX20_COROUTINES) && LUABRIDGE_CXX20_OR_GREATER && !(LUABRIDGE_ON_LUAU || LUABRIDGE_ON_LUAJIT || LUABRIDGE_ON_RAVI || LUA_VERSION_NUM < 502)
#define LUABRIDGE_HAS_CXX20_COROUTINES 1
#else
#define LUABRIDGE_HAS_CXX20_COROUTINES 0
#endif
#endif
/**
* @brief Enable C++23 expected library support.
*
* Requires C++23 and the expected header to be available.
* Define LUABRIDGE_DISABLE_CXX23_EXPECTED to force-disable even when available.
*/
#if !defined(LUABRIDGE_HAS_CXX23_EXPECTED)
#if !defined(LUABRIDGE_DISABLE_CXX23_EXPECTED) && LUABRIDGE_CXX23_OR_GREATER && __has_include(<expected>) && defined(__cpp_lib_expected)
#define LUABRIDGE_HAS_CXX23_EXPECTED 1
#else
#define LUABRIDGE_HAS_CXX23_EXPECTED 0
#endif
#endif
/**
* @brief Enable C++23 flat containers library support.
*
* Requires C++23 and the flat_map header to be available.
* Define LUABRIDGE_DISABLE_CXX23_FLAT_CONTAINERS to force-disable even when available.
*/
#if !defined(LUABRIDGE_HAS_CXX23_FLAT_CONTAINERS)
#if !defined(LUABRIDGE_DISABLE_CXX23_FLAT_CONTAINERS) && LUABRIDGE_CXX23_OR_GREATER && __has_include(<flat_map>) && __has_include(<flat_set>) && defined(__cpp_lib_flat_map)
#define LUABRIDGE_HAS_CXX23_FLAT_CONTAINERS 1
#else
#define LUABRIDGE_HAS_CXX23_FLAT_CONTAINERS 0
#endif
#endif
/**
* @brief Enable C++23 move_only_function library support.
*
* Requires C++23 and move_only_function to be available.
* Define LUABRIDGE_DISABLE_CXX23_MOVE_ONLY_FUNCTION to force-disable even when available.
*/
#if !defined(LUABRIDGE_HAS_CXX23_MOVE_ONLY_FUNCTION)
#if !defined(LUABRIDGE_DISABLE_CXX23_MOVE_ONLY_FUNCTION) && LUABRIDGE_CXX23_OR_GREATER && defined(__cpp_lib_move_only_function)
#define LUABRIDGE_HAS_CXX23_MOVE_ONLY_FUNCTION 1
#else
#define LUABRIDGE_HAS_CXX23_MOVE_ONLY_FUNCTION 0
#endif
#endif
+183
View File
@@ -0,0 +1,183 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2026, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "ClassInfo.h"
#include "Errors.h"
#include "LuaHelpers.h"
#include "Result.h"
#include <memory>
#include <optional>
#include <type_traits>
#include <unordered_map>
#include <utility>
namespace luabridge {
// Forward declaration.
template <class T, class>
struct Stack;
//=================================================================================================
/**
* @brief Opt-in trait for enabling custom type converters for type T.
*
* Specialize with enabled = true so that Stack<T>::get consults the metatable
* converter registry as a Phase 3 fallback after Phase 1 (exact match) and
* Phase 2 (inheritance) both fail.
*
* Example:
* template <> struct luabridge::StackConversion<MyType> { static constexpr bool enabled = true; };
*/
template <class T>
struct StackConversion
{
static constexpr bool enabled = false;
};
//=================================================================================================
/**
* @brief User-defined conversion hook from source type From to target type To.
*
* Specialize this template for each (To, From) pair and provide:
* static To convert(const From& from);
*
* Example:
* template <>
* struct luabridge::StackConverter<Vec3, glm::vec3> {
* static Vec3 convert(const glm::vec3& v) { return {v.x, v.y, v.z}; }
* };
*/
template <class To, class From>
struct StackConverter;
//=================================================================================================
namespace detail {
struct ConverterRegistry
{
std::unordered_map<const void*, const void*> converters;
};
inline ConverterRegistry* getOrCreateConverterRegistry(lua_State* L, int metatableIdx)
{
const int absIdx = lua_absindex(L, metatableIdx);
lua_rawgetp_x(L, absIdx, getConvertersKey());
if (lua_isuserdata(L, -1) && !lua_islightuserdata(L, -1))
{
auto* reg = align<ConverterRegistry>(lua_touserdata(L, -1));
lua_pop(L, 1);
return reg;
}
lua_pop(L, 1); // pop nil or unexpected value
// Create ConverterRegistry as an aligned Lua full userdata with automatic __gc
lua_newuserdata_aligned<ConverterRegistry>(L);
auto* reg = align<ConverterRegistry>(lua_touserdata(L, -1));
// Store the userdata in the class metatable
lua_pushvalue(L, -1); // dup userdata
lua_rawsetp_x(L, absIdx, getConvertersKey()); // store, pops dup
lua_pop(L, 1); // pop the original userdata
return reg;
}
template <class T>
class ConverterConstRef
{
public:
explicit ConverterConstRef(const T& value) noexcept
: m_ref(std::addressof(value))
{
}
explicit ConverterConstRef(T&& value) noexcept(std::is_nothrow_move_constructible_v<T>)
: m_value(std::move(value))
, m_ref(std::addressof(*m_value))
{
}
ConverterConstRef(ConverterConstRef&& other) noexcept(std::is_nothrow_move_constructible_v<T>)
: m_value(std::move(other.m_value))
, m_ref(m_value.has_value() ? std::addressof(*m_value) : other.m_ref)
{
}
ConverterConstRef(const ConverterConstRef& other)
: m_value(other.m_value)
, m_ref(m_value.has_value() ? std::addressof(*m_value) : other.m_ref)
{
}
ConverterConstRef& operator=(ConverterConstRef&& other) noexcept(std::is_nothrow_move_assignable_v<T>)
{
m_value = std::move(other.m_value);
m_ref = m_value.has_value() ? std::addressof(*m_value) : other.m_ref;
return *this;
}
ConverterConstRef& operator=(const ConverterConstRef& other)
{
m_value = other.m_value;
m_ref = m_value.has_value() ? std::addressof(*m_value) : other.m_ref;
return *this;
}
operator const T&() const noexcept
{
return *m_ref;
}
private:
std::optional<T> m_value;
const T* m_ref = nullptr;
};
template <class To>
TypeResult<To> tryConvertFromRegisteredConverter(lua_State* L, int index)
{
using FnType = TypeResult<To>(*)(lua_State*, int);
if (! lua_getmetatable(L, index))
return makeErrorCode(ErrorCode::InvalidTypeCast);
lua_rawgetp_x(L, -1, detail::getConvertersKey());
if (lua_isuserdata(L, -1) && !lua_islightuserdata(L, -1))
{
auto* reg = align<detail::ConverterRegistry>(lua_touserdata(L, -1));
lua_pop(L, 2); // registry userdata + metatable
auto it = reg->converters.find(detail::getClassRegistryKey<To>());
if (it != reg->converters.end() && it->second)
{
const auto* fn = static_cast<const FnType*>(it->second);
return (*fn)(L, index);
}
}
else
{
lua_pop(L, 2); // nil/other + metatable
}
return makeErrorCode(ErrorCode::InvalidTypeCast);
}
template <class To, class From>
TypeResult<To> convertFromStack(lua_State* L, int index)
{
auto result = detail::Userdata::get<From>(L, index, true);
if (!result || !*result)
return makeErrorCode(ErrorCode::InvalidTypeCast);
return StackConverter<To, From>::convert(**result);
}
} // namespace detail
} // namespace luabridge
+492
View File
@@ -0,0 +1,492 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2026, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include "CFunctions.h"
#include "Errors.h"
#include "LuaHelpers.h"
#include "Stack.h"
#if LUABRIDGE_HAS_CXX20_COROUTINES
#if LUABRIDGE_ON_LUAJIT || LUA_VERSION_NUM == 501 || LUABRIDGE_ON_LUAU
#ifndef LUABRIDGE_DISABLE_COROUTINE_INTEGRATION
#error "C++20 coroutine integration requires Lua 5.2+ with lua_yieldk support. Define LUABRIDGE_DISABLE_COROUTINE_INTEGRATION to suppress this error."
#endif
#else
#include <coroutine>
#include <exception>
#include <type_traits>
#include <utility>
namespace luabridge {
//=================================================================================================
/**
* @brief A C++20 coroutine type callable from Lua.
*
* Register instances via Namespace::addCoroutine(). When called from Lua, the coroutine body
* runs until the first co_yield (which yields a value back to Lua) or co_return (which
* returns a final value). Subsequent Lua resumes continue the body from the last suspension point.
*
* @tparam R The type yielded/returned by the coroutine. May be void.
*
* Example:
* @code
* luabridge::getGlobalNamespace(L)
* .addCoroutine("range", [](int start, int stop) -> luabridge::CppCoroutine<int> {
* for (int i = start; i < stop; ++i)
* co_yield i;
* co_return -1;
* });
* @endcode
*
* @note Requires Lua 5.2+ (lua_yieldk). Not supported on Lua 5.1, LuaJIT, or Luau.
* @note Not thread-safe. Must be driven from a single OS thread.
*/
template <class R>
struct CppCoroutine
{
struct promise_type
{
lua_State* L = nullptr;
int nresults = 0;
bool is_done = false;
std::exception_ptr exception;
std::suspend_always initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() noexcept
{
exception = std::current_exception();
}
std::suspend_always yield_value(const R& value)
{
nresults = 0;
if (L)
{
auto result = Stack<R>::push(L, value);
if (result)
nresults = 1;
else
exception = std::make_exception_ptr(std::system_error(result.error()));
}
return {};
}
std::suspend_always yield_value(R&& value)
{
nresults = 0;
if (L)
{
auto result = Stack<R>::push(L, std::move(value));
if (result)
nresults = 1;
else
exception = std::make_exception_ptr(std::system_error(result.error()));
}
return {};
}
void return_value(const R& value)
{
nresults = 0;
if (L)
{
auto result = Stack<R>::push(L, value);
if (result)
nresults = 1;
else
exception = std::make_exception_ptr(std::system_error(result.error()));
}
is_done = true;
}
void return_value(R&& value)
{
nresults = 0;
if (L)
{
auto result = Stack<R>::push(L, std::move(value));
if (result)
nresults = 1;
else
exception = std::make_exception_ptr(std::system_error(result.error()));
}
is_done = true;
}
CppCoroutine get_return_object()
{
return CppCoroutine{ std::coroutine_handle<promise_type>::from_promise(*this) };
}
};
std::coroutine_handle<promise_type> handle;
explicit CppCoroutine(std::coroutine_handle<promise_type> h) noexcept
: handle(h)
{
}
CppCoroutine(CppCoroutine&& other) noexcept
: handle(std::exchange(other.handle, {}))
{
}
CppCoroutine(const CppCoroutine&) = delete;
CppCoroutine& operator=(const CppCoroutine&) = delete;
~CppCoroutine() = default;
};
//=================================================================================================
/**
* @brief Specialisation for void-returning coroutines.
*/
template <>
struct CppCoroutine<void>
{
struct promise_type
{
lua_State* L = nullptr;
int nresults = 0;
bool is_done = false;
std::exception_ptr exception;
std::suspend_always initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() noexcept
{
exception = std::current_exception();
}
void return_void()
{
nresults = 0;
is_done = true;
}
CppCoroutine get_return_object()
{
return CppCoroutine{ std::coroutine_handle<promise_type>::from_promise(*this) };
}
};
std::coroutine_handle<promise_type> handle;
explicit CppCoroutine(std::coroutine_handle<promise_type> h) noexcept
: handle(h)
{
}
CppCoroutine(CppCoroutine&& other) noexcept
: handle(std::exchange(other.handle, {}))
{
}
CppCoroutine(const CppCoroutine&) = delete;
CppCoroutine& operator=(const CppCoroutine&) = delete;
~CppCoroutine() = default;
};
//=================================================================================================
/**
* @brief An awaitable wrapper around a Lua coroutine thread.
*
* Use inside a CppCoroutine body to synchronously resume a child Lua thread and obtain
* the number of values it left on its stack (either from yield or return).
*
* @note Runs the Lua thread synchronously (no external event loop required).
*/
class LuaCoroutine
{
public:
LuaCoroutine(lua_State* thread, lua_State* from = nullptr) noexcept
: m_thread(thread)
, m_from(from)
{
}
bool await_ready() noexcept
{
m_status = lua_resume_x(m_thread, m_from, 0, &m_nresults);
return true; // Always ready: runs synchronously
}
void await_suspend(std::coroutine_handle<>) noexcept
{
// Never called because await_ready always returns true
}
/**
* @returns {status, nresults} where status is LUA_OK or LUA_YIELD,
* and nresults is the number of values on the thread's stack.
*/
std::pair<int, int> await_resume() noexcept
{
return { m_status, m_nresults };
}
private:
lua_State* m_thread;
lua_State* m_from;
int m_status = LUABRIDGE_LUA_OK;
int m_nresults = 0;
};
//=================================================================================================
namespace detail {
/**
* @brief Trait: is T a CppCoroutine<R> specialisation?
*/
template <class T>
struct is_cpp_coroutine : std::false_type
{
};
template <class R>
struct is_cpp_coroutine<CppCoroutine<R>> : std::true_type
{
};
/**
* @brief Trait: does callable F return a CppCoroutine<R>?
*/
template <class F, class = void>
struct is_cpp_coroutine_factory : std::false_type
{
};
template <class F>
struct is_cpp_coroutine_factory<F, std::void_t<typename function_traits<std::remove_reference_t<F>>::result_type>>
: is_cpp_coroutine<typename function_traits<std::remove_reference_t<F>>::result_type>
{
};
template <class F>
inline constexpr bool is_cpp_coroutine_factory_v = is_cpp_coroutine_factory<F>::value;
//=================================================================================================
/**
* @brief RAII frame for a suspended CppCoroutine, stored as a Lua full userdata.
*
* Kept alive on the Lua thread's own stack (not in the registry) so that abandoning the
* coroutine — i.e. letting the Lua thread be collected by the GC — automatically triggers
* the __gc metamethod, which calls the destructor and destroys the coroutine handle.
*/
template <class CoroType>
struct CppCoroutineFrame
{
using HandleType = std::coroutine_handle<typename CoroType::promise_type>;
HandleType handle;
explicit CppCoroutineFrame(HandleType h) noexcept
: handle(h)
{
}
CppCoroutineFrame(const CppCoroutineFrame&) = delete;
CppCoroutineFrame& operator=(const CppCoroutineFrame&) = delete;
~CppCoroutineFrame()
{
if (handle && !handle.done())
handle.destroy();
}
};
//=================================================================================================
// Version-portable yield helpers.
//
// Lua 5.2: lua_yieldk ctx is int; continuation signature is (lua_State*, int) — ctx
// retrieved inside via lua_getctx().
// Lua 5.3+: lua_yieldk ctx is lua_KContext; continuation signature is
// (lua_State*, int, lua_KContext) — ctx passed directly.
// Forward declarations
template <class F> int coroutine_continuation_body(lua_State* L, int frame_abs_idx);
#if LUA_VERSION_NUM < 503
// Lua 5.2: lua_yieldk takes lua_CFunction (int(*)(lua_State*)) as continuation.
// The context is recovered inside via lua_getctx().
template <class F>
int coroutine_continuation(lua_State* L)
{
int frame_abs_idx = 0;
lua_getctx(L, &frame_abs_idx);
return coroutine_continuation_body<F>(L, frame_abs_idx);
}
template <class F>
int do_yield(lua_State* L, int nresults, int frame_abs_idx)
{
return lua_yieldk(L, nresults, frame_abs_idx, &coroutine_continuation<F>);
}
#else
// Lua 5.3+: continuation receives lua_KContext directly.
template <class F>
int coroutine_continuation(lua_State* L, int /*status*/, lua_KContext ctx)
{
return coroutine_continuation_body<F>(L, static_cast<int>(ctx));
}
template <class F>
int do_yield(lua_State* L, int nresults, int frame_abs_idx)
{
return lua_yieldk(L, nresults, static_cast<lua_KContext>(frame_abs_idx), &coroutine_continuation<F>);
}
#endif
//=================================================================================================
/**
* @brief Raises a Lua error from a stored C++ exception (or a generic message).
* Removes the frame userdata from the stack before raising so GC can collect it.
*/
[[noreturn]] inline void raise_from_exception(lua_State* L, int frame_abs_idx, std::exception_ptr ex)
{
lua_settop(L, frame_abs_idx - 1); // pop frame (and any value above it) — GC will collect it
#if LUABRIDGE_HAS_EXCEPTIONS
try
{
std::rethrow_exception(ex);
}
catch (const std::exception& e)
{
raise_lua_error(L, "%s", e.what());
}
catch (...)
{
#endif
raise_lua_error(L, "unknown exception in C++ coroutine");
#if LUABRIDGE_HAS_EXCEPTIONS
}
#endif
}
//=================================================================================================
/**
* @brief Common body for the coroutine continuation: resumes the C++ coroutine handle
* and either yields again or returns the final result.
*
* @param frame_abs_idx Absolute stack index where the CppCoroutineFrame userdata lives.
* Any resume arguments pushed above it are discarded first.
*/
template <class F>
int coroutine_continuation_body(lua_State* L, int frame_abs_idx)
{
using CoroType = typename function_traits<std::remove_reference_t<F>>::result_type;
using FrameType = CppCoroutineFrame<CoroType>;
// Discard resume arguments pushed above the frame (we don't expose them to C++ yet)
lua_settop(L, frame_abs_idx);
// Recover the frame from its stable stack position
auto* frame = align<FrameType>(lua_touserdata(L, frame_abs_idx));
// Resume the C++ coroutine body; yield_value/return_value will push at frame_abs_idx+1
frame->handle.resume();
auto& promise = frame->handle.promise();
if (promise.exception)
raise_from_exception(L, frame_abs_idx, promise.exception);
if (promise.is_done)
{
if (promise.nresults == 1)
lua_replace(L, frame_abs_idx); // swap return value into frame slot; pops frame userdata
else
lua_settop(L, frame_abs_idx - 1); // void: remove frame entirely
return promise.nresults;
}
// yield_value pushed one value above the frame; yield it, keeping frame below
return do_yield<F>(L, promise.nresults, frame_abs_idx);
}
//=================================================================================================
/**
* @brief lua_CFunction entry point for a registered CppCoroutine factory.
*
* Upvalue 1: the factory functor F (as aligned full userdata).
*
* The CppCoroutineFrame userdata is left on the Lua thread's own stack (not in the registry).
* This means an abandoned coroutine is naturally cleaned up when the Lua thread is GC'd.
*/
template <class F>
int invoke_coroutine_entry(lua_State* L)
{
using FnTraits = function_traits<std::remove_reference_t<F>>;
using ArgsPack = typename FnTraits::argument_types;
using CoroType = typename FnTraits::result_type;
using FrameType = CppCoroutineFrame<CoroType>;
LUABRIDGE_ASSERT(isfulluserdata(L, lua_upvalueindex(1)));
auto& factory = *align<F>(lua_touserdata(L, lua_upvalueindex(1)));
// Invoke the factory to create the coroutine object.
// The coroutine body does not run yet (initial_suspend returns suspend_always).
auto coro = invoke_callable_from_stack<ArgsPack, 1>(L, factory);
// Push the frame as a Lua full userdata and remember its absolute stack position.
// It is NOT pinned in the registry; keeping it on the thread's stack means GC will
// collect it (via __gc) when the Lua thread is abandoned.
lua_newuserdata_aligned<FrameType>(L, std::move(coro.handle));
coro.handle = {}; // ownership transferred to frame
int frame_abs_idx = lua_gettop(L);
auto* frame = align<FrameType>(lua_touserdata(L, frame_abs_idx));
// Give the promise access to the Lua state so yield_value/return_value can push values
frame->handle.promise().L = L;
// First resume: runs the body to the first co_yield or co_return
frame->handle.resume();
auto& promise = frame->handle.promise();
if (promise.exception)
raise_from_exception(L, frame_abs_idx, promise.exception);
if (promise.is_done)
{
if (promise.nresults == 1)
lua_replace(L, frame_abs_idx); // swap return value into frame slot
else
lua_settop(L, frame_abs_idx - 1); // void: remove frame
return promise.nresults;
}
// yield_value pushed one value above the frame; yield it, keeping frame below
return do_yield<F>(L, promise.nresults, frame_abs_idx);
}
//=================================================================================================
/**
* @brief Pushes a CppCoroutine factory as a Lua closure onto the stack.
*/
template <class F, class = std::enable_if_t<is_cpp_coroutine_factory_v<F>>>
inline void push_coroutine_function(lua_State* L, F&& f, const char* debugname)
{
using FDecay = std::decay_t<F>;
lua_newuserdata_aligned<FDecay>(L, std::forward<F>(f));
lua_pushcclosure_x(L, &invoke_coroutine_entry<FDecay>, debugname, 1);
}
} // namespace detail
} // namespace luabridge
#endif // !Lua 5.1 / LuaJIT / Luau
#endif // LUABRIDGE_HAS_CXX20_COROUTINES
+67
View File
@@ -0,0 +1,67 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2023, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include "Errors.h"
#include "LuaHelpers.h"
#include "Stack.h"
#include <type_traits>
namespace luabridge {
//=================================================================================================
/**
* @brief LuaBridge enum wrapper for enums as integers.
*
* An enum exposed with this class will just be decayed to lua as integer. It's responsibility of
* the developer to make sure that a lua integer could be converted back to C++. Failing to validate a lua
* integer before converting to the corresponding C++ enum value could lead to a C++ enum that has no defined value.
*
* For improved security, specify which values the enum will have, so runtime validation could be performed.
*/
template <class T, T... Values>
struct Enum
{
static_assert(std::is_enum_v<T>);
using Type = std::underlying_type_t<T>;
[[nodiscard]] static Result push(lua_State* L, T value)
{
return Stack<Type>::push(L, static_cast<Type>(value));
}
[[nodiscard]] static TypeResult<T> get(lua_State* L, int index)
{
const auto result = Stack<Type>::get(L, index);
if (! result)
return result.error();
if constexpr (sizeof...(Values) > 0)
{
constexpr Type values[] = { static_cast<Type>(Values)... };
for (std::size_t i = 0; i < sizeof...(Values); ++i)
{
if (values[i] == *result)
return static_cast<T>(*result);
}
return makeErrorCode(ErrorCode::InvalidTypeCast);
}
else
{
return static_cast<T>(*result);
}
}
[[nodiscard]] static bool isInstance(lua_State* L, int index)
{
return lua_type(L, index) == LUA_TNUMBER;
}
};
} // namespace luabridge
+123
View File
@@ -0,0 +1,123 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2021, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include <system_error>
namespace luabridge {
//=================================================================================================
namespace detail {
static inline constexpr char error_lua_stack_overflow[] = "stack overflow";
} // namespace detail
//=================================================================================================
/**
* @brief LuaBridge error codes.
*/
enum class ErrorCode
{
ClassNotRegistered = 1,
LuaStackOverflow,
LuaFunctionCallFailed,
IntegerDoesntFitIntoLuaInteger,
FloatingPointDoesntFitIntoLuaNumber,
InvalidTypeCast,
InvalidTableSizeInCast,
CoroutineYieldFromNonCoroutine,
CoroutineAlreadyDone
};
//=================================================================================================
namespace detail {
struct ErrorCategory : std::error_category
{
const char* name() const noexcept override
{
return "luabridge";
}
std::string message(int ev) const override
{
return errorString(ev);
}
static const char* errorString(int ev) noexcept
{
switch (static_cast<ErrorCode>(ev))
{
case ErrorCode::ClassNotRegistered:
return "The class is not registered in LuaBridge";
case ErrorCode::LuaStackOverflow:
return "The lua stack has overflow";
case ErrorCode::LuaFunctionCallFailed:
return "The lua function invocation raised an error";
case ErrorCode::IntegerDoesntFitIntoLuaInteger:
return "The native integer can't fit inside a lua integer";
case ErrorCode::FloatingPointDoesntFitIntoLuaNumber:
return "The native floating point can't fit inside a lua number";
case ErrorCode::InvalidTypeCast:
return "The lua object can't be cast to desired type";
case ErrorCode::InvalidTableSizeInCast:
return "The lua table has different size than expected";
case ErrorCode::CoroutineYieldFromNonCoroutine:
return "Cannot yield from a non-coroutine Lua state";
case ErrorCode::CoroutineAlreadyDone:
return "The Lua coroutine has already finished execution";
default:
return "Unknown error";
}
}
static const ErrorCategory& getInstance() noexcept
{
static ErrorCategory category;
return category;
}
};
} // namespace detail
//=================================================================================================
/**
* @brief Construct an error code from the error enum.
*/
inline std::error_code makeErrorCode(ErrorCode e)
{
return { static_cast<int>(e), detail::ErrorCategory::getInstance() };
}
/**
* @brief Supports std::error_code construction.
*/
inline std::error_code make_error_code(ErrorCode e)
{
return { static_cast<int>(e), detail::ErrorCategory::getInstance() };
}
} // namespace luabridge
namespace std {
template <> struct is_error_code_enum<luabridge::ErrorCode> : true_type {};
} // namespace std
File diff suppressed because it is too large Load Diff
+124
View File
@@ -0,0 +1,124 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2023, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include <type_traits>
#include <algorithm>
#include <string>
namespace luabridge {
template <class T, class... Ts>
class FlagSet
{
static_assert(std::is_integral_v<T>);
public:
constexpr FlagSet() noexcept = default;
constexpr void set(FlagSet other) noexcept
{
flags |= other.flags;
}
constexpr FlagSet withSet(FlagSet other) const noexcept
{
FlagSet result { flags };
result.flags |= other.flags;
return result;
}
constexpr void unset(FlagSet other) noexcept
{
flags &= ~other.flags;
}
constexpr FlagSet withUnset(FlagSet other) const noexcept
{
FlagSet result { flags };
result.flags &= ~other.flags;
return result;
}
constexpr bool test(FlagSet other) const noexcept
{
return (flags & other.flags) != 0;
}
constexpr FlagSet operator|(FlagSet other) const noexcept
{
return FlagSet(flags | other.flags);
}
constexpr FlagSet operator&(FlagSet other) const noexcept
{
return FlagSet(flags & other.flags);
}
constexpr FlagSet operator~() const noexcept
{
return FlagSet(~flags);
}
constexpr T toUnderlying() const noexcept
{
return flags;
}
std::string toString() const
{
std::string result;
result.reserve(sizeof(T) * std::numeric_limits<uint8_t>::digits);
(result.append((mask<Ts>() & flags) ? "1" : "0"), ...);
for (std::size_t i = sizeof...(Ts); i < sizeof(T) * std::numeric_limits<uint8_t>::digits; ++i)
result.append("0");
std::reverse(result.begin(), result.end());
return result;
}
template <class... Us>
static constexpr FlagSet Value() noexcept
{
return FlagSet{ mask<Us...>() };
}
template <class U>
static constexpr auto fromUnderlying(U newFlags) noexcept
-> std::enable_if_t<std::is_integral_v<U> && std::is_convertible_v<U, T>, FlagSet>
{
return { static_cast<T>(newFlags) };
}
private:
template <class U, class V, class... Us>
static constexpr T indexOf() noexcept
{
if constexpr (std::is_same_v<U, V>)
return static_cast<T>(0);
else
return static_cast<T>(1) + indexOf<U, Us...>();
}
template <class... Us>
static constexpr T mask() noexcept
{
return ((static_cast<T>(1) << indexOf<Us, Ts...>()) | ...);
}
constexpr FlagSet(T flags) noexcept
: flags(flags)
{
}
T flags = 0;
};
} // namespace luabridge
+872
View File
@@ -0,0 +1,872 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2020, Dmitry Tarakanov
// Copyright 2019, George Tokmaji
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include <functional>
#include <tuple>
namespace luabridge {
namespace detail {
//=================================================================================================
/**
* @brief Invokes undefined behavior when an unreachable part of the code is reached.
*
* An implementation may use this to optimize impossible code branches away (typically, in optimized builds) or to trap them to prevent
* further execution (typically, in debug builds).
*/
[[noreturn]] inline void unreachable()
{
#if defined(__GNUC__) // GCC, Clang, ICC
__builtin_unreachable();
#elif defined(_MSC_VER) // MSVC
__assume(false);
#endif
}
//=================================================================================================
/**
* @brief Provides the member typedef type which is the type referred to by T with its topmost cv-qualifiers removed.
*/
template< class T >
struct remove_cvref
{
typedef std::remove_cv_t<std::remove_reference_t<T>> type;
};
template <class T>
using remove_cvref_t = typename remove_cvref<T>::type;
//=================================================================================================
/**
* @brief Generic function traits.
*
* @tparam IsMember True if the function is a member function pointer.
* @tparam IsConst True if the function is const.
* @tparam R Return type of the function.
* @tparam Args Arguments types as variadic parameter pack.
*/
template <bool IsMember, bool IsConst, class R, class... Args>
struct function_traits_base
{
using result_type = R;
using argument_types = std::tuple<Args...>;
static constexpr auto arity = sizeof...(Args);
static constexpr auto is_member = IsMember;
static constexpr auto is_const = IsConst;
};
template <class, bool Enable>
struct function_traits_impl;
template <class R, class... Args>
struct function_traits_impl<R(Args...), true> : function_traits_base<false, false, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R (*)(Args...), true> : function_traits_base<false, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (C::*)(Args...), true> : function_traits_base<true, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (C::*)(Args...) const, true> : function_traits_base<true, true, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R(Args...) noexcept, true> : function_traits_base<false, false, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R (*)(Args...) noexcept, true> : function_traits_base<false, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (C::*)(Args...) noexcept, true> : function_traits_base<true, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (C::*)(Args...) const noexcept, true> : function_traits_base<true, true, R, Args...>
{
};
#if defined(_MSC_VER) && defined(_M_IX86) // Windows: WINAPI (a.k.a. __stdcall) function pointers (32bit only).
inline static constexpr bool is_stdcall_default_calling_convention = std::is_same_v<void __stdcall(), void()>;
inline static constexpr bool is_fastcall_default_calling_convention = std::is_same_v<void __fastcall(), void()>;
template <class R, class... Args>
struct function_traits_impl<R __stdcall(Args...), !is_stdcall_default_calling_convention> : function_traits_base<false, false, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R (__stdcall *)(Args...), !is_stdcall_default_calling_convention> : function_traits_base<false, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (__stdcall C::*)(Args...), true> : function_traits_base<true, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (__stdcall C::*)(Args...) const, true> : function_traits_base<true, true, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R __stdcall(Args...) noexcept, !is_stdcall_default_calling_convention> : function_traits_base<false, false, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R (__stdcall *)(Args...) noexcept, !is_stdcall_default_calling_convention> : function_traits_base<false, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (__stdcall C::*)(Args...) noexcept, true> : function_traits_base<true, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (__stdcall C::*)(Args...) const noexcept, true> : function_traits_base<true, true, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R __fastcall(Args...), !is_fastcall_default_calling_convention> : function_traits_base<false, false, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R (__fastcall *)(Args...), !is_fastcall_default_calling_convention> : function_traits_base<false, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (__fastcall C::*)(Args...), true> : function_traits_base<true, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (__fastcall C::*)(Args...) const, true> : function_traits_base<true, true, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R __fastcall(Args...) noexcept, !is_fastcall_default_calling_convention> : function_traits_base<false, false, R, Args...>
{
};
template <class R, class... Args>
struct function_traits_impl<R (__fastcall *)(Args...) noexcept, !is_fastcall_default_calling_convention> : function_traits_base<false, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (__fastcall C::*)(Args...) noexcept, true> : function_traits_base<true, false, R, Args...>
{
};
template <class C, class R, class... Args>
struct function_traits_impl<R (__fastcall C::*)(Args...) const noexcept, true> : function_traits_base<true, true, R, Args...>
{
};
#endif
template <class F, class = void>
struct has_call_operator : std::false_type
{
};
template <class F>
struct has_call_operator<F, std::void_t<decltype(&F::operator())>> : std::true_type
{
};
template <class F>
inline static constexpr bool has_call_operator_v = has_call_operator<F>::value;
template <class F>
struct is_move_only_function : std::false_type {};
#if LUABRIDGE_HAS_CXX23_MOVE_ONLY_FUNCTION
template <class R, class... Args> struct is_move_only_function<std::move_only_function<R(Args...)>> : std::true_type {};
template <class R, class... Args> struct is_move_only_function<std::move_only_function<R(Args...) noexcept>> : std::true_type {};
template <class R, class... Args> struct is_move_only_function<std::move_only_function<R(Args...) const>> : std::true_type {};
template <class R, class... Args> struct is_move_only_function<std::move_only_function<R(Args...) const noexcept>> : std::true_type {};
#endif
template <class F>
inline static constexpr bool is_move_only_function_v = is_move_only_function<F>::value;
template <class F, class = void>
struct functor_traits_impl
{
};
template <class F>
struct functor_traits_impl<F, std::enable_if_t<has_call_operator_v<F>>> : function_traits_impl<decltype(&F::operator()), true>
{
};
template <class F>
struct functor_traits_impl<F, std::enable_if_t<!has_call_operator_v<F> && std::is_invocable_v<F&> && !is_move_only_function_v<F>>>
: function_traits_base<false, false, std::invoke_result_t<F&>>
{
};
#if LUABRIDGE_HAS_CXX23_MOVE_ONLY_FUNCTION
template <class R, class... Args>
struct functor_traits_impl<std::move_only_function<R(Args...)>>
: function_traits_base<false, false, R, Args...>
{
};
template <class R, class... Args>
struct functor_traits_impl<std::move_only_function<R(Args...) noexcept>>
: function_traits_base<false, false, R, Args...>
{
};
template <class R, class... Args>
struct functor_traits_impl<std::move_only_function<R(Args...) const>>
: function_traits_base<false, true, R, Args...>
{
};
template <class R, class... Args>
struct functor_traits_impl<std::move_only_function<R(Args...) const noexcept>>
: function_traits_base<false, true, R, Args...>
{
};
#endif // LUABRIDGE_HAS_CXX23_MOVE_ONLY_FUNCTION
//=================================================================================================
/**
* @brief Traits class for callable objects (e.g. function pointers, lambdas)
*
* @tparam F Callable object.
*/
template <class F>
struct function_traits : std::conditional_t<std::is_class_v<F>,
detail::functor_traits_impl<F>,
detail::function_traits_impl<F, true>>
{
};
template <class T, bool IsClass = std::is_class_v<T>, class = void>
struct has_function_traits : std::false_type
{
};
template <class T>
struct has_function_traits<T, true, std::void_t<typename function_traits<T>::result_type, typename function_traits<T>::argument_types>> : std::true_type
{
};
template <class T>
inline static constexpr bool has_function_traits_v = has_function_traits<T>::value;
//=================================================================================================
/**
* @brief Deduces the argument type of a callble object or void in case it has no argument.
*
* @tparam I Argument index.
* @tparam F Callable object.
*/
template <std::size_t I, class F, class = void>
struct function_argument_or_void
{
using type = void;
};
template <std::size_t I, class F>
struct function_argument_or_void<I, F, std::enable_if_t<I < std::tuple_size_v<typename function_traits<F>::argument_types>>>
{
using type = std::tuple_element_t<I, typename function_traits<F>::argument_types>;
};
template <std::size_t I, class F>
using function_argument_or_void_t = typename function_argument_or_void<I, F>::type;
//=================================================================================================
/**
* @brief Deduces the return type of a callble object.
*
* @tparam F Callable object.
*/
template <class F>
using function_result_t = typename function_traits<F>::result_type;
/**
* @brief Deduces the argument type of a callble object.
*
* @tparam I Argument index.
* @tparam F Callable object.
*/
template <std::size_t I, class F>
using function_argument_t = std::tuple_element_t<I, typename function_traits<F>::argument_types>;
/**
* @brief Deduces the arguments type of a callble object.
*
* @tparam F Callable object.
*/
template <class F>
using function_arguments_t = typename function_traits<F>::argument_types;
/**
* @brief An integral constant expression that gives the number of arguments accepted by the callable object.
*
* @tparam F Callable object.
*/
template <class F>
static constexpr std::size_t function_arity_v = function_traits<F>::arity;
/**
* @brief An boolean constant expression that checks if the callable object is a member function.
*
* @tparam F Callable object.
*/
template <class F>
static constexpr bool function_is_member_v = function_traits<F>::is_member;
/**
* @brief An boolean constant expression that checks if the callable object is const.
*
* @tparam F Callable object.
*/
template <class F>
static constexpr bool function_is_const_v = function_traits<F>::is_const;
//=================================================================================================
/**
* @brief Detect if we T is a callable object.
*
* @tparam T Potentially callable object.
*/
template <class T, class = void>
struct is_callable
{
static constexpr bool value = false;
};
template <class T>
struct is_callable<T, std::void_t<decltype(&T::operator())>>
{
static constexpr bool value = true;
};
template <class T>
struct is_callable<T, std::enable_if_t<std::is_class_v<T> && !has_call_operator_v<T> && has_function_traits_v<T>>>
{
static constexpr bool value = true;
};
template <class T>
struct is_callable<T, std::enable_if_t<std::is_pointer_v<T> && std::is_function_v<std::remove_pointer_t<T>>>>
{
static constexpr bool value = true;
};
template <class T>
struct is_callable<T, std::enable_if_t<std::is_member_function_pointer_v<T>>>
{
static constexpr bool value = true;
};
template <class T>
inline static constexpr bool is_callable_v = is_callable<T>::value;
//=================================================================================================
/**
* @brief Detect if we T is a const member function pointer.
*
* @tparam T Potentially const member function pointer.
*/
template <class T>
struct is_const_member_function_pointer
{
static constexpr bool value = false;
};
template <class T, class R, class... Args>
struct is_const_member_function_pointer<R (T::*)(Args...)>
{
static constexpr bool value = false;
};
template <class T, class R, class... Args>
struct is_const_member_function_pointer<R (T::*)(Args...) const>
{
static constexpr bool value = true;
};
template <class T, class R, class... Args>
struct is_const_member_function_pointer<R (T::*)(Args...) noexcept>
{
static constexpr bool value = false;
};
template <class T, class R, class... Args>
struct is_const_member_function_pointer<R (T::*)(Args...) const noexcept>
{
static constexpr bool value = true;
};
template <class T>
inline static constexpr bool is_const_member_function_pointer_v = is_const_member_function_pointer<T>::value;
//=================================================================================================
/**
* @brief Detect if T is a lua cfunction pointer.
*
* @tparam T Potentially lua cfunction pointer.
*/
template <class T>
struct is_cfunction_pointer
{
static constexpr bool value = false;
};
template <>
struct is_cfunction_pointer<int (*)(lua_State*)>
{
static constexpr bool value = true;
};
template <class T>
inline static constexpr bool is_cfunction_pointer_v = is_cfunction_pointer<T>::value;
//=================================================================================================
/**
* @brief Detect if T is a member lua cfunction pointer.
*
* @tparam T Potentially member lua cfunction pointer.
*/
template <class T>
struct is_member_cfunction_pointer
{
static constexpr bool value = false;
};
template <class T>
struct is_member_cfunction_pointer<int (T::*)(lua_State*)>
{
static constexpr bool value = true;
};
template <class T>
struct is_member_cfunction_pointer<int (T::*)(lua_State*) const>
{
static constexpr bool value = true;
};
template <class T>
inline static constexpr bool is_member_cfunction_pointer_v = is_member_cfunction_pointer<T>::value;
/**
* @brief Detect if T is a const member lua cfunction pointer.
*
* @tparam T Potentially const member lua cfunction pointer.
*/
template <class T>
struct is_const_member_cfunction_pointer
{
static constexpr bool value = false;
};
template <class T>
struct is_const_member_cfunction_pointer<int (T::*)(lua_State*)>
{
static constexpr bool value = false;
};
template <class T>
struct is_const_member_cfunction_pointer<int (T::*)(lua_State*) const>
{
static constexpr bool value = true;
};
template <class T>
inline static constexpr bool is_const_member_cfunction_pointer_v = is_const_member_cfunction_pointer<T>::value;
//=================================================================================================
/**
* @brief Detect if T is a member or non member lua cfunction pointer.
*
* @tparam T Potentially member or non member lua cfunction pointer.
*/
template <class T>
inline static constexpr bool is_any_cfunction_pointer_v = is_cfunction_pointer_v<T> || is_member_cfunction_pointer_v<T>;
//=================================================================================================
/**
* @brief A constexpr check for proxy_member functions.
*
* @tparam T Type where the callable should be able to operate.
* @tparam F Callable object.
*/
template <class T, class F>
inline static constexpr bool is_proxy_member_function_v =
!std::is_member_function_pointer_v<F> &&
std::is_same_v<T, remove_cvref_t<std::remove_pointer_t<function_argument_or_void_t<0, F>>>>;
template <class T, class F>
inline static constexpr bool is_const_proxy_function_v =
is_proxy_member_function_v<T, F> &&
std::is_const_v<std::remove_reference_t<std::remove_pointer_t<function_argument_or_void_t<0, F>>>>;
//=================================================================================================
/**
* @brief An integral constant expression that gives the number of arguments excluding one type (usually used with lua_State*) accepted by the callable object.
*
* @tparam F Callable object.
*/
template <class, class>
struct function_arity_excluding
{
};
template < class... Ts, class ExclusionType>
struct function_arity_excluding<std::tuple<Ts...>, ExclusionType>
: std::integral_constant<std::size_t, (0 + ... + (std::is_same_v<std::decay_t<Ts>, ExclusionType> ? 0 : 1))>
{
};
template <class F, class ExclusionType>
inline static constexpr std::size_t function_arity_excluding_v = function_arity_excluding<function_arguments_t<F>, ExclusionType>::value;
/**
* @brief An integral constant expression that gives the number of arguments excluding one type (usually used with lua_State*) accepted by the callable object.
*
* @tparam F Callable object.
*/
template <class, class, class, class, class = void>
struct member_function_arity_excluding
{
};
template <class T, class F, class... Ts, class ExclusionType>
struct member_function_arity_excluding<T, F, std::tuple<Ts...>, ExclusionType, std::enable_if_t<!is_proxy_member_function_v<T, F>>>
: std::integral_constant<std::size_t, (0 + ... + (std::is_same_v<std::decay_t<Ts>, ExclusionType> ? 0 : 1))>
{
};
template <class T, class F, class... Ts, class ExclusionType>
struct member_function_arity_excluding<T, F, std::tuple<Ts...>, ExclusionType, std::enable_if_t<is_proxy_member_function_v<T, F>>>
: std::integral_constant<std::size_t, (0 + ... + (std::is_same_v<std::decay_t<Ts>, ExclusionType> ? 0 : 1)) - 1>
{
};
template <class T, class F, class ExclusionType>
inline static constexpr std::size_t member_function_arity_excluding_v = member_function_arity_excluding<T, F, function_arguments_t<F>, ExclusionType>::value;
//=================================================================================================
/**
* @brief Detectors for const and non const functions in packs and counting them.
*/
template <class T, class F>
static constexpr bool is_const_function =
detail::is_const_member_function_pointer_v<F> ||
(detail::function_arity_v<F> > 0 && detail::is_const_proxy_function_v<T, F>);
template <class T, class... Fs>
inline static constexpr std::size_t const_functions_count = (0 + ... + (is_const_function<T, Fs> ? 1 : 0));
template <class T, class... Fs>
inline static constexpr std::size_t non_const_functions_count = (0 + ... + (is_const_function<T, Fs> ? 0 : 1));
//=================================================================================================
/**
* @brief Simple make_tuple alternative that doesn't decay the types.
*
* @tparam Types Argument types that will compose the tuple.
*/
template <class... Types>
constexpr auto tupleize(Types&&... types)
{
return std::tuple<Types...>(std::forward<Types>(types)...);
}
//=================================================================================================
/**
* @brief Remove first type from tuple.
*/
template <class T>
struct remove_first_type
{
};
template <class T, class... Ts>
struct remove_first_type<std::tuple<T, Ts...>>
{
using type = std::tuple<Ts...>;
};
template <class T>
using remove_first_type_t = typename remove_first_type<T>::type;
//=================================================================================================
/**
* @brief Drop the first N types from a tuple.
*/
template <std::size_t N, class Tuple>
struct tuple_drop_first
{
using type = typename tuple_drop_first<N - 1, remove_first_type_t<Tuple>>::type;
};
template <class Tuple>
struct tuple_drop_first<0, Tuple>
{
using type = Tuple;
};
template <std::size_t N, class Tuple>
using tuple_drop_first_t = typename tuple_drop_first<N, Tuple>::type;
//=================================================================================================
/**
* @brief Prepend a type to a tuple.
*/
template <class T, class Tuple>
struct tuple_prepend;
template <class T, class... Ts>
struct tuple_prepend<T, std::tuple<Ts...>>
{
using type = std::tuple<T, Ts...>;
};
template <class T, class Tuple>
using tuple_prepend_t = typename tuple_prepend<T, Tuple>::type;
//=================================================================================================
/**
* @brief Take only the first N types from a tuple (uses an accumulator to avoid ambiguity).
*/
template <std::size_t N, class Tuple, class Accum = std::tuple<>>
struct tuple_take_first_impl
{
using type = Accum;
};
template <std::size_t N, class T, class... Ts, class... Acc>
struct tuple_take_first_impl<N, std::tuple<T, Ts...>, std::tuple<Acc...>>
{
using type = typename tuple_take_first_impl<N - 1, std::tuple<Ts...>, std::tuple<Acc..., T>>::type;
};
template <class T, class... Ts, class... Acc>
struct tuple_take_first_impl<0, std::tuple<T, Ts...>, std::tuple<Acc...>>
{
using type = std::tuple<Acc...>;
};
template <std::size_t N, class Tuple>
using tuple_take_first_t = typename tuple_take_first_impl<N, Tuple>::type;
//=================================================================================================
/**
* @brief Extracts the class type from a member function pointer.
*/
template <class F>
struct member_function_class;
template <class C, class R, class... Args>
struct member_function_class<R (C::*)(Args...)> { using type = C; };
template <class C, class R, class... Args>
struct member_function_class<R (C::*)(Args...) const> { using type = const C; };
template <class C, class R, class... Args>
struct member_function_class<R (C::*)(Args...) noexcept> { using type = C; };
template <class C, class R, class... Args>
struct member_function_class<R (C::*)(Args...) const noexcept> { using type = const C; };
template <class F>
using member_function_class_t = typename member_function_class<F>::type;
//=================================================================================================
/**
* @brief Computes the leading argument tuple for bind_back: for member function pointers,
* prepends ClassType* to the explicit remaining args; for all other callables, returns
* the explicit remaining args unchanged.
*/
template <class Fn, class ExplicitRemaining, bool IsMember>
struct bind_back_leading_impl
{
using type = ExplicitRemaining;
};
template <class Fn, class ExplicitRemaining>
struct bind_back_leading_impl<Fn, ExplicitRemaining, true>
{
using type = tuple_prepend_t<member_function_class_t<Fn>*, ExplicitRemaining>;
};
template <class Fn, class ExplicitRemaining>
using bind_back_leading_t =
typename bind_back_leading_impl<Fn, ExplicitRemaining, std::is_member_function_pointer_v<Fn>>::type;
//=================================================================================================
/**
* @brief Internal storage for luabridge::bind_front — exposes a non-template operator() so that
* function_traits can statically resolve result_type and argument_types.
*/
template <class R, class RemainingArgsTuple, class Fn, class... BoundArgs>
struct bind_front_wrapper;
template <class R, class... Remaining, class Fn, class... BoundArgs>
struct bind_front_wrapper<R, std::tuple<Remaining...>, Fn, BoundArgs...>
{
Fn fn_;
std::tuple<BoundArgs...> bound_;
template <class F, class... BA>
bind_front_wrapper(F&& f, BA&&... ba)
: fn_(std::forward<F>(f)), bound_(std::forward<BA>(ba)...)
{
}
R operator()(Remaining... args) const
{
return std::apply([&](const auto&... ba) { return std::invoke(fn_, ba..., args...); }, bound_);
}
};
} // namespace detail
//=================================================================================================
/**
* @brief Drop-in replacement for std::bind_front with statically introspectable argument types.
*
* std::bind_front returns an object whose operator() is a template, so its argument and result
* types cannot be resolved at compile time without an explicit std::function<Sig> cast. This
* wrapper stores the callable and its leading bound arguments, then exposes a concrete
* non-template operator() whose parameter types are derived directly from the underlying
* callable's signature.
*
* For member function pointers the implicit object argument consumed by std::invoke is not
* counted as part of the remaining (Lua-visible) parameter list, matching std::bind_front
* semantics.
*
* @tparam F Callable type (function pointer, member function pointer, functor).
* @tparam BoundArgs Leading argument types to bind.
* @param f The callable to wrap.
* @param args Leading arguments forwarded into the wrapper by value.
* @return A callable object whose operator() accepts the remaining (unbound) arguments.
*/
template <class F, class... BoundArgs>
auto bind_front(F&& f, BoundArgs&&... args)
{
using Fn = std::decay_t<F>;
using FnTraits = detail::function_traits<Fn>;
static constexpr std::size_t skip = std::is_member_function_pointer_v<Fn> ? 1u : 0u;
static constexpr std::size_t num_effective_bound = sizeof...(BoundArgs) - skip;
using remaining = detail::tuple_drop_first_t<num_effective_bound, typename FnTraits::argument_types>;
using R = typename FnTraits::result_type;
return detail::bind_front_wrapper<R, remaining, Fn, std::decay_t<BoundArgs>...>(
std::forward<F>(f), std::forward<BoundArgs>(args)...);
}
//=================================================================================================
namespace detail {
/**
* @brief Internal storage for luabridge::bind_back — exposes a non-template operator() so that
* function_traits can statically resolve result_type and argument_types.
*
* LeadingArgsTuple is the tuple of arguments that the caller must provide; BoundArgs are
* the trailing arguments captured at bind time. For member function pointers, the class
* pointer is included as the first element of LeadingArgsTuple.
*/
template <class R, class LeadingArgsTuple, class Fn, class... BoundArgs>
struct bind_back_wrapper;
template <class R, class... Leading, class Fn, class... BoundArgs>
struct bind_back_wrapper<R, std::tuple<Leading...>, Fn, BoundArgs...>
{
Fn fn_;
std::tuple<BoundArgs...> bound_;
template <class F, class... BA>
bind_back_wrapper(F&& f, BA&&... ba)
: fn_(std::forward<F>(f)), bound_(std::forward<BA>(ba)...)
{
}
R operator()(Leading... args) const
{
return std::apply([&](const auto&... ba) { return std::invoke(fn_, args..., ba...); }, bound_);
}
};
} // namespace detail
//=================================================================================================
/**
* @brief Drop-in replacement for std::bind_back with statically introspectable argument types.
*
* Stores the callable and its trailing bound arguments, then exposes a concrete non-template
* operator() whose parameter types are derived directly from the underlying callable's signature.
* This lets LuaBridge register the result with addFunction / addStaticFunction without any extra
* annotation.
*
* For member function pointers the class pointer is automatically prepended to the remaining
* (Lua-visible) parameter list so that LuaBridge can dispatch it as a proxy member function.
*
* @tparam F Callable type (function pointer, member function pointer, functor).
* @tparam BoundArgs Trailing argument types to bind.
* @param f The callable to wrap.
* @param args Trailing arguments forwarded into the wrapper by value.
* @return A callable object whose operator() accepts the remaining (leading) arguments.
*/
template <class F, class... BoundArgs>
auto bind_back(F&& f, BoundArgs&&... args)
{
using Fn = std::decay_t<F>;
using FnTraits = detail::function_traits<Fn>;
static constexpr std::size_t num_explicit = FnTraits::arity;
static constexpr std::size_t num_bound = sizeof...(BoundArgs);
static constexpr std::size_t num_remaining = num_explicit - num_bound;
using explicit_remaining = detail::tuple_take_first_t<num_remaining, typename FnTraits::argument_types>;
using leading = detail::bind_back_leading_t<Fn, explicit_remaining>;
using R = typename FnTraits::result_type;
return detail::bind_back_wrapper<R, leading, Fn, std::decay_t<BoundArgs>...>(
std::forward<F>(f), std::forward<BoundArgs>(args)...);
}
} // namespace luabridge
+77
View File
@@ -0,0 +1,77 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2023, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include "Stack.h"
#include <optional>
#include <type_traits>
namespace luabridge {
//=================================================================================================
/**
* @brief Get a global value from the lua_State.
*
* @note This works on any type specialized by `Stack`, including `LuaRef` and its table proxies.
*/
template <class T>
TypeResult<T> getGlobal(lua_State* L, const char* name)
{
lua_getglobal(L, name);
auto result = luabridge::Stack<T>::get(L, -1);
lua_pop(L, 1);
return result;
}
//=================================================================================================
/**
* @brief Try to get a field from a global table without creating a LuaRef.
*
* This is a fast-path helper for optional lookup patterns. It invokes normal Lua field access
* on the table, including metamethods, and returns std::nullopt when the global is not a table
* or the field cannot be converted to the requested type.
*/
template <class T>
std::optional<T> tryGetGlobalField(lua_State* L, const char* globalName, const char* fieldName)
{
const StackRestore stackRestore(L);
lua_getglobal(L, globalName);
if (! lua_istable(L, -1))
return std::nullopt;
lua_getfield(L, -1, fieldName);
auto result = Stack<std::decay_t<T>>::get(L, -1);
if (! result)
return std::nullopt;
return *result;
}
//=================================================================================================
/**
* @brief Set a global value in the lua_State.
*
* @note This works on any type specialized by `Stack`, including `LuaRef` and its table proxies.
*/
template <class T>
bool setGlobal(lua_State* L, T&& t, const char* name)
{
if (auto result = push(L, std::forward<T>(t)))
{
lua_setglobal(L, name);
return true;
}
return false;
}
} // namespace luabridge
+263
View File
@@ -0,0 +1,263 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2021, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include "Errors.h"
#include "Stack.h"
#include "LuaRef.h"
#include "LuaException.h"
#include <functional>
#include <tuple>
#include <type_traits>
#include <utility>
namespace luabridge {
//=================================================================================================
namespace detail {
template <class F>
bool is_handler_valid(const F& f) noexcept
{
if constexpr (std::is_pointer_v<remove_cvref_t<F>>)
return f != nullptr;
else if constexpr (std::is_constructible_v<bool, remove_cvref_t<F>>)
return static_cast<bool>(f);
else
return true;
}
template <class Tuple, std::size_t... Indices>
TypeResult<Tuple> decode_tuple_result(lua_State* L, int first_result_index, std::index_sequence<Indices...>)
{
auto results = std::make_tuple(
Stack<std::tuple_element_t<Indices, Tuple>>::get(L, first_result_index + static_cast<int>(Indices))...);
std::error_code ec;
const bool ok =
(([&]()
{
const auto& element = std::get<Indices>(results);
if (! element)
{
ec = element.error();
return false;
}
return true;
}())
&& ...);
if (! ok)
return ec;
return Tuple{ std::move(*std::get<Indices>(results))... };
}
template <class R>
TypeResult<R> decode_call_result(lua_State* L, int first_result_index, int num_returned_values)
{
if constexpr (std::is_same_v<R, void> || std::is_same_v<R, std::tuple<>>)
{
if (num_returned_values != 0)
return makeErrorCode(ErrorCode::InvalidTableSizeInCast);
return {};
}
else if constexpr (is_tuple_v<R>)
{
constexpr auto expected_size = static_cast<int>(std::tuple_size_v<R>);
if (num_returned_values != expected_size)
return makeErrorCode(ErrorCode::InvalidTableSizeInCast);
return decode_tuple_result<R>(L, first_result_index, std::make_index_sequence<expected_size>{});
}
else
{
if (num_returned_values < 1)
return makeErrorCode(ErrorCode::InvalidTypeCast);
return Stack<R>::get(L, first_result_index);
}
}
} // namespace detail
//=================================================================================================
/**
* @brief Safely call Lua code and decode the return values to R.
*/
template <class R, class Ref, class F, class... Args>
TypeResult<R> callWithHandler(const Ref& object, F&& errorHandler, Args&&... args)
{
static_assert(std::is_same_v<detail::remove_cvref_t<F>, detail::remove_cvref_t<decltype(std::ignore)>> || std::is_invocable_r_v<int, F, lua_State*>);
static constexpr bool isValidHandler =
!std::is_same_v<detail::remove_cvref_t<F>, detail::remove_cvref_t<decltype(std::ignore)>>;
lua_State* L = object.state();
const StackRestore stackRestore(L);
const int initialTop = lua_gettop(L);
bool hasHandler = false;
if constexpr (isValidHandler)
{
hasHandler = detail::is_handler_valid(errorHandler);
if (hasHandler)
detail::push_function(L, std::forward<F>(errorHandler), "");
}
object.push();
{
const auto [result, index] = detail::push_arguments(L, std::forward_as_tuple(args...));
if (! result)
return result.error();
}
const int messageHandlerIndex = hasHandler ? (initialTop + 1) : 0;
const int code = lua_pcall(L, sizeof...(Args), LUA_MULTRET, messageHandlerIndex);
if (code != LUABRIDGE_LUA_OK)
{
auto ec = makeErrorCode(ErrorCode::LuaFunctionCallFailed);
#if LUABRIDGE_HAS_EXCEPTIONS
if constexpr (! isValidHandler)
{
if (LuaException::areExceptionsEnabled(L))
LuaException::raise(L, ec);
}
#endif
lua_pop(L, 1);
return ec;
}
if (hasHandler)
lua_remove(L, initialTop + 1);
const int firstResultIndex = initialTop + 1;
const int numReturnedValues = lua_gettop(L) - initialTop;
return detail::decode_call_result<R>(L, firstResultIndex, numReturnedValues);
}
template <class Ref, class F, class... Args>
TypeResult<void> callWithHandler(const Ref& object, F&& errorHandler, Args&&... args)
{
return callWithHandler<void, Ref, F, Args...>(object, std::forward<F>(errorHandler), std::forward<Args>(args)...);
}
template <class R = void, class Ref, class... Args>
TypeResult<R> call(const Ref& object, Args&&... args)
{
return callWithHandler<R>(object, std::ignore, std::forward<Args>(args)...);
}
template <class Signature>
class LuaFunction;
template <class R, class... Args>
class LuaFunction<R(Args...)>
{
public:
LuaFunction() = default;
explicit LuaFunction(const LuaRef& function)
: m_function(function)
{
}
explicit LuaFunction(LuaRef&& function)
: m_function(std::move(function))
{
}
[[nodiscard]] TypeResult<R> operator()(Args... args) const
{
return call(std::forward<Args>(args)...);
}
[[nodiscard]] TypeResult<R> call(Args... args) const
{
return luabridge::call<R>(m_function, std::forward<Args>(args)...);
}
template <class F>
[[nodiscard]] TypeResult<R> callWithHandler(F&& errorHandler, Args... args) const
{
return luabridge::callWithHandler<R>(m_function, std::forward<F>(errorHandler), std::forward<Args>(args)...);
}
[[nodiscard]] bool isValid() const
{
return m_function.isCallable();
}
[[nodiscard]] const LuaRef& ref() const
{
return m_function;
}
private:
LuaRef m_function;
};
//=============================================================================================
/**
* @brief Wrapper for `lua_pcall` that throws if exceptions are enabled.
*/
inline int pcall(lua_State* L, int nargs = 0, int nresults = 0, int msgh = 0)
{
const int code = lua_pcall(L, nargs, nresults, msgh);
#if LUABRIDGE_HAS_EXCEPTIONS
if (code != LUABRIDGE_LUA_OK && LuaException::areExceptionsEnabled(L))
LuaException::raise(L, makeErrorCode(ErrorCode::LuaFunctionCallFailed));
#endif
return code;
}
//=============================================================================================
template <class Impl, class LuaRef>
template <class R, class... Args>
TypeResult<R> LuaRefBase<Impl, LuaRef>::call(Args&&... args) const
{
return luabridge::call<R>(impl(), std::forward<Args>(args)...);
}
template <class Impl, class LuaRef>
template <class... Args>
TypeResult<void> LuaRefBase<Impl, LuaRef>::operator()(Args&&... args) const
{
return call<void>(std::forward<Args>(args)...);
}
template <class Impl, class LuaRef>
template <class R, class F, class... Args>
TypeResult<R> LuaRefBase<Impl, LuaRef>::callWithHandler(F&& errorHandler, Args&&... args) const
{
return luabridge::callWithHandler<R>(impl(), std::forward<F>(errorHandler), std::forward<Args>(args)...);
}
template <class Impl, class LuaRef>
template <class F, class... Args>
TypeResult<void> LuaRefBase<Impl, LuaRef>::callWithHandler(F&& errorHandler, Args&&... args) const
{
return callWithHandler<void>(std::forward<F>(errorHandler), std::forward<Args>(args)...);
}
template <class Impl, class LuaRef>
template <class Signature>
LuaFunction<Signature> LuaRefBase<Impl, LuaRef>::callable() const
{
const StackRestore stackRestore(m_L);
impl().push(m_L);
return LuaFunction<Signature>(LuaRef::fromStack(m_L));
}
} // namespace luabridge
+256
View File
@@ -0,0 +1,256 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2018, Dmitry Tarakanov
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// SPDX-License-Identifier: MIT
#pragma once
#include "LuaRef.h"
#include <utility>
#if LUABRIDGE_HAS_CXX20_RANGES
#include <iterator>
#include <ranges>
#endif
namespace luabridge {
//=================================================================================================
/**
* @brief Iterator class to allow table iteration.
*
* @see Range class.
*/
class Iterator
{
public:
explicit Iterator(const LuaRef& table, bool isEnd = false)
: m_L(table.state())
, m_table(table)
, m_key(table.state()) // m_key is nil
, m_value(table.state()) // m_value is nil
{
if (! isEnd)
{
next(); // get the first (key, value) pair from table
}
}
#if LUABRIDGE_HAS_CXX20_RANGES
using value_type = std::pair<LuaRef, LuaRef>;
using difference_type = std::ptrdiff_t;
using iterator_concept = std::input_iterator_tag;
#endif
/**
* @brief Return an associated Lua state.
*
* @return A Lua state.
*/
lua_State* state() const noexcept
{
return m_L;
}
/**
* @brief Dereference the iterator.
*
* @return A key-value pair for a current table entry.
*/
std::pair<LuaRef, LuaRef> operator*() const
{
return std::make_pair(m_key, m_value);
}
/**
* @brief Return the value referred by the iterator.
*
* @return A value for the current table entry.
*/
LuaRef operator->() const
{
return m_value;
}
/**
* @brief Compare two iterators.
*
* @param rhs Another iterator.
*
* @return True if iterators point to the same entry of the same table, false otherwise.
*/
bool operator!=(const Iterator& rhs) const
{
LUABRIDGE_ASSERT(m_L == rhs.m_L);
return ! m_table.rawequal(rhs.m_table) || ! m_key.rawequal(rhs.m_key);
}
/**
* @brief Move the iterator to the next table entry.
*
* @return This iterator.
*/
Iterator& operator++()
{
if (isNil())
{
// if the iterator reaches the end, do nothing
return *this;
}
else
{
next();
return *this;
}
}
/**
* @brief Check if the iterator points after the last table entry.
*
* @return True if there are no more table entries to iterate, false otherwise.
*/
bool isNil() const noexcept
{
return m_key.isNil();
}
/**
* @brief Return the key for the current table entry.
*
* @return A reference to the entry key.
*/
LuaRef key() const
{
return m_key;
}
/**
* @brief Return the key for the current table entry.
*
* @return A reference to the entry value.
*/
LuaRef value() const
{
return m_value;
}
private:
// Don't use postfix increment, it is less efficient
Iterator operator++(int);
void next()
{
#if LUABRIDGE_SAFE_STACK_CHECKS
if (! lua_checkstack(m_L, 2))
{
m_key = LuaNil();
m_value = LuaNil();
return;
}
#endif
m_table.push();
m_key.push();
if (lua_next(m_L, -2))
{
m_value.pop();
m_key.pop();
}
else
{
m_key = LuaNil();
m_value = LuaNil();
}
lua_pop(m_L, 1);
}
lua_State* m_L = nullptr;
LuaRef m_table;
LuaRef m_key;
LuaRef m_value;
};
//=================================================================================================
/**
* @brief Range class taking two table iterators.
*/
class Range
{
public:
Range(const Iterator& begin, const Iterator& end)
: m_begin(begin)
, m_end(end)
{
}
const Iterator& begin() const noexcept
{
return m_begin;
}
const Iterator& end() const noexcept
{
return m_end;
}
private:
Iterator m_begin;
Iterator m_end;
};
//=================================================================================================
/**
* @brief Return a range for the Lua table reference.
*
* @return A range suitable for range-based for statement.
*/
inline Range pairs(const LuaRef& table)
{
return Range{ Iterator(table, false), Iterator(table, true) };
}
#if LUABRIDGE_HAS_CXX20_RANGES
/**
* @brief Equality comparison for Iterator.
*/
inline bool operator==(const Iterator& lhs, const Iterator& rhs)
{
if (lhs.isNil() && rhs.isNil())
return true;
if (lhs.isNil() != rhs.isNil())
return false;
return lhs.key().rawequal(rhs.key()) && lhs.value().rawequal(rhs.value());
}
/**
* @brief Sentinel type for Iterator end detection.
*/
struct IteratorSentinel {};
/**
* @brief Sentinel equality: Iterator is at end when isNil().
*/
inline bool operator==(const Iterator& it, IteratorSentinel)
{
return it.isNil();
}
inline bool operator==(IteratorSentinel, const Iterator& it)
{
return it.isNil();
}
#endif // LUABRIDGE_HAS_CXX20_RANGES
} // namespace luabridge
#if LUABRIDGE_HAS_CXX20_RANGES
template <>
inline constexpr bool std::ranges::enable_borrowed_range<luabridge::Range> = false;
#endif
+189
View File
@@ -0,0 +1,189 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2021, kunitoki
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// Copyright 2008, Nigel Atkinson <suprapilot+LuaCode@gmail.com>
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include "ClassInfo.h"
#include "LuaHelpers.h"
#include <string>
#include <sstream>
#include <exception>
namespace luabridge {
//================================================================================================
class LuaException : public std::exception
{
public:
//=============================================================================================
/**
* @brief Construct a LuaException after a lua_pcall().
*
* Assumes the error string is on top of the stack, but provides a generic error message otherwise.
*/
LuaException(lua_State* L, std::error_code code)
: m_L(L)
, m_code(code)
{
}
~LuaException() noexcept override
{
}
//=============================================================================================
/**
* @brief Return the error message.
*/
const char* what() const noexcept override
{
return m_what.c_str();
}
//=============================================================================================
/**
* @brief Throw an exception or raises a luaerror when exceptions are disabled.
*/
static void raise(lua_State* L, std::error_code code)
{
LUABRIDGE_ASSERT(areExceptionsEnabled(L));
#if LUABRIDGE_HAS_EXCEPTIONS
throw LuaException(L, code, FromLua{});
#else
unused(L, code);
std::abort();
#endif
}
//=============================================================================================
/**
* @brief Check if exceptions are enabled.
*/
static bool areExceptionsEnabled(lua_State* L) noexcept
{
lua_pushlightuserdata(L, detail::getExceptionsKey());
lua_gettable(L, LUA_REGISTRYINDEX);
const bool enabled = lua_isboolean(L, -1) ? static_cast<bool>(lua_toboolean(L, -1)) : false;
lua_pop(L, 1);
return enabled;
}
/**
* @brief Initializes error handling.
*
* Subsequent Lua errors are translated to C++ exceptions, or logging and abort if exceptions are disabled.
*/
static void enableExceptions(lua_State* L) noexcept
{
lua_pushlightuserdata(L, detail::getExceptionsKey());
lua_pushboolean(L, true);
lua_settable(L, LUA_REGISTRYINDEX);
#if LUABRIDGE_HAS_EXCEPTIONS && LUABRIDGE_ON_LUAJIT
lua_pushlightuserdata(L, (void*)luajitWrapperCallback);
luaJIT_setmode(L, -1, LUAJIT_MODE_WRAPCFUNC | LUAJIT_MODE_ON);
lua_pop(L, 1);
#endif
#if LUABRIDGE_ON_LUAU
auto callbacks = lua_callbacks(L);
callbacks->panic = +[](lua_State* L, int) { panicHandlerCallback(L); };
#else
lua_atpanic(L, panicHandlerCallback);
#endif
}
//=============================================================================================
/**
* @brief Retrieve the lua_State associated with the exception.
*
* @return A Lua state.
*/
lua_State* state() const { return m_L; }
private:
struct FromLua {};
LuaException(lua_State* L, std::error_code code, FromLua)
: m_L(L)
, m_code(code)
{
whatFromStack();
}
void whatFromStack()
{
std::stringstream ss;
const char* errorText = nullptr;
if (lua_gettop(m_L) > 0)
{
errorText = lua_tostring(m_L, -1);
lua_pop(m_L, 1);
}
ss << (errorText ? errorText : "Unknown error") << " (code=" << m_code.message() << ")";
m_what = std::move(ss).str();
}
static int panicHandlerCallback(lua_State* L)
{
#if LUABRIDGE_HAS_EXCEPTIONS
throw LuaException(L, makeErrorCode(ErrorCode::LuaFunctionCallFailed), FromLua{});
#else
unused(L);
std::abort();
#endif
}
#if LUABRIDGE_HAS_EXCEPTIONS && LUABRIDGE_ON_LUAJIT
static int luajitWrapperCallback(lua_State* L, lua_CFunction f)
{
try
{
return f(L);
}
catch (const std::exception& e)
{
lua_pushstring(L, e.what());
lua_error_x(L);
}
}
#endif
lua_State* m_L = nullptr;
std::error_code m_code;
std::string m_what;
};
//=================================================================================================
/**
* @brief Initializes error handling using C++ exceptions.
*
* Subsequent Lua errors are translated to C++ exceptions. It aborts the application if called when no exceptions.
*/
inline void enableExceptions(lua_State* L) noexcept
{
#if LUABRIDGE_HAS_EXCEPTIONS
LuaException::enableExceptions(L);
#else
unused(L);
LUABRIDGE_ASSERT(false); // Never call this function when exceptions are not enabled.
#endif
}
} // namespace luabridge
+756
View File
@@ -0,0 +1,756 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// Copyright 2007, Nathan Reed
// SPDX-License-Identifier: MIT
#pragma once
#include "FuncTraits.h"
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <limits>
#include <type_traits>
#include <utility>
namespace luabridge {
/**
* @brief Helper for unused vars.
*/
template <class... Args>
constexpr void unused(Args&&...)
{
}
// These are for Lua versions prior to 5.2.0.
#if LUA_VERSION_NUM < 502
using lua_Unsigned = std::make_unsigned_t<lua_Integer>;
#if ! LUABRIDGE_ON_LUAU
inline int lua_absindex(lua_State* L, int idx)
{
if (idx > LUA_REGISTRYINDEX && idx < 0)
return lua_gettop(L) + idx + 1;
else
return idx;
}
#endif
#define LUA_OPEQ 1
#define LUA_OPLT 2
#define LUA_OPLE 3
inline int lua_compare(lua_State* L, int idx1, int idx2, int op)
{
switch (op)
{
case LUA_OPEQ:
return lua_equal(L, idx1, idx2);
case LUA_OPLT:
return lua_lessthan(L, idx1, idx2);
case LUA_OPLE:
return lua_equal(L, idx1, idx2) || lua_lessthan(L, idx1, idx2);
default:
return 0;
}
}
#if ! LUABRIDGE_ON_LUAJIT
inline void* luaL_testudata(lua_State* L, int ud, const char* tname)
{
void* p = lua_touserdata(L, ud);
if (p == nullptr)
return nullptr;
if (! lua_getmetatable(L, ud))
return nullptr;
luaL_getmetatable(L, tname);
if (! lua_rawequal(L, -1, -2))
p = nullptr;
lua_pop(L, 2);
return p;
}
#endif
inline int get_length(lua_State* L, int idx)
{
return static_cast<int>(lua_objlen(L, idx));
}
#else // LUA_VERSION_NUM >= 502
inline int get_length(lua_State* L, int idx)
{
return static_cast<int>(lua_rawlen(L, idx));
}
#endif // LUA_VERSION_NUM < 502
// These functions and defines are for Luau.
#if LUABRIDGE_ON_LUAU
inline int luaL_ref(lua_State* L, int idx)
{
LUABRIDGE_ASSERT(idx == LUA_REGISTRYINDEX);
const int ref = lua_ref(L, -1);
lua_pop(L, 1);
return ref;
}
inline void luaL_unref(lua_State* L, int idx, int ref)
{
unused(idx);
lua_unref(L, ref);
}
template <class T>
inline void* lua_newuserdata_x(lua_State* L, size_t sz)
{
return lua_newuserdatadtor(L, sz, [](void* x)
{
T* object = static_cast<T*>(x);
object->~T();
});
}
inline void lua_pushcfunction_x(lua_State *L, lua_CFunction fn, const char* debugname)
{
lua_pushcfunction(L, fn, debugname);
}
inline void lua_pushcclosure_x(lua_State* L, lua_CFunction fn, const char* debugname, int n)
{
lua_pushcclosure(L, fn, debugname, n);
}
[[noreturn]] inline void lua_error_x(lua_State* L)
{
lua_error(L);
}
inline int lua_getstack_x(lua_State* L, int level, lua_Debug* ar)
{
return lua_getinfo(L, level, "nlS", ar);
}
inline int lua_getstack_info_x(lua_State* L, int level, const char* what, lua_Debug* ar)
{
return lua_getinfo(L, level, what, ar);
}
inline int lua_rawgetp_x(lua_State* L, int idx, void* p)
{
return lua_rawgetp(L, idx, p);
}
inline void lua_rawsetp_x(lua_State* L, int idx, void* p)
{
lua_rawsetp(L, idx, p);
}
#else
using ::luaL_ref;
using ::luaL_unref;
template <class T>
inline void* lua_newuserdata_x(lua_State* L, size_t sz)
{
return lua_newuserdata(L, sz);
}
inline void lua_pushcfunction_x(lua_State *L, lua_CFunction fn, const char* debugname)
{
unused(debugname);
lua_pushcfunction(L, fn);
}
inline void lua_pushcclosure_x(lua_State* L, lua_CFunction fn, const char* debugname, int n)
{
unused(debugname);
lua_pushcclosure(L, fn, n);
}
[[noreturn]] inline void lua_error_x(lua_State* L)
{
lua_error(L);
detail::unreachable();
}
inline int lua_getstack_x(lua_State* L, int level, lua_Debug* ar)
{
return lua_getstack(L, level, ar);
}
inline int lua_getstack_info_x(lua_State* L, int level, const char* what, lua_Debug* ar)
{
lua_getstack(L, level, ar);
return lua_getinfo(L, what, ar);
}
inline int lua_rawgetp_x(lua_State* L, int idx, void* p)
{
#if LUA_VERSION_NUM < 503
idx = lua_absindex(L, idx);
luaL_checkstack(L, 1, "not enough stack slots");
lua_pushlightuserdata(L, p);
lua_rawget(L, idx);
return lua_type(L, -1);
#else
return lua_rawgetp(L, idx, p);
#endif
}
inline void lua_rawsetp_x(lua_State* L, int idx, void* p)
{
#if LUA_VERSION_NUM < 503
idx = lua_absindex(L, idx);
luaL_checkstack(L, 1, "not enough stack slots");
lua_pushlightuserdata(L, p);
lua_insert(L, -2);
lua_rawset(L, idx);
#else
lua_rawsetp(L, idx, p);
#endif
}
#endif // LUABRIDGE_ON_LUAU
// These are for Lua versions prior to 5.5.0.
#if LUA_VERSION_NUM < 505
inline lua_State* lua_newstate_x(lua_Alloc f, void* ud, [[maybe_unused]] unsigned seed)
{
return lua_newstate(f, ud);
}
#else
inline lua_State* lua_newstate_x(lua_Alloc f, void* ud, unsigned seed)
{
return lua_newstate(f, ud, seed);
}
#endif
// These are for Lua versions prior to 5.3.0.
#if LUA_VERSION_NUM < 503
inline lua_Number to_numberx(lua_State* L, int idx, int* isnum)
{
lua_Number n = lua_tonumber(L, idx);
if (isnum)
*isnum = (n != 0 || lua_isnumber(L, idx));
return n;
}
inline lua_Integer to_integerx(lua_State* L, int idx, int* isnum)
{
int ok = 0;
lua_Number n = to_numberx(L, idx, &ok);
if (ok)
{
if (n < static_cast<lua_Number>(std::numeric_limits<lua_Integer>::min()) ||
n >= -static_cast<lua_Number>(std::numeric_limits<lua_Integer>::min()))
{
if (isnum)
*isnum = 0;
return 0;
}
const auto int_n = static_cast<lua_Integer>(n);
if (n == static_cast<lua_Number>(int_n))
{
if (isnum)
*isnum = 1;
return int_n;
}
}
if (isnum)
*isnum = 0;
return 0;
}
#endif // LUA_VERSION_NUM < 503
inline int lua_rawgetp_x(lua_State* L, int idx, const void* p)
{
return lua_rawgetp_x(L, idx, const_cast<void*>(p));
}
inline void lua_rawsetp_x(lua_State* L, int idx, const void* p)
{
lua_rawsetp_x(L, idx, const_cast<void*>(p));
}
#ifndef LUA_OK
#define LUABRIDGE_LUA_OK 0
#else
#define LUABRIDGE_LUA_OK LUA_OK
#endif
/**
* @brief Helper to throw or return an error code.
*/
template <class T, class ErrorType>
std::error_code throw_or_error_code(ErrorType error)
{
#if LUABRIDGE_HAS_EXCEPTIONS
throw T(makeErrorCode(error).message().c_str());
#else
return makeErrorCode(error);
#endif
}
template <class T, class ErrorType>
std::error_code throw_or_error_code(lua_State* L, ErrorType error)
{
#if LUABRIDGE_HAS_EXCEPTIONS
throw T(L, makeErrorCode(error));
#else
return unused(L), makeErrorCode(error);
#endif
}
/**
* @brief Helper to throw or LUABRIDGE_ASSERT.
*/
template <class T, class... Args>
void throw_or_assert(Args&&... args)
{
#if LUABRIDGE_HAS_EXCEPTIONS
throw T(std::forward<Args>(args)...);
#else
unused(std::forward<Args>(args)...);
LUABRIDGE_ASSERT(false);
#endif
}
/**
* @brief Helper to set unsigned.
*/
template <class T>
void pushunsigned(lua_State* L, T value)
{
static_assert(std::is_unsigned_v<T>);
lua_pushinteger(L, static_cast<lua_Integer>(value));
}
/**
* @brief Helper to convert to integer.
*/
inline lua_Number tonumber(lua_State* L, int idx, int* isnum)
{
#if ! LUABRIDGE_ON_LUAU && LUA_VERSION_NUM > 502
return lua_tonumberx(L, idx, isnum);
#else
return to_numberx(L, idx, isnum);
#endif
}
/**
* @brief Helper to convert to integer.
*/
inline lua_Integer tointeger(lua_State* L, int idx, int* isnum)
{
#if ! LUABRIDGE_ON_LUAU && LUA_VERSION_NUM > 502
return lua_tointegerx(L, idx, isnum);
#else
return to_integerx(L, idx, isnum);
#endif
}
/**
* @brief Register main thread, only supported on 5.1.
*/
inline constexpr char main_thread_name[] = "__luabridge_main_thread";
inline void register_main_thread(lua_State* threadL)
{
#if LUA_VERSION_NUM < 502
if (threadL == nullptr)
lua_pushnil(threadL);
else
lua_pushthread(threadL);
lua_setglobal(threadL, main_thread_name);
#else
unused(threadL);
#endif
}
/**
* @brief Get main thread, not supported on 5.1.
*/
inline lua_State* main_thread(lua_State* threadL)
{
#if LUA_VERSION_NUM < 502
lua_getglobal(threadL, main_thread_name);
if (lua_isthread(threadL, -1))
{
auto L = lua_tothread(threadL, -1);
lua_pop(threadL, 1);
return L;
}
LUABRIDGE_ASSERT(false); // Have you forgot to call luabridge::registerMainThread ?
lua_pop(threadL, 1);
return threadL;
#else
lua_rawgeti(threadL, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD);
lua_State* L = lua_tothread(threadL, -1);
lua_pop(threadL, 1);
return L;
#endif
}
/**
* @brief Get a table value, bypassing metamethods.
*/
inline int rawgetfield(lua_State* L, int index, const char* key)
{
LUABRIDGE_ASSERT(lua_istable(L, index));
index = lua_absindex(L, index);
lua_pushstring(L, key);
#if LUA_VERSION_NUM <= 502
lua_rawget(L, index);
return lua_type(L, -1);
#else
return lua_rawget(L, index);
#endif
}
/**
* @brief Set a table value, bypassing metamethods.
*/
inline void rawsetfield(lua_State* L, int index, const char* key)
{
LUABRIDGE_ASSERT(lua_istable(L, index));
index = lua_absindex(L, index);
lua_pushstring(L, key);
lua_insert(L, -2);
lua_rawset(L, index);
}
/**
* @brief Returns true if the value is a full userdata (not light).
*/
[[nodiscard]] inline bool isfulluserdata(lua_State* L, int index)
{
return lua_isuserdata(L, index) && !lua_islightuserdata(L, index);
}
/**
* @brief Test lua_State objects for global equality.
*
* This can determine if two different lua_State objects really point
* to the same global state, such as when using coroutines.
*
* @note This is used for assertions.
*/
[[nodiscard]] inline bool equalstates(lua_State* L1, lua_State* L2)
{
return lua_topointer(L1, LUA_REGISTRYINDEX) == lua_topointer(L2, LUA_REGISTRYINDEX);
}
/**
* @brief Return the size of lua table, even if not a sequence { 1=x, 2=y, 3=... }.
*/
[[nodiscard]] inline int table_length(lua_State* L, int index)
{
LUABRIDGE_ASSERT(lua_istable(L, index));
int items_count = 0;
lua_pushnil(L);
while (lua_next(L, index) != 0)
{
++items_count;
lua_pop(L, 1);
}
return items_count;
}
/**
* @brief Return an aligned pointer of type T.
*/
template <class T>
[[nodiscard]] T* align(void* ptr) noexcept
{
const auto address = reinterpret_cast<size_t>(ptr);
const auto offset = address % alignof(T);
const auto aligned_address = (offset == 0) ? address : (address + alignof(T) - offset);
return reinterpret_cast<T*>(aligned_address);
}
/**
* @brief Return if a pointer of type T is aligned.
*/
template <std::size_t Alignment, class T, std::enable_if_t<std::is_pointer_v<T>, int> = 0>
[[nodiscard]] bool is_aligned(T address) noexcept
{
static_assert(Alignment > 0u);
return (reinterpret_cast<std::uintptr_t>(address) & (Alignment - 1u)) == 0u;
}
/**
* @brief Return the space needed to align the type T on an unaligned address.
*/
template <class T>
[[nodiscard]] constexpr size_t maximum_space_needed_to_align() noexcept
{
return sizeof(T) + alignof(T) - 1;
}
/**
* @brief Deallocate lua userdata taking into account alignment.
*/
template <class T>
int lua_deleteuserdata_aligned(lua_State* L)
{
LUABRIDGE_ASSERT(isfulluserdata(L, 1));
T* aligned = align<T>(lua_touserdata(L, 1));
aligned->~T();
return 0;
}
/**
* @brief Allocate lua userdata taking into account alignment.
*
* Using this instead of lua_newuserdata directly prevents alignment warnings on 64bits platforms.
*/
template <class T, class... Args>
void* lua_newuserdata_aligned(lua_State* L, Args&&... args)
{
using U = std::remove_reference_t<T>;
#if LUABRIDGE_ON_LUAU
void* pointer = lua_newuserdatadtor(L, maximum_space_needed_to_align<U>(), [](void* x)
{
U* aligned = align<U>(x);
aligned->~U();
});
#else
void* pointer = lua_newuserdata_x<U>(L, maximum_space_needed_to_align<U>());
lua_newtable(L);
lua_pushcfunction_x(L, &lua_deleteuserdata_aligned<U>, "");
rawsetfield(L, -2, "__gc");
lua_setmetatable(L, -2);
#endif
U* aligned = align<U>(pointer);
new (aligned) U(std::forward<Args>(args)...);
return pointer;
}
/**
* @brief Safe error able to walk backwards for error reporting correctly.
*/
[[noreturn]] inline void raise_lua_error(lua_State* L, const char* fmt, ...)
{
va_list argp;
va_start(argp, fmt);
lua_pushvfstring(L, fmt, argp);
va_end(argp);
const char* message = lua_tostring(L, -1);
if (message != nullptr)
{
if (auto str = std::string_view(message); !str.empty() && str[0] == '[')
lua_error_x(L);
}
bool pushed_error = false;
for (int level = 1; level <= 2; ++level)
{
lua_Debug ar;
#if LUABRIDGE_ON_LUAU
if (lua_getinfo(L, level, "sl", &ar) == 0)
continue;
#else
if (lua_getstack(L, level, &ar) == 0 || lua_getinfo(L, "Sl", &ar) == 0)
continue;
#endif
if (ar.currentline <= 0)
continue;
lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline);
pushed_error = true;
break;
}
if (! pushed_error)
lua_pushliteral(L, "");
lua_pushvalue(L, -2);
lua_remove(L, -3);
lua_concat(L, 2);
lua_error_x(L);
}
/**
* @brief Checks if the value on the stack is a number type and can fit into the corresponding c++ integral type..
*/
template <class U = lua_Integer, class T>
constexpr bool is_integral_representable_by(T value)
{
constexpr bool same_signedness = (std::is_unsigned_v<T> && std::is_unsigned_v<U>)
|| (!std::is_unsigned_v<T> && !std::is_unsigned_v<U>);
if constexpr (sizeof(T) == sizeof(U))
{
if constexpr (same_signedness)
{
return true;
}
else if constexpr (std::is_unsigned_v<T>)
{
return value <= static_cast<T>((std::numeric_limits<U>::max)());
}
else
{
return value >= static_cast<T>((std::numeric_limits<U>::min)())
&& static_cast<U>(value) <= (std::numeric_limits<U>::max)();
}
}
else if constexpr (sizeof(T) < sizeof(U))
{
return static_cast<U>(value) >= (std::numeric_limits<U>::min)()
&& static_cast<U>(value) <= (std::numeric_limits<U>::max)();
}
else if constexpr (std::is_unsigned_v<T>)
{
return value <= static_cast<T>((std::numeric_limits<U>::max)());
}
else
{
return value >= static_cast<T>((std::numeric_limits<U>::min)())
&& value <= static_cast<T>((std::numeric_limits<U>::max)());
}
}
template <class U = lua_Integer>
bool is_integral_representable_by(lua_State* L, int index)
{
int isValid = 0;
const auto value = tointeger(L, index, &isValid);
return isValid ? is_integral_representable_by<U>(value) : false;
}
/**
* @brief Checks if the value on the stack is a number type and can fit into the corresponding c++ numerical type..
*/
template <class U = lua_Number, class T>
bool is_floating_point_representable_by(T value)
{
if constexpr (sizeof(T) == sizeof(U))
{
return true;
}
else if constexpr (sizeof(T) < sizeof(U))
{
if (std::isnan(value) || std::isinf(value))
return true;
return static_cast<U>(value) >= -(std::numeric_limits<U>::max)()
&& static_cast<U>(value) <= (std::numeric_limits<U>::max)();
}
else
{
if (std::isnan(value) || std::isinf(value))
return true;
return value >= static_cast<T>(-(std::numeric_limits<U>::max)())
&& value <= static_cast<T>((std::numeric_limits<U>::max)());
}
}
template <class U = lua_Number>
bool is_floating_point_representable_by(lua_State* L, int index)
{
int isValid = 0;
const auto value = tonumber(L, index, &isValid);
return isValid ? is_floating_point_representable_by<U>(value) : false;
}
/**
* @brief Portable wrapper for lua_resume that normalises calling convention differences
* across Lua 5.1/LuaJIT (no from, no nresults), 5.2-5.3 (from but no nresults), and 5.4+ (from + nresults).
*
* @param L The coroutine thread to resume.
* @param from The thread doing the resuming (may be nullptr on older Lua).
* @param nargs Number of arguments on L's stack to pass to the resumed function.
* @param nresults Output: number of values on L's stack after resume (yielded or returned).
* For Lua 5.4+, filled directly by lua_resume. For older versions, computed via lua_gettop.
* @returns LUA_OK, LUA_YIELD, or an error code.
*/
inline int lua_resume_x(lua_State* L, lua_State* from, int nargs, int* nresults = nullptr)
{
#if LUABRIDGE_ON_LUAJIT || LUA_VERSION_NUM == 501
unused(from);
int status = lua_resume(L, nargs);
if (nresults)
*nresults = lua_gettop(L);
return status;
#elif LUABRIDGE_ON_LUAU || LUABRIDGE_ON_RAVI || LUA_VERSION_NUM < 504
int status = lua_resume(L, from, nargs);
if (nresults)
*nresults = lua_gettop(L);
return status;
#else
int nr = 0;
int status = lua_resume(L, from, nargs, &nr);
if (nresults)
*nresults = nr;
return status;
#endif
}
/**
* @brief Returns true if the currently running C function can yield via lua_yieldk.
*
* Returns false on Lua 5.1, LuaJIT, and Luau where lua_yieldk is unavailable.
*/
inline bool lua_isyieldable_x(lua_State* L)
{
#if LUABRIDGE_ON_LUAJIT || LUA_VERSION_NUM == 501 || LUABRIDGE_ON_LUAU
unused(L);
return false;
#elif LUA_VERSION_NUM < 503
unused(L);
return true; // lua_yieldk exists in 5.2; assume yieldable when reached
#else
return lua_isyieldable(L) != 0;
#endif
}
} // namespace luabridge
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+50
View File
@@ -0,0 +1,50 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2023, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "FlagSet.h"
#include <cstdint>
namespace luabridge {
//=================================================================================================
namespace detail {
struct OptionExtensibleClass;
struct OptionAllowOverridingMethods;
struct OptionVisibleMetatables;
} // namespace Detail
/**
* @brief Options for the library.
*/
using Options = FlagSet<uint32_t,
detail::OptionExtensibleClass,
detail::OptionAllowOverridingMethods,
detail::OptionVisibleMetatables>;
/**
* @brief Set of default options.
*
* This setting means all options are not enabled.
*/
static inline constexpr Options defaultOptions = Options();
/**
* @brief Enable extensible C++ classes when registering them.
*/
static inline constexpr Options extensibleClass = Options::Value<detail::OptionExtensibleClass>();
/**
* @brief Allow to be able to override methods from lua in extensible C++ classes.
*/
static inline constexpr Options allowOverridingMethods = Options::Value<detail::OptionAllowOverridingMethods>();
/**
* @brief Specify if metatables are visible for namespaces and classes.
*/
static inline constexpr Options visibleMetatables = Options::Value<detail::OptionVisibleMetatables>();
} // namespace luabridge
+84
View File
@@ -0,0 +1,84 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2020, Dmitry Tarakanov
// Copyright 2019, George Tokmaji
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include "Errors.h"
#include "Stack.h"
#include "TypeTraits.h"
#include "Userdata.h"
#include <functional>
#include <tuple>
namespace luabridge {
//=================================================================================================
/**
* @brief Overloaded objects.
*/
template <class... Args>
struct NonConstOverload
{
template <class R, class T>
constexpr auto operator()(R (T::*ptr)(Args...)) const noexcept -> decltype(ptr)
{
return ptr;
}
template <class R, class T>
static constexpr auto with(R (T::*ptr)(Args...)) noexcept -> decltype(ptr)
{
return ptr;
}
};
template <class... Args>
struct ConstOverload
{
template <class R, class T>
constexpr auto operator()(R (T::*ptr)(Args...) const) const noexcept -> decltype(ptr)
{
return ptr;
}
template <class R, class T>
static constexpr auto with(R (T::*ptr)(Args...) const) noexcept -> decltype(ptr)
{
return ptr;
}
};
template <class... Args>
struct Overload : ConstOverload<Args...>, NonConstOverload<Args...>
{
using ConstOverload<Args...>::operator();
using NonConstOverload<Args...>::operator();
template <class R>
constexpr auto operator()(R (*ptr)(Args...)) const noexcept -> decltype(ptr)
{
return ptr;
}
template <class R, class T>
static constexpr auto with(R (T::*ptr)(Args...)) noexcept -> decltype(ptr)
{
return ptr;
}
};
//=================================================================================================
/**
* @brief Overload resolution.
*/
template <class... Args> [[maybe_unused]] constexpr Overload<Args...> overload = {};
template <class... Args> [[maybe_unused]] constexpr ConstOverload<Args...> constOverload = {};
template <class... Args> [[maybe_unused]] constexpr NonConstOverload<Args...> nonConstOverload = {};
} // namespace luabridge
+257
View File
@@ -0,0 +1,257 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2022, kunitoki
// SPDX-License-Identifier: MIT
#pragma once
#include "Errors.h"
#include <type_traits>
namespace luabridge {
//=================================================================================================
/**
* @brief Simple result class containing a result.
*/
struct Result
{
Result() noexcept = default;
Result(std::error_code ec) noexcept
: m_ec(ec)
{
}
Result(const Result&) noexcept = default;
Result(Result&&) noexcept = default;
Result& operator=(const Result&) noexcept = default;
Result& operator=(Result&&) noexcept = default;
explicit operator bool() const noexcept
{
return !m_ec;
}
std::error_code error() const noexcept
{
return m_ec;
}
const char* error_cstr() const noexcept
{
return detail::ErrorCategory::errorString(m_ec.value());
}
operator std::error_code() const noexcept
{
return m_ec;
}
std::string message() const
{
return m_ec.message();
}
#if LUABRIDGE_HAS_EXCEPTIONS
void throw_on_error() const
{
if (m_ec)
throw std::system_error(m_ec);
}
#endif
private:
std::error_code m_ec;
};
//=================================================================================================
/**
* @brief Simple result class containing or a type T or an error code.
*/
template <class T>
struct TypeResult
{
TypeResult() noexcept = default;
template <class U, class = std::enable_if_t<std::is_convertible_v<U, T> && !std::is_same_v<std::decay_t<U>, std::error_code>>>
TypeResult(U&& value) noexcept
: m_value(std::in_place, std::forward<U>(value))
{
}
TypeResult(std::error_code ec) noexcept
: m_value(makeUnexpected(ec))
{
}
TypeResult(const TypeResult&) = default;
TypeResult(TypeResult&&) = default;
TypeResult& operator=(const TypeResult&) = default;
TypeResult& operator=(TypeResult&&) = default;
explicit operator bool() const noexcept
{
return m_value.hasValue();
}
const T& value() const
{
return m_value.value();
}
T& operator*() &
{
return m_value.value();
}
T operator*() &&
{
return std::move(m_value.value());
}
const T& operator*() const&
{
return m_value.value();
}
T operator*() const&&
{
return std::move(m_value.value());
}
T* operator->()
{
return &m_value.value();
}
const T* operator->() const
{
return &m_value.value();
}
template <class U>
T valueOr(U&& defaultValue) const&
{
return m_value.valueOr(std::forward<U>(defaultValue));
}
template <class U>
T valueOr(U&& defaultValue) &&
{
return m_value.valueOr(std::forward<U>(defaultValue));
}
std::error_code error() const
{
return m_value.error();
}
const char* error_cstr() const noexcept
{
return detail::ErrorCategory::errorString(m_value.error().value());
}
operator std::error_code() const
{
return m_value.error();
}
std::string message() const
{
return m_value.error().message();
}
#if LUABRIDGE_HAS_EXCEPTIONS
void throw_on_error() const
{
if (! m_value.hasValue())
throw std::system_error(m_value.error());
}
#endif
private:
Expected<T, std::error_code> m_value;
};
template <>
struct TypeResult<void>
{
TypeResult() noexcept = default;
TypeResult(std::error_code ec) noexcept
: m_ec(ec)
{
}
TypeResult(const TypeResult&) noexcept = default;
TypeResult(TypeResult&&) noexcept = default;
TypeResult& operator=(const TypeResult&) noexcept = default;
TypeResult& operator=(TypeResult&&) noexcept = default;
explicit operator bool() const noexcept
{
return ! m_ec;
}
void value() const noexcept
{
}
std::error_code error() const noexcept
{
return m_ec;
}
const char* error_cstr() const noexcept
{
return detail::ErrorCategory::errorString(m_ec.value());
}
operator std::error_code() const noexcept
{
return m_ec;
}
std::string message() const
{
return m_ec.message();
}
#if LUABRIDGE_HAS_EXCEPTIONS
void throw_on_error() const
{
if (m_ec)
throw std::system_error(m_ec);
}
#endif
private:
std::error_code m_ec;
};
template <class U>
inline bool operator==(const TypeResult<U>& lhs, const U& rhs) noexcept
{
return lhs ? *lhs == rhs : false;
}
template <class U>
inline bool operator==(const U& lhs, const TypeResult<U>& rhs) noexcept
{
return rhs == lhs;
}
template <class U>
inline bool operator!=(const TypeResult<U>& lhs, const U& rhs) noexcept
{
return !(lhs == rhs);
}
template <class U>
inline bool operator!=(const U& lhs, const TypeResult<U>& rhs) noexcept
{
return !(rhs == lhs);
}
} // namespace luabridge
+47
View File
@@ -0,0 +1,47 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2021, kunitoki
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include "Stack.h"
namespace luabridge::detail {
//=================================================================================================
/**
* @brief Scope guard.
*/
template <class F>
class ScopeGuard
{
public:
template <class V>
explicit ScopeGuard(V&& v)
: m_func(std::forward<V>(v))
, m_shouldRun(true)
{
}
~ScopeGuard()
{
if (m_shouldRun)
m_func();
}
void reset() noexcept
{
m_shouldRun = false;
}
private:
F m_func;
bool m_shouldRun;
};
template <class F>
ScopeGuard(F&&) -> ScopeGuard<F>;
} // namespace luabridge::detail
File diff suppressed because it is too large Load Diff
+159
View File
@@ -0,0 +1,159 @@
// https://github.com/kunitoki/LuaBridge3
// Copyright 2020, kunitoki
// Copyright 2019, Dmitry Tarakanov
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// SPDX-License-Identifier: MIT
#pragma once
#include "Config.h"
#include <memory>
#include <tuple>
namespace luabridge {
namespace detail {
template <template <class...> class C, class... Ts>
std::true_type is_base_of_template_impl(const C<Ts...>*);
template <template <class...> class C>
std::false_type is_base_of_template_impl(...);
template <class T, template <class...> class C>
using is_base_of_template = decltype(is_base_of_template_impl<C>(std::declval<T*>()));
template <class T, template <class...> class C>
static inline constexpr bool is_base_of_template_v = is_base_of_template<T, C>::value;
template <class... Args>
constexpr bool dependent_false = false;
template <class T>
struct is_tuple : std::false_type
{
};
template <class... Ts>
struct is_tuple<std::tuple<Ts...>> : std::true_type
{
};
template <class T>
constexpr bool is_tuple_v = is_tuple<T>::value;
} // namespace detail
//=================================================================================================
/**
* @brief Container traits.
*
* Unspecialized ContainerTraits has the isNotContainer typedef for SFINAE. All user defined containers must supply an appropriate
* specialization for ContinerTraits (without the alias isNotContainer). The containers that come with LuaBridge also come with the
* appropriate ContainerTraits specialization.
*
* @note See the corresponding declaration for details.
*
* A specialization of ContainerTraits for some generic type ContainerType looks like this:
*
* @code
*
* template <class T>
* struct ContainerTraits<ContainerType<T>>
* {
* using Type = T;
*
* static ContainerType<T> construct(T* c)
* {
* return c; // Implementation-dependent on ContainerType
* }
*
* static T* get(const ContainerType<T>& c)
* {
* return c.get(); // Implementation-dependent on ContainerType
* }
* };
*
* @endcode
*/
template <class T>
struct ContainerTraits
{
using IsNotContainer = bool;
using Type = T;
};
/**
* @brief Register shared_ptr support as container.
*
* @tparam T Class that is hold by the shared_ptr, must inherit from std::enable_shared_from_this to support Stack::get to reconstruct it from lua.
*/
template <class T>
struct ContainerTraits<std::shared_ptr<T>>
{
using Type = T;
template <class U = T>
static std::shared_ptr<U> construct(U* t)
{
if constexpr (detail::is_base_of_template_v<U, std::enable_shared_from_this>)
{
return std::static_pointer_cast<U>(t->shared_from_this());
}
else
{
static_assert(detail::dependent_false<U>,
"Failed reconstructing the reference count of the object instance, class must inherit from std::enable_shared_from_this");
}
}
static T* get(const std::shared_ptr<T>& c)
{
return c.get();
}
};
/**
* @brief Register unique_ptr support as container.
*
* @note Lua gets a non-owning view of the object. The C++ owner must outlive any Lua reference.
*
* @tparam T Class that is held by the unique_ptr.
*/
template <class T>
struct ContainerTraits<std::unique_ptr<T>>
{
using Type = T;
static T* get(const std::unique_ptr<T>& c)
{
return c.get();
}
};
namespace detail {
//=================================================================================================
/**
* @brief Determine if type T is a container.
*
* To be considered a container, there must be a specialization of ContainerTraits with the required fields.
*/
template <class T>
class IsContainer
{
private:
typedef char yes[1]; // sizeof (yes) == 1
typedef char no[2]; // sizeof (no) == 2
template <class C>
static constexpr no& test(typename C::IsNotContainer*);
template <class>
static constexpr yes& test(...);
public:
static constexpr bool value = sizeof(test<ContainerTraits<T>>(nullptr)) == sizeof(yes);
};
} // namespace detail
} // namespace luabridge
File diff suppressed because it is too large Load Diff
+2
View File
@@ -0,0 +1,2 @@
theme: jekyll-theme-cayman
exclude: ThirdParty
+249
View File
@@ -0,0 +1,249 @@
#!/usr/bin/env python3
import os
import re
import argparse
import datetime
from collections import deque
PARSE_FILE = 0
EXTERNAL_FILE = 1
ALREADY_SCANNED = 2
CPP_HEADER_FILE_EXT = set([".hpp" , ".h" , ".hxx" , ".hh" , ".inl"])
PRAGMA_ONCE_MATCHER = re.compile(r'#pragma once')
INCLUDE_FILE_MATCHER = re.compile(r'#include\s*[<\"]([\w.\\/]*)[>\"]')
LOCAL_INCLUDE_FILE_MATCHER = re.compile(r'#include\s*\"([\w.\\/]*)\"')
GUARDED_INCLUDES = [
{ "header": "version" },
{ "header": "coroutine", "condition": "(__cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L))" },
{ "header": "ranges", "condition": "(__cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L))" },
{ "header": "span", "condition": "(__cplusplus >= 202002L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L))" },
{ "header": "flat_map", "condition": "(__cplusplus >= 202302L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202302L))" },
{ "header": "flat_set", "condition": "(__cplusplus >= 202302L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202302L))" },
{ "header": "expected", "condition": "(__cplusplus >= 202302L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202302L))" },
{ "header": "move_only_function", "condition": "(__cplusplus >= 202302L || (defined(_MSVC_LANG) && _MSVC_LANG >= 202302L))" },
]
def GetGuardedInclude(header):
for guarded in GUARDED_INCLUDES:
if guarded["header"] == header:
return guarded
return None
def IsCppHeaderFile(ext):
return ext in CPP_HEADER_FILE_EXT
def AdjustFileExtension(ext):
if ext[0] != '.':
ext = '.' + ext
def RemoveComments(text):
def BlotOutNonNewlines(strIn):
if strIn.startswith("/*"):
return "\n"
else:
return ""
def Replacer(match):
s = match.group(0)
if s.startswith('/'):
return BlotOutNonNewlines(s)
else:
return s
pattern = re.compile(
r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
re.DOTALL | re.MULTILINE
)
return re.sub(pattern, Replacer, text)
class SourceInfo:
def __init__(self, baseDir , outputDir, outputName):
self.includeDirs = list()
self.headerQueue = deque()
self.systemHeaders = set()
self.scannedFiles = set()
self.baseDir = baseDir
self.outputDir = outputDir
self.outputName = outputName
self.headerFileExt = ".h"
self.AddIncludeDirectory(self.baseDir)
self.AddIncludeDirectory(os.path.join(self.baseDir, "detail"))
def LogMessage(self, message):
print(message)
def AddIncludeDirectory(self, path):
if not os.path.exists(path):
return False
self.LogMessage(f"Include Directory Added: {path}")
self.includeDirs.append(path)
return True
def GetAbsoluteSourcePath(self, pwd, include):
if os.path.isabs(include):
return include
for includeDir in self.includeDirs:
absPath = os.path.normpath(os.path.join(includeDir, include))
if os.path.exists(absPath):
return absPath
return None
def ShouldParseFile(self , path , ext):
if (path in self.scannedFiles): return ALREADY_SCANNED
if not IsCppHeaderFile(ext) or not os.path.exists(path):
return EXTERNAL_FILE
return PARSE_FILE
def ScanSourceFile(self, path , depth):
dirpath, filename = os.path.split(path)
ext = os.path.splitext(filename)[1]
info = self.ShouldParseFile(path, ext)
if info != PARSE_FILE:
return info
self.LogMessage(f"Scan file: {path}")
self.scannedFiles.add(path)
with open (path , "r") as src:
lines = src.readlines()
for line in lines:
includeResult = INCLUDE_FILE_MATCHER.findall(line)
if not includeResult:
continue
localResult = LOCAL_INCLUDE_FILE_MATCHER.findall(line)
if localResult:
includeFile = self.GetAbsoluteSourcePath(dirpath, localResult[0])
if includeFile is None:
continue
call = self.ScanSourceFile(includeFile , depth + 1)
if call == EXTERNAL_FILE:
self.scannedFiles.add(includeFile)
else:
self.systemHeaders.add(includeResult[0])
self.AddFileToQueue(path, ext)
return info
def ParseDirectories(self):
all_files = []
for sourceDirectory in self.includeDirs:
for root, _, files in os.walk(sourceDirectory):
for filename in files:
all_files.append(os.path.join(root, filename))
for path in sorted(all_files):
self.ScanSourceFile(path, 0)
def WriteBeginFileHeader(self, filename, stream):
stream.write(f"\n// Begin File: {filename}\n")
def WriteEndFileHeader(self, filename, stream):
stream.write(f"\n// End File: {filename}\n")
def AddFileToQueue(self, filename, ext):
if IsCppHeaderFile(ext):
self.LogMessage(f"Enqueue header file: {filename}")
self.headerQueue.append(filename)
def AmalgamateQueue(self, queue, stream):
while (len(queue) > 0):
path = queue.popleft()
self.WriteFileToStream(path, stream)
def WriteFileToStream(self, path, stream):
self.LogMessage(f"Write File: {path}")
with open (path, 'r') as source:
self.WriteBeginFileHeader(path, stream)
lastLineWasEmpty = False
text = RemoveComments(source.read())
lines = text.replace("\r", "\n").split("\n")
for line in lines:
result = INCLUDE_FILE_MATCHER.findall(line)
if result:
continue
result = PRAGMA_ONCE_MATCHER.findall(line)
if result:
continue
stripped_line = line.strip()
if stripped_line or not lastLineWasEmpty:
stream.write(f"{line}\n")
lastLineWasEmpty = not stripped_line
self.WriteEndFileHeader(path, stream)
def WriteAlgamationFiles(self):
headerPath = os.path.join(self.outputDir, self.outputName + self.headerFileExt)
self.LogMessage(f"Creating source Amalgamation: {headerPath}")
with open (headerPath , 'w') as headerAmalgamation:
current_year = datetime.date.today().year
headerAmalgamation.write(f"// https://github.com/kunitoki/LuaBridge3\n")
headerAmalgamation.write(f"// Copyright {current_year}, kunitoki\n")
headerAmalgamation.write(f"// SPDX-License-Identifier: MIT\n\n")
headerAmalgamation.write(f"// clang-format off\n\n")
headerAmalgamation.write(f"#pragma once\n\n")
systemHeaders = list(self.systemHeaders)
for header in sorted(systemHeaders):
if GetGuardedInclude(header) is None:
headerAmalgamation.write(f"#include <{header}>\n")
headerAmalgamation.write("\n")
for header in reversed(sorted(systemHeaders)):
guard = GetGuardedInclude(header)
if guard is not None:
headerAmalgamation.write(f"#if defined(__has_include) && __has_include(<{header}>)")
if "condition" in guard and guard["condition"] is not None:
headerAmalgamation.write(f" && {guard['condition']}\n")
else:
headerAmalgamation.write("\n")
headerAmalgamation.write(f"#include <{header}>\n")
headerAmalgamation.write(f"#endif\n\n")
headerAmalgamation.write("\n")
self.AmalgamateQueue(self.headerQueue, headerAmalgamation)
headerAmalgamation.write("// clang-format on\n\n")
return headerPath
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Amalgamate LuaBridge.')
parser.add_argument('--base', action='store', default="Source/LuaBridge/")
parser.add_argument('--output', action='store', default="Distribution/LuaBridge/")
parser.add_argument('--name', action='store', default="LuaBridge")
args = parser.parse_args()
sourceInfo = SourceInfo(args.base, args.output, args.name)
sourceInfo.ParseDirectories()
sourceInfo.WriteAlgamationFiles()
+88
View File
@@ -0,0 +1,88 @@
#!/usr/bin/env python3
import sys
import xml.etree.ElementTree as ET
from collections import defaultdict
def extract_uncovered(root):
"""
Returns:
dict[str, list[int]] -> filename -> uncovered line numbers
"""
result = defaultdict(list)
for cls in root.findall(".//class"):
filename = cls.attrib.get("filename", "unknown")
lines = cls.find("lines")
if lines is None:
continue
for line in lines.findall("line"):
if line.attrib.get("hits") == "0":
result[filename].append(int(line.attrib["number"]))
return result
def compress_ranges(numbers):
"""
Converts sorted list like [1,2,3,5,6,10] -> ["1-3","5-6","10"]
"""
if not numbers:
return []
numbers = sorted(set(numbers))
ranges = []
start = prev = numbers[0]
for n in numbers[1:]:
if n == prev + 1:
prev = n
else:
ranges.append((start, prev))
start = prev = n
ranges.append((start, prev))
# Format
out = []
for s, e in ranges:
if s == e:
out.append(f"{s}")
else:
out.append(f"{s}-{e}")
return out
def write_ultra_compact(data, output_path):
"""
Writes:
file:range,range,...
"""
with open(output_path, "w", encoding="utf-8") as f:
for filename in sorted(data.keys()):
ranges = compress_ranges(data[filename])
if ranges:
f.write(f"{filename}:{','.join(ranges)}\n")
def main():
if len(sys.argv) != 3:
print(f"Usage: {sys.argv[0]} input.xml output.txt")
sys.exit(1)
input_path = sys.argv[1]
output_path = sys.argv[2]
tree = ET.parse(input_path)
root = tree.getroot()
uncovered = extract_uncovered(root)
write_ultra_compact(uncovered, output_path)
if __name__ == "__main__":
main()
+54
View File
@@ -0,0 +1,54 @@
default:
@just -l
generate CXX="17":
cmake -G Xcode -B Build{{CXX}} -DLUABRIDGE_BENCHMARKS=ON -DCMAKE_CXX_STANDARD={{CXX}} .
open CXX="17":
@just generate {{CXX}}
-open Build{{CXX}}/LuaBridge.xcodeproj
build CXX="17":
@just generate {{CXX}}
cmake --build Build{{CXX}} --config Debug -j8
test CXX="17":
@just build {{CXX}}
ctest --test-dir Build{{CXX}} -C Debug -j8
test-all:
@just test 17
@just test 20
@just test 23
build1 CXX="17":
@just generate {{CXX}}
cmake --build Build{{CXX}} --config Debug --target LuaBridgeTests54 -j8
test1 CXX="17":
@just build1 {{CXX}}
./Build{{CXX}}/Tests/Debug/LuaBridgeTests54
sanitize TYPE="address" CXX="17":
cmake -G Xcode -B Build{{CXX}} -DLUABRIDGE_SANITIZE={{TYPE}} .
benchmark CXX="17":
@just generate {{CXX}}
cmake --build Build{{CXX}} --config Release --target LuaBridge3Benchmark -j8
cmake --build Build{{CXX}} --config Release --target LuaBridgeVanillaBenchmark -j8
cmake --build Build{{CXX}} --config Release --target Sol3Benchmark -j8
./Build{{CXX}}/Benchmarks/Release/LuaBridge3Benchmark --benchmark_out_format=json --benchmark_out=Build{{CXX}}/LuaBridge3Benchmark.json
./Build{{CXX}}/Benchmarks/Release/LuaBridgeVanillaBenchmark --benchmark_out_format=json --benchmark_out=Build{{CXX}}/LuaBridgeVanillaBenchmark.json
./Build{{CXX}}/Benchmarks/Release/Sol3Benchmark --benchmark_out_format=json --benchmark_out=Build{{CXX}}/Sol3Benchmark.json
@just plot {{CXX}}
plot CXX="17":
uv run --with-requirements Benchmarks/requirements.txt Benchmarks/plot_benchmarks.py --input Build{{CXX}}/*.json --output Images/benchmarks.png
amalgamate:
uv run amalgamate.py
clean:
rm -rf Build
+19
View File
@@ -0,0 +1,19 @@
sonar.projectKey=LuaBridge3
sonar.organization=kunitoki
# This is the name and version displayed in the SonarCloud UI.
#sonar.projectName=LuaBridge3
#sonar.projectVersion=1.0
# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
sonar.sources=Source/LuaBridge,Tests
sonar.coverage.exclusions=Tests/**/*
sonar.cpd.exclusions=Tests/**/*
# Disable some rules on some files
sonar.issue.ignore.multicriteria=a1
sonar.issue.ignore.multicriteria.a1.ruleKey=*
sonar.issue.ignore.multicriteria.a1.resourceKey=Tests/**/*
# Encoding of the source code. Default is default system encoding
#sonar.sourceEncoding=UTF-8