Interactive use and sharing of c/c + + and lua

Posted by timmy0320 on Sun, 12 Dec 2021 16:16:47 +0100

preface:

In the process of embedded development, we will use some scripting tools to assist our work, such as shel or python, Lua, etc. today, I will share with you the interactive use of lua scripts I use in my work.

Author: conscience still exists

REAUTHORIZING authorization and onlookers: welcome to WeChat official account: Yu Linjun

Or add the author's personal wechat: become_me

Plot introduction:

During work, because our sensor needs factory calibration, we need to have a configuration file to save our sensor parameters. This file supports reading and modification. There are many ways to realize this function. Generally, we use an ordinary file for reading and writing.

However, considering the complexity of our data and the description of file comments, I chose xml files to save the data, but I don't want to write the xml file operation library myself or add it externally. It is originally a small function. There is no need to add additional links and use other xml operation libraries, So I focused on the lua script used in our laser slam mapping algorithm. The package of this lua script itself and it was added in the kernel and used in other processes. I only need to add - llua dynamic chain in my compilation option, so that multiple processes can be used together in the past.

In addition to convenience, we also consider that lua is a lightweight script that supports interactive calls. For example, we can call lua script functions through internal execution in the code, or execute the functions registered in the code in lua. This has many advantages over shell and python. Shell can only be executed at the terminal where its script is generated, and Python is similar. It can't make interactive function calls between both sides. lua can be called interactively, so it is very convenient.

lua introduction

Lua was developed in 1993 by a research team at the Catholic University of Rio de Janeiro, Brazil. Is a lightweight and compact script language, written in standard C language and open in the form of source code. Its design purpose is to embed in the application program, so as to provide flexible expansion and customization functions for the application program.
Its design purpose is to embed in the application, so as to provide flexible expansion and customization functions for the application.

Lua characteristics

Lightweight: it is written in standard C language and open in the form of source code. After compilation, it is only more than 100 K, which can be easily embedded in other programs.
Extensibility: Lua provides very easy-to-use extension interfaces and mechanisms: these functions are provided by the host language (usually C or C + +), and Lua can use them as if they were built-in functions.

Support process oriented programming and functional programming;
Automatic memory management; Only a general type of table is provided, which can be used to implement arrays, hash tables, sets and objects;
Language built-in pattern matching; Closure; Function can also be regarded as a value; Provide multithreading (collaborative process, not the thread supported by the operating system);
Closure and table can easily support some key mechanisms required by object-oriented programming, such as data abstraction, virtual function, inheritance and overloading.

Lua application scenario

Game development, independent application scripts, Web application scripts, extensions and database plug-ins, such as MySQL Proxy and MySQL WorkBench, and security systems, such as intrusion detection system.

Fundamentals of lua interaction principle

lua and c + + interact through a virtual stack.
c + + calling lua is actually: c + + first puts the data into the stack, lua takes the data from the stack, then returns the value corresponding to the data to the top of the stack, and then returns c + + from the top of the stack.
lua calls c + +: first write your own c module, then register the function in the lua interpreter, and then lua calls the function of this module.

Because there is lua library on our device, I developed it directly in cmakelists Txt file added - llua, but at the beginning of their own pc verification, the machine did not have the corresponding lua package, or downloaded the source code of the official website lua, compiled it, and put it in the specified directory of my computer for operation verification.

  1. lua source code download

Go to the official website http://www.lua.org/download.html Download

make

make install

The compiled files are placed in the following three directories

  • /usr/local/bin interpreter directory
  • /usr/local/include header file directory
  • /usr/local/lib Dynamic Link Library Directory

Later, when we test the native code, we can add the absolute directory to search the header file and link the dynamic library.

The following is the contents of the Makefile file of my demo test, in which the header file directory and dynamic link library directory are used.

OBJS = test_cpp_lua.o 
CFLAGS = -Wall -g -std=c++11
CC = gcc
CPP = g++
INCLUDES +=-I /usr/local/include 
LIBS +=  -L /usr/local/lib   -llua -ldl
#LIBS =   -ldl -llua

target:${OBJS}
#	g++  -o target test_cpp_lua.o  -llua -ldl 
	@echo "-- start " ${CC} ${CFLAGS} ${OBJS}  -o $@  ${INCLUDES}  ${LIBS}
	$(CPP) ${CFLAGS} ${OBJS}  -o $@  ${INCLUDES}  ${LIBS}

clean:
	-rm -f *.o core *.core target

.cpp.o:
#%.o:%.cpp
	${CPP} ${CFLAGS} ${INCLUDES} -c  $<

Note: I also used - ldl when compiling because dlopen, dlsym, dlclose and dlerror are used in the program to display and load dynamic libraries. The link option - ldl needs to be set

