• Announcement: Lua.org now officially recommends this forum as a meeting place for the Lua community
  • The forum is currently open to new registrations. The registration will close for a short period of time when reaching 500 active members, to upgrade the server resources.

Lua, Sol 3 and C++ (Oh, My!) : Part 1 - Nice Doggy. (1 Viewer)

Lua, Sol 3 and C++ (Oh, My!) : Part 1 - Nice Doggy.


header-image.png

I've seen a couple of questions now about embedding Lua in C++. While it is certainly possible to use the C API in C++, I wanted to introduce a pure C++ interface to Lua called Sol 3. Sol3 is a template based approach to Lua. It is extremely fast and has some very slick features for interfacing with Lua that almost completely eliminates the need to access the Lua stack.

Now, lets be very clear: I suck at C++. I think it's an abysmal language and it's practitioners are masochists. Every time I try and interface C++ with Lua I am struck by the complete dissimilarities. One is a large, complex monster ready to eat your children and destroy months of your life; the other a wonderful tool for usefully automating the world. If you read this "article" (rant?) let the curse that is C++ be on your head, I wash my hands. Now for the foolhardy, let us have a look.

Toolchains
Sol 3 works with G++ (GNU C++ compiler) and Microsoft Visual C++ (e.g. Visual Studio 'proper'), but I will be using my WinLua Toolchain (LLVM/CLang) on Windows 10 with VS Code (not Visual Studio 'proper'). WinLua Toolchain is installed via MSI and consists of Lua 5.3, Lua Rocks (package manager), a compiler and the most sublime build system: xmake. While I am eager to announce WinLua, it's still not quite ready. Feel free to download and follow along, but the debugger doesn't work in VS Code (GDB command line only!) and LuaRocks is a little finky. The MSI uninstaller completely removes it from your system, so using it is relatively risk free. None the less, there should be a full WinLua release before the end of the year.

While build systems are plentiful, I will rely on xmake for this article. If you wish to use xmake without Winlua (e.g. with VC++ or Mingw-64), download instructions are here.

Sol 3
Sol 3 is a "Header Only" Lua interface (5.1+) to C++ written against the C++ 17 standard. Header Only, means you don't have to build a library, or add source trees. C++17 is important because it takes advantage of all the latest tools and compiler tricks to make your Lua calls lightening fast. To use Sol 3 you just include the header in your C++ application and you're done. That simple! There are some parts that are pure magic and completely obfuscate the boundaries between C++ and Lua. However, Sol3 is, a tricky beast due to it being C++17 and primarily written in C++ templates and there are decisions and design tradeoffs that will need to be made later in your projects. Most of that difficulty, however, is outside the scope of this article.

The documentation for Sol 3 is quite extensive and the tutorials/examples are plentiful. Getting started is relatively easy. However, even after spending time with the tutorials, I often found myself asking "ya, but I just want to...", "sure, but what if I need to..." and so forth. That was true until I started to understand the brilliant use of generics to make accessing Lua very simple. Unfortunately the API reference is dense C++ templates written for those who are already initiated. Remember, this is C++ and you are supposed to know what you're doing. None the less, I hope to spell out some of those rough spots.

