Provide a C++ substitute for Lua's C-API that is more expressive and easier to use.
- Works as documented
- Purpose more or less fulfilled
- User will occasionally use Lua API directly to fill gaps in the usecases covered by LuaAide.
- Work continues to cover more usecases.
- Embedding Lua in a program, e.g. for configuration or as a plugin
- Extending Scripts by loading binary Lua-modules written with LuaAide
- Exposing C++ Types and functions to Lua scripts
Use Lua in your application for a variety of purposes: To make it scriptable, to implement a configuration format, to offer a plugin-interface, to use the Lua-format for data input/output and so forth.
#include <LuaAide.h>
auto Q=LuaStack::New(true, nullptr); // Initialise Lua.
Q<<LuaCode("local a,b=...; return a+b")<<21<<22>>1; // Execute a piece of Lua code, passing arguments and ..
printf("C++ code receives 21+22=%d\n", Q.toint(-1)); // .. receiving result on the stack.
const std::vector<std::string> A {"Hoppla", "a list", "of strings"}; // Use Lua's table.concat to ..
Q<<LuaCode(R"__(return table.concat(..., "\n"))__")<<A>>1; // concatenate C++ strings.
const std::string A1(Q.tostring(-1)); // == "Hoppla\na list\nof strings"
Use C++ to create modules that can be loaded into Lua scripts, benefitting from performance, static typing, access to C/C++ libraries etc.
Example demomodule.cpp: compile/link to demomodule.so or demomodule.dll
namespace {
// These functions are implemented elsewhere in this module.
// They are exposed to the Lua runtime in luaopen_demomodule.
extern "C" int pwd(lua_Stack*);
extern "C" int cd(lua_Stack*);
}
extern "C" int luaopen_demomodule(lua_State*L)
{
LuaStack Q(L);
Q<<LuaTable()
<<"0.1">>LuaField("version")
<<pwd>>LuaField("pwd")
<<cd>>LuaField("cd");
return 1;
}
Use demomodule from a script:
local demo=require "demomodule"
print("demomodule version", demo.version)
..
Take advantage of C++ strong typing by implementing specific types in C++ and exposing them to Lua as modules.
Several examples are included in the examples folder: module_vec3.cpp, module_timestamp.lua, module_colorenum.cpp
- C++ 20
- Lua 5.4
git clone --recurse-submodules --remote-submodules https://github.com/vorgestern/luaaide.git
- Adapt Makefile if Lua is not at default location.
- Run
make - Run
make test(optional) This executes ./LuaAideTest (tests of the static library) and test scripts for the example modules. - Install LuaAide manually by copying libLuaAide.a and include/LuaAide.h where they belong.
- Edit buildsys/VS17/Lua.props to point to your Lua-Installation:
- AdditionalIncludeDirectories: Include the directory that contains lua.hpp.
- AdditionalLibraryDirectories: Include the directory where the import library is located.
- AdditionalDependencies: Include the import library for Lua 5.4.
- LuaAide.lib (Release|Win32) will be built in the root directory, others under buildsys/VS17. Select a different Configuration|Platform in buildsys/VS17/LuaAide.props.
- Build with Visual Studio 2022 (VS17) or later by launching buildsys/VS17/LuaAide.sln
In these examples, it is assumed that Q ist an instance of LuaStack, e.g. from LuaStack Q(L)
or auto Q=LuaStack::New(true, nullptr);.
using namespace std;
Q<<lualist<<21<<22<<23; // Pushes {21, 22, 23} on to the stack.
Q<<LuaTable()<<1.5>>LuaField("x") // Pushes {x=1.5, y=0.7, z=-2.1} on to the stack.
<<0.7>>LuaField("y")
<<-2.1>>LuaField("z");
Q<<vector<string> {"A", "B", "C"}; // Pushes {"A", "B", "C"} on the stack.
Q<<unordered_map<string,string> { // Pushes {x="21", y="22", z="23"} on to the stack.
{"x", "21"}, {"y", "21"}, {"z", "23"}
};
Q <<lualist<<21<<22<<23<<lualistend
>>LuaGlobal("L1"); // L1={21, 22, 23} is a global variable.
Q<<LuaCode("return 21")>>1; // Execute a script to push one result.
auto result=Q.toint(-1); // Read value on top of the stack as integer.
// Result will be 21.
using namespace std;
Q<<"This was not expected">>luaerror; // Equiv. of 'error "This was not expected"'
vector<string> A={"a", "b", "c"};
Q<<formatany<<A>>1; // formatany is part of LuaAide.
auto str=Q.tostring(-1); // It converts (nearly) any value to a string.
int join(lua_State*L) // demofunction: table.concat with separator in upvalue
{
LuaStack Q(L);
Q <<LuaGlobal("table")<<LuaElementCall("concat") // local arg=...
<<LuaValue(1)<<LuaUpValue(1)>>1; // return table.concat(arg, up1)
return 1;
}
Q<<", "<<LuaClosure({join, 1})>>LuaGlobal("KommaJoin"); // Create Closure that joins with comma.
Q<<"-" <<LuaClosure({join, 1})>>LuaGlobal("HyphJoin"); // Create Closure that joins with hyphens.
Q<<LuaCode(R"__( // Execute demo script
local A={"a", "b", "c"}
print(KommaJoin(A)) // prints "a, b, c"
print(HyphJoin(A)) // prints "a-b-c"
)__")>>0;
Q<<LuaGlobal("string")<<LuaElementCall("format") // Pushes "vector=[21, 22, 23]" on to the stack.
<<"vector=[%s, %s, %s]"<<21<<22<<23>>1;
Q<<lualist<<"First"<<"Second"; // Pushes {"First", "Second"} on to the stack.
Q<<LuaGlobal("table")<<LuaElementCall("concat")
<<LuaValue(-2)<<"+">>1; // Calls table.concat on the list, i.e. pushes
// "First+Second" on to the stack.
- Install a PanicHandler to translate Lua-exceptions to C++ runtime exceptions.
- Pass a source name together with Lua source code LuaCode to get better error messages.
#include <LuaAide.h> using namespace std; int main_throwing(lua_State*L) { LuaStack Q=L; // Executing this Script will fail because a parenthesis is not closed. Q<<LuaCode("FunctioningLuaCompiletimeFailureDemo", R"___( function map(A, M local R={} for _,e in ipairs(A) do table.insert(R, M[e] or e) end return R end )___")>>0; } int panichandler(lua_State*L) { LuaStack Q(L); throw runtime_error(Q.errormessage()); return 0; } int main() { LuaStack Q=LuaStack::New(true, panichandler); try { return main_throwing(Q); } catch (const runtime_error&E) { printf("Runtime error:\n%s\n", E.what()); cout<<Q<<"\n"; return 0; } }
-
Throw conventional Lua-errors where applicable. Have them translated to a C++ runtime exception as shown above.
-
Handle unexpected results from script execution by checking the return value:
// This script will cause a runtime-error, because table.concat cannot handle // elements of type boolean. const auto rc=Q<<make_pair("Demo", LuaCode(R"___( local a={...} return table.concat(a, ", ") )___"))<<21<<22<<true<<false>>1; if (rc!=LUA_OK) { const auto message=Q.tostring(-1); // Use as suits your application, e.g. throw a C++ runtime-error. }
Throw a conventional Lua-Error, let Lua handle it:
```
int demofunction(lua_State*L)
{
// Called at runtime from Lua, unhappy with arguments:
LuaStack Q(L);
if (height(Q)<1) return Q<<"demofunction: Argument (string) expected">>luaerror;
if (Q.typeat(-1)!=LuaType::TSTRING) return Q<<"demofunction: string expected">>luaerror;
.....
}
```
LuaStack Q=...;
cout<<Q;