Load the dynamic link library, first allocate physical memory for the shared library, and then establish the mapping between virtual pages and physical pages in the page table entry corresponding to the process.

Lua is an embedded scripting language, that is, Lua is not a program that can run alone. In practical applications, there are two main application forms. The first form is that C/C + + as the main program calls Lua code. At this time, Lua can be regarded as an "extensible language". We call this application "application code". The second form is that Lua has control, and C/C + + code is used as Lua's "library code". In both forms, the communication between the two languages is completed through the C API provided by Lua.

Next, I'll introduce the usage of the two calls and the demo of reading and writing operations that I actually use xml files. This article does not describe the use and operation of lua scripting language, but only does some application sharing in the actual call process.

C/C + + code calls lua variables and functions

First of all, the most commonly used is to call the script. The most common call mechanism is to execute and call the functions in the script in the code or obtain some setting information in the script file.

This is the code part of lua script:

debug_enbale = "enable"

angle_table = {
roll_offset = 0.05 ,
pitch_offset = 0.0,
yaw_offset = 0.0,
}

for i,v in ipairs(angle_table) do
        print(i,v)
 end

This defines a string variable debug_enbale, and a lua's table angle_table, and finally a for loop flow control code for table traversal. In this way, the variable information in the corresponding table can be printed when the lua script is executed

In lua, the lua stack is a struct. The stack index can be positive or negative. The difference is that positive index 1 always represents the bottom of the stack and negative index - 1 always represents the top of the stack. So we will see push in the process of use_ X and to_ Functions such as X operate on the stack.

This part is for routine use. I get number data and string data respectively and put them into the running variables of my execution code.

#include "lua.hpp"
#include <iostream>
int main(int argc,char ** argv)
{
    lua_State *pLua = luaL_newstate();
    if(!pLua)
    {
        LOG(Info,  "Failed to open Lua!");
        return false;
    }
    luaL_openlibs(pLua);
    
    int bRet = luaL_loadfile(pLua, lua_path.c_str());
    if (bRet)
    {
        LOG(Info, "load .lua file failed" );
        return false;
    }
   // Execute lua file
    bRet = lua_pcall(pLua, 0, 0, 0);
    if (bRet)
    {
        LOG(Info,  "call .lua file failed" );
        return false;
    }
    
    lua_getglobal(pLua, "debug_enbale"); 
    std::string str = lua_tostring(pLua, -1);//Get lua script debug_enbale location data
    LOG(Info,  "debug_enbale=" << str);
    

    auto get_float_data_from_lua = [&](const char * table_name,const char * value_name) -> float{
           lua_getglobal(pLua, table_name);
           lua_getfield(pLua, -1, value_name);
           return lua_tonumber(pLua, -1);
    };
    roll_offset = get_float_data_from_lua("angle_table","roll_offset");
    pitch_offset = get_float_data_from_lua("angle_table","pitch_offset");
    yaw_offset = get_float_data_from_lua("angle_table","yaw_offset");

    LOG(Info,  "angle_table:" 
        << roll_offset <<" "
        << pitch_offset <<" "
        << yaw_offset );  
    lua_close(pLua);
        
}

Important function description

1. Because the project is cpp, add Lua HPP, if it is a C project, can directly include Lua h.

2.lua_State *pLua = luaL_ newstate(); The Lua library does not define any global variables, but saves all States in the dynamic structure Lua_ In state, all subsequent C API s require this pointer as the first parameter.
3.luaL_ The openlibs function is used to open all standard libraries in Lua, such as io library, string library, etc.

4.luaL_loadfile actually called lua_load function to load the lua file.

5.lua_ The pcall function pops the block from the stack and runs it in protected mode. 0 will be returned if the execution is successful, otherwise the error information will be pushed into the stack.
6.lua_ When getglobal calls this macro, it will push the corresponding global variable value in Lua code into the stack

7.lua_ - 1 in the toString function indicates the index value at the top of the stack, the index value at the bottom of the stack is 1, and so on. This function returns the string information at the top of the stack

7.lua_getfield sets the specified index - 1 in the stack as the top angle of the stack_ Value in table_ The specific value of name is push ed to the stack.

8.lua_tonumber returns the data at the top of the stack in numerical form

9.lua_close is used to release the resource referenced by the status pointer.

Where, the data differentiated function Lua is used_ ToNumber and lua_tostring two functions, lua_tonumber returns both integer and floating point types.

In this way, I get the data I wrote in the lua script to cooperate with my code execution.

lua variable calls C/C + + code functions

Citation article< Step By Step(Lua calls C function)>
Lua's ability to call C functions will greatly improve Lua's scalability and availability. For some functions related to the operating system or modules requiring high efficiency, we can completely implement them through C functions, and then call the specified C functions through Lua. For those C functions that can be called by Lua, their interfaces must follow the form required by Lua, that is, typ edef int (lua_CFunction)(lua_State L).
Briefly, this function type only contains a pointer representing Lua environment as its unique parameter. Implementers can further obtain the actual parameters passed in Lua code through this pointer. The return value is an integer, indicating the number of return values that the C function will return to Lua code. If there is no return value, return 0 can be used. It should be noted that the C function cannot be used directly Return the real return value to Lua code, but pass the call parameters and return value between Lua code and C function through virtual stack. Here we will introduce two rules for Lua to call C function.

Lua calls C functions in two ways

1. The program body runs in C, and the C function is registered in Lua. C calls Lua, and Lua calls the function registered by C, and C obtains the execution result of the function.

2. The main body of the program runs in Lua, and the C function is used by Lua as a library function.
The first method looks wordy and strange. Since the program body runs in C and the function defined in C is used in the end, why register the function with Lua and call the function through Lua?

Therefore, compared with the first method, the second method is more common.

As for this part of the code, I only ran a few examples on my computer. You can also go online to find relevant examples. I didn't use this part on the device myself. Later, I can write this part of the application to share with you.

lua performs operations on xml files

This part is because the previous functions have been modified. The reason is that the file for placing the sensor parameters we need can be modified. If the parameters are written in the lua script, the parameters are dead. We can't use the lua script to modify the data in the lua script later, so later I used the xml file to place my sensor parameters and used the lua pin This is for reading and writing.

xml is a format commonly used to write some of our uncertain data configuration files. lua also has luaxml toolkit. However, in order not to add additional library implementation, I use the I/O Library (the library used by lua to read and process files) in lua to read and write xml files.

You can also use xml, a lua tool, or other tools, which will be more convenient. Here is an official link to:
http://lua-users.org/wiki/LuaXml

There are four different xml reading and writing tools:

  • tool kit;
  • Lua's XML parser only;
  • XML parser containing C code and binding;
  • Modules for handling XML based protocols such as XML-RPC and SOAP.

Read and write xml functions in lua script

function get_value_from_xml(path,element_name)
	xml_file=path        
	element=element_name    

	head="<"..element..">"     
	tail="</"..element..">"    

	file = io.open(xml_file, "r");  --open xml file
	data = file:read("*all");     --Read the entire contents of the file to data Variable
	file:close();                 --close xml file

	--Get start tag And close tag Content between to value in
	_,_,value=string.find(data, head.."(.-)"..tail)

	--output value To standard output
	-- print(value)
	return value

end
function set_value_to_xml(path,element_name,set_value)
	xml_file=path   
	element=element_name    
	new_value=set_value  

	head="<"..element..">"    --Generate start from element name tag,Namely<element_name>
	tail="</"..element..">"   --Generate close based on element name tag,Namely</element_name>

	file = io.open(xml_file, "r"); --open xml file
	data = file:read("*all");      --Read the entire contents of the file to data Variable
	file:close();                  --close xml file


	--take element Previous content, element The value of, element The subsequent contents are saved in pre,old_value,follow in
	_,_,pre,old_value,follow=string.find(data, "(.*)("..head..".-"..tail..")(.*)")


	file = io.open(xml_file, "w");      --open xml file
	file:write(pre..head..new_value..tail..follow); --Assemble new file contents and write
	file:close();                       --close xml file
end

The above mainly uses string.find, which has three parameters and three return values. The function of string.find is to find specific contents from strings.

The first parameter is the target string (all contents, such as data), the second parameter is the string you want to find (such as the contents between item s), and the third parameter starts from the first few characters (I make it a changing value).

The first return value is the first character position (a number) of the found string (content between items), the second return value is the last character position (a number) of the found string (content between items), and the third is the found string (content between items).

So the code gets the content by the third return value of each loop.

cpp Code:

#include "lua.hpp"
#include <iostream>
int main(int argc,char ** argv)
{
   lua_State *pLua = luaL_newstate();
    if(!pLua)
    {
        LOG(Info,  "Failed to open Lua!");
        return false;
    }
    luaL_openlibs(pLua);
    
    int bRet = luaL_loadfile(pLua, lua_path.c_str());
    if (bRet)
    {
        LOG(Info, "load .lua file failed" );
        return false;
    }
   // Execute lua file
    bRet = lua_pcall(pLua, 0, 0, 0);
    if (bRet)
    {
        LOG(Info,  "call .lua file failed" );
        return false;
    }

    auto lua_func_call_wirte = [&](const char * func_name, const char * key_name,float &value){
        lua_getglobal(pLua, func_name);
        lua_pushstring(pLua ,surface_xml_used_path.c_str());  
        lua_pushstring(pLua ,key_name);  
        lua_pushnumber(pLua ,value);  
        bRet = lua_pcall(pLua, 3, 1, 0);//Three parameters, one return value
        if (bRet)
        {
            const char* pErrorMsg = lua_tostring(pLua, -1);
            LOG(Info, "lua_pcall - ErrorMsg:"  << pErrorMsg );
            // lua_close(pLua);
            return false;
        }
        
        if (lua_isnumber(pLua, -1))
        {
            value = lua_tonumber(pLua, -1);
            LOG(Info,  "surface_config " << value);
            return true;
        }     
        return false;
    };  
      float roll_offset = 0.5,pitch_offset = 0.2,yaw_offset=0.6;

    lua_func_call_wirte("set_value_to_xml"," roll_offset", roll_offset);
    lua_func_call_wirte("set_value_to_xml"," pitch_offset", pitch_offset);
    lua_func_call_wirte("set_value_to_xml"," yaw_offset", yaw_offset);
    
    
    
  auto lua_func_call_number = [&](const char * func_name, const char * key_name,float &value){
        lua_getglobal(pLua, func_name);
        lua_pushstring(pLua ,surface_xml_used_path.c_str());  
        lua_pushstring(pLua ,key_name);  
        bRet = lua_pcall(pLua, 2, 1, 0);//Two parameters, one return value
        if (bRet)
        {
            const char* pErrorMsg = lua_tostring(pLua, -1);
            LOG(Info, "lua_pcall - ErrorMsg:"  << pErrorMsg );
            // lua_close(pLua);
            return false;
        }
        
        if (lua_isnumber(pLua, -1))
        {
            value = lua_tonumber(pLua, -1);
            LOG(Info,  "config " << value);
            return true;
        }     
        return false;
    };  

    auto lua_func_call_string = [&](const char * func_name, const char * key_name,std::string &value){
        lua_getglobal(pLua, func_name);
        lua_pushstring(pLua ,surface_xml_used_path.c_str());  
        lua_pushstring(pLua ,key_name);  
        bRet = lua_pcall(pLua, 2, 1, 0);
        if (bRet)
        {
            const char* pErrorMsg = lua_tostring(pLua, -1);
            ZY_LOG("robotctl",  kInfo, "lua_pcall - ErrorMsg:"  << pErrorMsg );
            lua_close(pLua);
            return false;
        }
        
        if (lua_isstring(pLua, -1))
        {
            value = lua_tostring(pLua, -1);
            LOG(Info,  "config " << value.c_str() );
            return true;
        }     
        return false;
  
    };
    std::string  temp_from_lua;
    lua_func_call_string("get_value_from_xml","debug_enable",temp_from_lua);
    LOG(Info,  "debug_enbale =" << temp_from_lua);


    lua_func_call_number("get_value_from_xml","down_roll_offset",roll_offset);
    lua_func_call_number("get_value_from_xml","down_pitch_offset",pitch_offset);
    lua_func_call_number("get_value_from_xml","down_yaw_offset",yaw_offset);

    
    LOG(Info,  "ngle_table:" 
        << roll_offset <<" "
        << pitch_offset <<" "
        << yaw_offset );   
    lua_close(pLua);

}

When you see this, you may ask me why not directly create a file under linux to read and write configuration data. My idea is that pure files are not easy to annotate, because my configuration parameters are long and I want to make them understood by others as much as possible, so I wrote some comments in it. xml meets my requirements, In addition, lua scripts can also assist my own code execution during the use of some scripts, so I consider integrating some functions and operations that are almost executed as a whole and executing them with a unified interface. Therefore, lua+xml technology is selected finally. There are many implementation ideas, but we need to measure which parts of the technology can make the platform more reusable.

epilogue

This is how I share my use of lua script in my work. If you have better ideas and needs, you are also welcome to add my friends to communicate and share.

Author: conscience still exists, hard work during the day, original public number owner at night. In addition to technology, there are some life insights in official account. A serious driver who drives the contents of the workplace is also a rich person living outside technology. And basketball is a photo and music. Follow me and walk with me.

                              ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧  END  ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧

Recommended reading

[1]On the court, I bowed my head to the RMB player

[2]Linux development coredump file analysis and actual combat sharing

[3]How does the program in the CPU run Required reading

[4]cartographer environment establishment and mapping test

[5]Comparison of simple factory mode, factory mode and abstract factory mode of design mode

All the original dry cargo of the official account has been organized into a catalogue, and the reply is made available.

Topics: C++ Linux lua