Finally, a note about where to find help. While I can provide some information, I am only a few steps ahead of you in my use of Sol 3 (remember, I don't like C++). The Sol 3 maintainer/author is amazing and should be your go-to source for issues. The Phantom Derpstorm - aka ThePHD - has always answered my github questions very quickly and provided the necessary - albeit densely technical - solutions. And no, ThePHD is not a doctor (yet? I embarrassed myself on the lua-l mailing list once).

So, without further ado, lets get started.

Pre-Requisites
- Lua 5.3+
- C++ Compiler and build tools
- Sol header file
- IDE, preferably a debugger
- (optional) Command line shell (powershell, terminal, WSL, gitbash, msys2, etc.)

If you already have Lua and a compiler toolchain set up, then download the Sol 3 git repository and copy the "single" hpp file to your include directory:
(Note the repository is still called sol2. Officially Sol 3 is called Sol 2 v3. <head explodes>)

Code:
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

Try the new cross-platform PowerShell https://aka.ms/pscore6

C:\Users\russh> cd c:\temp
C:\temp> git clone https://www.github.com/thephd/sol2
Cloning into 'sol2'...
warning: redirecting to https://github.com/thephd/sol2.git/
remote: Enumerating objects: 19769, done.
remote: Total 19769 (delta 0), reused 0 (delta 0), pack-reused 19769
Receiving objects: 100% (19769/19769), 24.16 MiB | 3.98 MiB/s, done.
Resolving deltas: 100% (14178/14178), done.
C:\temp> cp .\sol2\single\include\sol\sol.hpp c:\users\spiffy\yerproj1\include

If you've decided you want to follow along with WinLua, then download WinLua Toolchain, and run the installer. Sol 3 comes packaged with Winlua so it is already installed.

WinLua Setup in VS Code

Next, download and open Visual Studio Code and install two add ins: xmake and C++.

First, C++...
vs-code-c++-plugin.png
Next, the xmake plugin

vs-code-xmake-plugin.png

If you clear your search box, you will see the two installed plugins:

vs-code-setup.png

You can now set up a folder for your project. Once you've created your containing folder (in my case, thats LuaSolExample), I need to create some subfolders and an empty file for Sol. We need to create include\sol\config.hpp by right clicking, creating the include folder, then double click into the include folder, then creating the sol folder. Finally, in the sole folder, right click and create a "new text file" and rename the file config.hpp. *NOTE: If you have file extensions hidden in Windows Explorer, you will need to show extensions (menu bar -> view -> check File name extentions) and remove the ".txt" at the end.* Create a folder, then right click in the folder and select "open in VSCode".

include-sol-new-folders.png

With this part complete, there are only one or two more "WinLua specific steps for VS Code", but for now, lets move on to some generic code.
open-vs-code.png
Hello World

In you editor of choice, create a file called main.cpp and enter the following contents:

C++:
#include <iostream>
#include <sol.hpp>

int main(int, char*[])
{
    sol::state lua;
    // open some common libraries
    lua.open_libraries(sol::lib::base, sol::lib::package);
    lua.script("print('bark bark bark!')");

    std::cout << std::endl;

    return 0;
}

This code is almost directly from the Sol 3 tutorials. As you can see, getting started is amazingly simple. With three lines of code, we can get Lua to say something for us.

As with any C or C++ program, there are two things that need to be specified in order to complete our application:

1) We need to tell a compiler where to find the header files included in our project and
2) We need to tell the linker where to find the binaries that contain the functionality that we want to include in our application.

In our case, we need the compiler to know where the sol.hpp file and the config.hpp files are located, as well as the header files for Lua. While you can see that we have not included any Lua headers in our application, the Sol 3 library links to Lua for us, so we need to specify them too.

As for the binary libraries that actually contain the functionality, we are lucky in that only a Lua library is required because Sol 3 is a header only implementation. Note that if you are using WinLua or Microsoft VC++ (`cl`), you will need to have a *.lib file for Lua. That file can be a static lib or a linking lib, but you need that file. A lua53.dll file will not work by itself. If you are using GNU g++, you are exempt (`ld` will link directly to a DLL. yikes!).

Now, if you are using Visual Studio VC++, you will need to drill into endless dialog boxes and specify the correct settings. I will not specify the VS configuration here, but if someone asks in the comments, I will perhaps relent.

Building Our Example
While one could use Visual Studio (msbuild from the command line), WinLua provides three possible build systems: the veritable GNU `make` ("gmake"), the sublime lua based `xmake`, and `luarocks`. GNU `make` is excellent if you have a long grey unix beard or you are particularly keen on slamming your head against hard objects. Luarocks is excellent if you are creating Lua modules only. Since neither of these scenarios applies directly to this article, we will skip to xmake (And in case my bias wasn't obvious enough - because it's my favorite and I am the one writing this article, so there).

Open powershell and `cd` into your directory. I use Windows Terminal from the Windows store:

Code:
C:\Users\russh> cd .\Examples\LuaSolExample\
C:\Users\russh\Examples\LuaSolExample> ls


    Directory: C:\Users\russh\Examples\LuaSolExample


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       11/28/2020  12:56 AM             88 main.cpp

Xmake is excellent at detecting existing toolchains. If VC++ is installed, it will find that first.

Code:
C:\Users\russh\Examples\LuaSolExample> xmake config
note: xmake.lua not found, try generating it (pass -y or --confirm=y/n/d to skip confirm)?
please input: n (y/n)
y
xmake.lua not found, scanning files ..
target(main): binary
    [+]: main.cpp
