Open In App

Integrating Lua in C++

Improve
Improve
Like Article
Like
Save
Share
Report

Lua is a high-level, multi-paradigm programming language, mainly used in embedded applications as well as powerful scripting support for existing products. For Example Scripting enhancement of an NGINX, HA Proxy, Wireshark, etc. Another major area where Lua had found application is the game engine frameworks.

Why Use Lua?

There is the various reason for choosing Lua to provide the scripting support:

  • Its low footprint and superior speed as compared to other scripting frameworks.
  • In version 2.0, Lua had a major restructuring which had lead toward significant performance gains.
  • In addition, Lua is bringing a JIT compiler, which is very fast and with a very small footprint.
  • AS compared to other popular scripting platforms (Python), Lua uses a much smaller memory footprint.
  • Lua has a simple, but very efficient, syntax.
  • It has out-of-box support for passing anonymous functions as arguments, map/array as one concept called “table”, meta functions, and meta tables, allowing implementation of various programming paradigms, etc.

Despite all the above advantages, Lua provides a very low-level C API which requires the developer to learn the internals of the Lua engine before he can use it in the applications. However, this has changed with the Lua Cpp library.

LuaCpp: It is a lightweight wrapper for Lua C APIs that provides access to the Lua library of two levels i.e., Accesses through high-level APIs that are hiding the complexity of the C APIs and the engine, and access to the Lua low-level APIs.

How to install Lua?

The LuaCpp can be installed as a system-wide library, or as a sub-module of your exiting project. Run the below command to install the LuaCpp in ubuntu.

  • Clone the LuaCpp library from the below link:

=> git clone https://github.com/jordanvrtanoski/luacpp.git

  • Change the directory to luacpp, create a new directory as build, and change the directory to build again using the below commands:

=> cd luacpp
=> mkdir build
=> cd build

  • Now, make the source using the below commands:

=> cmake ../Source
=> make -j `nproc`

  • Now, install the library using the below command:

=> make install

Once the library is installed, build the file as follows:

=> gcc hello.cpp -I /usr/local/include/LuaCpp -I /usr/include/lua5.3/ -lluacpp -llua5.3 -lstdc++ -o hello

Output the file as:

=> hello

Below is the same program to illustrate the same:

C++




// C++ program to illustrate the use of
// LuaCpp library
#include <LuaCpp.hpp>
#include <iostream>
using namespace LuaCpp::Registry;
using namespace std;
 
// Driver Code
int main(int argc, char** argv)
{
 
    cout << "Hi from C++, this is a demo"
         << " how LuaCpp can be used\n";
 
    LuaContext lua;
 
    // The simplest way is to use
    // CompileStringAndRun method
    try {
 
        lua.CompileStringAndRun(
            "print('The fastest way to "
            "start using lua in "
            "a project')");
    }
 
    catch (std::runtime_error& e) {
        std::cout << e.what()
                  << '\n';
    }
}


Output:

Passing data from C++ to Lua and back:

The example is showing us how to compile and execute Lua code snippet from C++. However, without the ability to pass data from C++ to Lua, and back from Lua to C++, there are not many real-life cases that can be addressed by this pattern.

LuaCpp arrives prepared to establish the bridge between the two execution environments wilt as little as possible knowledge of the internal working of Lua, as well as with minimal code. Let’s improve the “Hello World” example by adding a variable that will be shared by both execution environments. This introduces a “String” variable called “world” and populates it with a value from the C++ context. Inside the Lua context, update the value of the variable, and upon return to the C++ context and print the value of the variable.

Below is the program to illustrate the same:

C++




// C++ program to illustrate the
// above approach
#include <LuaCpp.hpp>
#include <iostream>
using namespace LuaCpp;
using namespace LuaCpp::Registry;
using namespace LuaCpp::Engine;
using namespace std;
 
// Driver Code
int main(int argc, char** argv)
{
    LuaContext ctx;
 
    shared_ptr<Engine::LuaTString> str = make_shared<Engine::LuaTString>(
        "world from C++!");
 
  ctx.AddGlobalVariable("world", str));
  ctx.CompileString("test",
                    "print('Hello '..world)"
                    "world = 'world from lua!'");
 
  // Try Catch Block
  try {
      ctx.Run("test");
  }
  catch (runtime_error& e) {
      cout << e.what() << '\n';
  }
 
  cout << "Hello "
       << str->getValue() << "\n";
}


Output:

The context allows passing multiple variables from the C++ scope to Lua scope and vice versa. The above pattern allows for adding the scripting support to the C++ project for the majority of the cases. The simple 4-step process is:

  • Create the context.
  • Create shared variables and register them with the context.
  • Compile the Lua script (from string, file, or multiple files in a folder).
  • Run the script.

Supported Lua Types:

The LuaCpp provides the following types of variables that can be passed between the C++ and Lua context:

  • “LuaTString”: The equivalent of “std::string” in C++.
  • “LuaTNumber”: the equivalent of “double” in C++. Lua allows the LUA_TNUMBER (the internal Lua type of number) to be compiled as float, however, LuaCpp will contain to be presented in the C++ context as a double, which means that, in cases where Lua library is customized to define a number as a float, there could be loss of data due to precision.
  • “LuaTBoolean”: The equivalent of “bool” in C++.
  • “LuaTNil”: a null type that is used by the Lua engine to signify a lack of value.
  • “LuaTTable”: a hybrid of array/map, which in C++ is implemented as a “std::map”. The map can have a string or number as a key, and there is a special case when all the keys in the map are of type number, the map represents an array. This follows the logic of the Lua Table implementation.
  • “LuaTUserData”: a special type that allows implementation of user-defined types. This is a very powerful type, and the engine’s LuaMetaObject type is implemented based on this primitive type. This concept deserves its own separate article.

Conclusion:

Adding scripting support to the existing C++ project brings huge flexibility and configurability to the developed application. Although Lua C APIs are not very complex, they still demand from the developer to fully understand the inner workings of the Lua virtual machine. As covered in this article, LuaCpp is abstracting all of this complexity and provides an interface that is very familiar to the C++ developer. 



Last Updated : 29 Dec, 2022
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads