C++ MFC calls Lua

These days, I've studied lua. The main focus is on the integration between Lua and vc. Write the code in the host program and then call the host program's code (or interface, component, whatever you like) in lua. I want to use scripts to control the host program's behavior. This is actually a separate, script-controlled architecture, which some might call business engines, workflows, and so on.
Why choose lua?
Because it is a scripting language that combines C/C++, and our program is written in VC++; Another reason is because of its popularity, even WOW uses lua to provide API s for players to modify their game behavior, so I can't find any reason to reject it.
What is Lua? Where do I get the LUA?
Not to mention in detail, there are a lot of them on the Internet, just its official website: www.lua.org , here you can find the application of lua, the version released by lua, I am using 5.1.4, download the version of the source code.
Integration of LUA and VC MFC?
1. Include LUA: To use LUA, of course, first include it in our project, either in lib/dll mode or in static lib mode. Of course, you can also put the entire Lua code into our project and compile it, because Lua only has a few hundred K s, which is very small. Include the whole code. Let me say:
a) Create a new project in VC MFC (e.g. Dialog base project)
b) Go to the tab page of the file in the project, create a new folder, and then include all the.C,.h files in the lua. Note that there are several not included, lua.c, wmain.c, Lua C.C. After including them, select all.C files under this folder, right-click setting s, select Not using precompiler file s,
In VS2008, right-click the.CPP file,
->Properties---->C\C+++-->Precompiled Headers--->Create/Use Precompiled Headers--->Select: Do not use precompiled headers

Compile it as soon as you have finished this step. It should be OK!
c) There are also dynamic lib raries and static LIBS to include lua in your project, so you can try them yourself.
2. Include lua's header somewhere (I'm at the beginning of the MFC Lua1Dlg.cpp file) and declare a lua_state pointer, as follows:

extern "C"  
{  
#include "lua.h"  
#include "lualib.h"  
#include "lauxlib.h"  
}  
lua_State *lua;  
   
//Call the following section of code somewhere appropriate (I'm in OnInitDialog) to open some necessary libraries:  
       lua = lua_open ();    
       if(lua)  
       {  
              luaopen_base (lua);  
              luaopen_table (lua);  
              luaopen_string (lua);  
              luaopen_math (lua);  
              luaopen_debug (lua);  
              //luaopen_io (lua);  
       }  
//When you run out of lua, call the following sentence to close the Lua library:  
lua_close (lua);  

Okay, so far, lua has completely become part of our program. Try compiling it to see if it passes.
Interaction between LUA and MFC?
After Lua has become part of our program, we will use it. Remember that our goal is to use scripts to control the execution of our host program. We will do two things. First, we will call Lua's functions with the MFC program, and second, we will call mfc's functions with the Lua program. The following may be a little difficult for beginners to understand. Please be alerted to the fact that I'll try to be as simple as possible.
1. mfc calls lua's function. Here we use a StackDump's function. It is about the interaction stack between the main program and lua. The following is a special description of the interaction stack.
First, we build a test.lua from Notepad, which is an additive function:

function add ( x, y )   
return x + y;  
end  

Then call it in VC, the following code, when you look at this code, first ignore the StackDump function, just know that it is a function that outputs the contents of the lua and VC interaction stack. Yes, you can create a new button click function, and then put this code in:

StackDump(lua);      
luaL_dofile(lua, "test.lua");     // Interpret and analyze lua files  
StackDump(lua);  
lua_getglobal(lua, "add");       // Get a global label, add, and stack the add function  
StackDump(lua);      
lua_pushnumber(lua, 1);        // Push the first parameter on the stack  
StackDump(lua);  
lua_pushnumber(lua, 2);        // Second parameter stack  
StackDump(lua);  
//lua_call(lua, 2, 1);   
if(lua_pcall(lua, 2, 1, 0) != 0)        // Execute add function  
{  
        AfxMessageBox("lua_pcall error!");               
return;  
}  
StackDump(lua);  
int d = (int)lua_tonumber(lua, -1);        // When the function is finished, the execution result is stacked, so the top number is the result value, -1 is the value at the top of the stack  
CString str;  
str.Format("%d", d);  
AfxMessageBox(str);  
StackDump(lua);  
lua_pop(lua, 1);      // Clear the value from the stack, pop a value  
StackDump(lua);  