xmake.lua generated, scan ok!
checking for architecture ... x64
checking for Microsoft Visual Studio (x64) version ... 2019
C:\Users\russh\Examples\LuaSolExample>

If GNU mingw-64 is available in the path, it will find that (if VC++ isn't present). For our example, I am going to set a global mingw directory and specify the project configuration parameters:

Code:
C:\Users\russh\Examples\LuaSolExample> xmake global --mingw="C:\Program Files (x86)\WinLua\WLC"
configure
{
    mingw = C:\Program Files (x86)\WinLua\WLC
    proxy_pac = pac.lua
    theme = default
    network = public
}
C:\Users\russh\Examples\LuaSolExample> xmake f -c -v -a i686 -p mingw -m debug
note: xmake.lua not found, try generating it (pass -y or --confirm=y/n/d to skip confirm)?
please input: n (y/n)
y
checking for mingw directory ... C:\Program Files (x86)\WinLua\WLC
configure
{
    proxy_pac = pac.lua
    kind = static
    theme = default
    arch = i686
    mingw = C:\Program Files (x86)\WinLua\WLC
    buildir = build
    cross = i686-w64-mingw32-
    mode = debug
    plat = mingw
    bin = C:\Program Files (x86)\WinLua\WLC\bin
    clean = true
    host = windows
    ccache = true
    ndk_stdcxx = true
    network = public
}
C:\Users\russh\Examples\LuaSolExample>

The following table explains the switches:

Switch
Value
Description
f​
N/A​
Specified "config" to configure the project​
-c​
N/A​
Clear the existing configuration​
-v​
N/A​
verbose​
-a​
i686​
Architecuture - i386 or i686 can be specified for 32 Bit. x86_64 is the 64 bit designation.
WinLua is only 32 bit at the moment so we will use i686. WinLua 64 bit is not far off.​
-p​
mingw​
Platform: Mingw is the C/C++ library we will be using.​
-m​
debug​
Compilation "mode". Also known as a build configuraiton.​

xmake will also generate a new xmake.lua file for you. If we try it however, it will fail because we haven't specified our headers or link libraries:

Code:
C:\Users\russh\Examples\LuaSolExample> xmake
checking for mingw directory ... C:\Users\russh\llvm-mingw
[ 50%]: compiling.debug main.cpp
error: main.cpp:2:10: fatal error: 'sol.hpp' file not found
#include <sol.hpp>
         ^~~~~~~~~
1 error generated.

Let's pop open VS Code again and update our xmake.lua file

Code:
C:\Users\russh\Examples\LuaSolExample> code.cmd .\xmake.lua

xmake-doesnt-work-yet.png

Our xmake file specifies a "target", being the thing we are going to build. Within that target it sets the kind of build and adds our C++ file. However, what it does not do yet is define where our header files are, or where our link libraries are. Lets add that now:

Lua:
-- define target
target("main")
    -- set kind
    set_kind("binary")
    -- add files
    add_files("main.cpp")
    add_includedirs("include", "C:\\Program Files (x86)\\WinLua\\Lua\\5.3\\include")
    add_linkdirs("C:\\Program Files (x86)\\WinLua\\Lua\\5.3\\bin")
    add_links("lua53")

We have added three lines that tell the compiler and linker where to find the pieces they need to complete the program. I have used the include directory for WinLua because Sol and all the Lua files are in there. I have also added our own include directory so that the system can find sol/config.hpp. If you put Sol 3 in your own include directory, your includes would like something like this:

Lua:
    add_includedirs("C:\\Users\\spiffy\\MyFirstSolProj\\include", "C:\\Users\\spiffy\\MyLuaInstall\\include")

Now lets try again from the command line:
Code:
C:\Users\russh\Examples\LuaSolExample> xmake clean
C:\Users\russh\Examples\LuaSolExample> xmake build
[ 50%]: compiling.debug main.cpp
[COLOR=rgb(184, 49, 47)]error[/COLOR]: C:\\Program Files (x86)\\WinLua\\Lua\\5.3\\include\sol.hpp:1257:10: error: redefinition of 'fx_traits<R (Args...) noexcept, false>'
                struct fx_traits<R(Args...) noexcept, false> : public basic_traits<true, false, void, R, Args...> {
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\\Program Files (x86)\\WinLua\\Lua\\5.3\\include\sol.hpp:1141:10: note: previous definition is here
                struct fx_traits<R(Args...), false> : public basic_traits<false, false, void, R, Args...> {
                       ^
C:\\Program Files (x86)\\WinLua\\Lua\\5.3\\include\sol.hpp:1262:10: error: redefinition of 'fx_traits<R (*)(Args...) noexcept, false>'
                struct fx_traits<R (*)(Args...) noexcept, false> : public basic_traits<true, false, void, R, Args...> {
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\\Program Files (x86)\\WinLua\\Lua\\5.3\\include\sol.hpp:1146:10: note: previous definition is here
                struct fx_traits<R (*)(Args...), false> : public basic_traits<false, false, void, R, Args...> {
                       ^
C:\\Program Files (x86)\\WinLua\\Lua\\5.3\\include\sol.hpp:1267:10: error: redefinition of 'fx_traits<R (Args..., ...) noexcept, false>'
                struct fx_traits<R(Args..., ...) noexcept, false> : public basic_traits<true, true, void, R, Args...> {
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\\Program Files (x86)\\WinLua\\Lua\\5.3\\include\sol.hpp:1151:10: note: previous definition is here
                struct fx_traits<R(Args..., ...), false> : public basic_traits<false, true, void, R, Args...> {
C:\Users\russh\Examples\LuaSolExample>

Ooops. That didn't go well. What happened? I left this error in here because it's an important one to know about. This error is telling you in a round about way that you are using the wrong C++ standard. Remember, Sol 3 uses C++17. Most compilers default to C++11 or C++14. Let's fix that using a compiler option in our xmake.lua file:

Lua:
add_cxflags("-std=c++17")

Save the file, drop back to the command line, and lets try again. (Note that `xmake`, `xmake build` and `xmake b` all run the build command):

Code:
C:\Users\russh\Examples\LuaSolExample> xmake b
checking for mingw directory ... C:\Users\russh\llvm-mingw
[ 50%]: compiling.debug main.cpp
[ 75%]: linking.debug main.exe
[100%]: build ok!

OH MY GOD! It worked, it worked, it worked it...(dance of joy). Ahem. I mean: As you can see, we have now compiled our first Lua/C++ application. Let's try running it:

xmake-success.png
Conclusion
In this article I have very, very, very briefly outlined how to link Lua to C++ using the Sol 3 Lua bindings. If you want to charge ahead, there are plenty of samples in the Sol 3 documentation and in the repository. Next week(ish), I will post the second part of the article outlining a simple use of Sol 3. If you are familiar with Lua's stack based access, the simplicity of Sol 3 will blow your mind. If you've not had the 'pleasure' of accessing Lua from C, then Sol 3 is going to spoil you. Either way, I think you'll appreciate the masterpiece that will shed light on Lua/C++ (Sol means sun in portugese. 'shed light', sun... get it?)

Dinsdale
 
Last edited:

LedLoaf

Newcomer
Joined
Mar 14, 2021
Messages
2
Reaction score
0
Hello,

Been trying for hours to find some help and came across your tutorials. I'm trying to read an exported Tiled Editor Map to lua and I can't seem to grab the first dang variable! It returns a table and I was hoping you could get me started by telling me how to parse out the first variable.

Code:
return {
  version = "1.4",
  luaversion = "5.1",
  tiledversion = "1.4.1",
  orientation = "orthogonal",
  renderorder = "right-down",
  width = 32,
  height = 20,
  tilewidth = 16,
  tileheight = 16,
  nextlayerid = 8,
  nextobjectid = 1,
  properties = {},
  tilesets = {
    { .... }
    ....
    }

If it was just version = "1.4" this would be easy, but because it returns it as a table I can't for the life of me figure out how to start grabbing them correctly. Any help would be appreciated. Everything i do seems to get me "[sol3] An error occurred and panic has been invoked: stack index -1, expected string, received nil" as the error
 

LedLoaf

Newcomer
Joined
Mar 14, 2021
Messages
2
Reaction score
0
I don't see an edit option on here. I talked to the developer and figured it out. Thank you.
 

mishaelmiles

Newcomer
Joined
Mar 18, 2021
Messages
1
Reaction score
0
Age
28
Location
USA
Then you will have to implement those requirements into the right system. Then, what do you have?
 
Top