Looking at this code, we probably know that one of the procedures for calling the Lua function is: dofile-function name stack-parameter stack-lua_pcall execution (stack of execution results) Take out the execution results (if there are more than one, take more than one out of the stack.) so that we can easily call functions in Lua to know what's happening in the stack.
2. Lua calls MFC functions, for example, if we want to call a Msg function in lua, we can pop up a window to display the string we want to display, and then return a value of "MsgOK!" string.
The lua file looks like this. The first sentence is to call the Msg function, and the second sentence is to test whether the returned string is "MsgOK!":
c = Msg ("123");
Msg©;
The MFC program looks like this:
First write a function that will be exported, many articles call it a glue function:
static int Msg(lua_State* L)
{
const char *s1 = luaL_checkstring(L, 1); // Test if the first parameter is a string and get it
StackDump(L);
MessageBox(NULL, s1, "caption", MB_OK);
lua_pop(lua, 1); // Clear this string from the stack
StackDump(L);
lua_pushlstring(L,'MsgOK!', 6); // Push the return value on the stack
//This return is the number of returned values
return 1;
}
Then export the function as follows:
lua_pushcfunction(lua, Msg);
lua_setglobal(lua, "Msg");
Then just execute the Lua file, remember to lua_before executing Open () oh:
luaL_dofile(lua, "test.lua");
The result of running is to jump out of two messagebox es in a row, the first one is 123, the second one is "MsgOK!", indicating that the string we returned was received by lua, and the second line of lua does not receive its return value, which is automatically discarded.
If you need to return more values, let's put the following sentence:

lua_pushlstring(L, "MsgOK!", 6);  // Push the return value on the stack  
// This return is the number of returned values  
return 1;  

Change to:

lua_pushlstring(L, "MsgOK!", 6);  // Push the return value on the stack  
lua_pushlstring(L, "haha!", 5);      // Push the return value on the stack  
// This return is the number of returned values  
return 2;  

So we can get two return values in the lua file as follows:
c,d = Msg("123");
Then c and d are "MsgOK!" and "haha!" strings, respectively. This automatic mechanism is convenient to use.
3. Interaction stack
Both of the above calls are actually useful to the Lua stack, so we should better understand the concept of the interaction stack between Lua and vc (what is the stack? Please refer to the Books on Data Structure.) This is the stack through which Lua and vc interact. The access function of this stack is lua_gettop, lua_settop, lua_tostring, lua_ For functions like toXXX, we need to know what happens in the stack when a function call occurs. I used a StackDump function above, and when we call it, we can clearly see what's happening in the stack.
First of all, we need to know that the number from top to bottom of the stack is -1, -2, and from bottom to top is 1, 2.
If lua_is used Gettop (L, 1) is the first element at the bottom of the stack. lua_gettop(L, -1) is the first element to get to the top of the stack. lua_pop() (L, 1) is to eject an element from the top of the stack, lua_ Po() (L, 2) ejects the two elements at the top of the stack.
Okay, write it all, and finally implement the StackDump function:

int StackDump(lua_State* L)  
{  
       int nTop = lua_gettop(L); //Gets the number of elements in the stack. Position at the top of the stack.  
       OutputDebugString("The Length of stack is %d\n", nTop); //Output stack top position  
       for (int i = 1; i <= nTop; ++i)  
       {  
              int t = lua_type(L, i);  
              OutputDebugString("%s:", lua_typename(L, t)); //Here, typename is the name of the type by converting the enumeration of the type to a string. Not in the stack.  
              switch(t)  
              {  
              case LUA_TNUMBER:  
                     OutputDebugString("%f", lua_tonumber(L, i));  
                     break;  
              case LUA_TSTRING:  
                     OutputDebugString("%s", lua_tostring(L, i));  
                     break;  
              case LUA_TTABLE:  
                     //OutputDebugString("%s\n", lua_tostring(L,i));  
                     break;  
              case LUA_TFUNCTION:  
                     //OutputDebugString("%s\n", lua_tostring(L,i));  
                     break;  
              case LUA_TNIL:  
                     OutputDebugString("Is NULL");  
                     break;  
              case LUA_TBOOLEAN:  
                     OutputDebugString("%s", lua_toboolean(L, i) ? "true" : "false");  
                     break;  
              default:  
                     break;  
              }  
              OutputDebugString("\n");  
       }  
       return 0;  
}  

This article mainly talks about the integration of lua and VC, the compilation of LUA source code with VC project, the code that VC calls LUA, the code that LUA calls VC, the return value and the element information in multiple return values, interaction stack, output interaction stack, etc. The next one will talk about how to avoid blocking scripts, the use of lua and multithreading, etc.

Keywords: C++ lua MFC

Added by mikeschuld on Tue, 09 Nov 2021 19:40:45 +0200