Garry's Mod Wiki

Revision Difference

C_Lua:_Functions#547586

<cat>Dev</cat> # Resources If you haven't already set up your IDE for creating binary modules, refer to one of these pages: - <page>Creating Binary Modules: Premake</page> (Recommended) - <page>Creating Binary Modules: CMake</page> - <page>Setting Visual Studio Up for Making Binary Modules</page> (Deprecated) If you have never used C++ before, [a tutorial is available](http://www.cplusplus.com) to help get your footing The [Lua C API Documentation](https://www.lua.org/manual/5.1/manual.html#3) is an incredibly useful resource for understanding Lua's C API. For additional useful functions, you can refer to [LuaBase.h](https://github.com/Facepunch/gmod-module-base/blob/development/include/GarrysMod/Lua/LuaBase.h). # Understanding stacks Lua C API is built upon stacks and understanding them is critical. Lua's C API is built upon stacks and understanding them is critical. A stack is a sort of array that can have elements "pushed" to it and "popped" from it. Pushing an element puts it on top of the stack, while popping the stack removes the topmost element. It is also possible to use the `Remove` and `Insert` functions to remove and insert values at certain positions in the stack, circumventing the restrictive nature of stacks, but you should use these sparingly. You will often see negative numbers when fetching stack positions. `1` would refer to the bottom of the stack, or the first element, while `-1` would refer to the top of the stack, or the last/latest element. `-1` is often used to fetch the value that was just pushed to the top of the stack. `-2` would fetch the secondmost top, or 2nd to last, value. **It is important to note that certain functions in Lua's C API interact with the stack in potentially unexpected ways**, and if you find yourself running into errors and losing track of the stack, you should look up the functions you utilized. One potent example is the `Call` function. Calling a function from the C API pops the function and every argument from the stack. The following snippet of code calls `math.floor(5.6)` and stores its return value in a `double MyDouble` while commenting a visualization of the current stack in a familiar array format: ``` double MyDouble; // Get the global table, [Global{}] LUA->PushSpecial(GarrysMod::Lua::SPECIAL_GLOB); // Get the math table, [Global{}, math{}] LUA->GetField(-1, "math"); // Get the floor function, [Global{}, math{}, floor()] LUA->GetField(-1, "floor"); // Push the double 5.6, [Global{}, math{}, floor(), 5.6] LUA->PushNumber(5.6); // Call a function with 1 argument and 1 return value // This pops the function, floor(), and the argument, 5.6, from the stack, then pushes the return value to the top of the stack // The stack is now [Global{}, math{}, 5] LUA->Call(1, 1); // Fetches the number at the top of the stack, DOES NOT POP IT, and stores it in MyDouble, [Global{}, math{}, 5] MyDouble = LUA->GetNumber(-1); // Pops Global{}, math{}, and 5 from the stack, [] LUA->Pop(3); ``` # Creating C++ functions for Lua Making C++ functions for Lua is absolutely essential for your binaries. Without it, your modules just won't be as useful. In this section I will cover how to create functions in C++ for use in Lua, checking the arguments and return values. ## Defining Our Function in C++ The first thing we will do is define our C++ function. ``` LUA_FUNCTION( MyFirstFunction ) { //We'll add stuff here in a sec } ``` Now that we have a blank function, lets have it return a value. To do this we push something onto the stack and then return the number of values we want to return. So in this case we'll push a bool, true, and then return 1. ``` LUA_FUNCTION( MyFirstFunction ) { LUA->PushBool( true ); // Push our bool onto the stack. return 1; // How many values we are returning } ``` Now if we were to call this function in Lua, which we can't yet because we have only defined it in C++, then it would always return true. Alright, so now that you have a basic understanding of how it works, let's make it more complex. How about we add a parameter and if it's over a certain value we return true, otherwise false. ``` LUA_FUNCTION( MyFirstFunction ) { LUA->CheckType( 1, GarrysMod::Lua::Type::Number ); // Make sure a number is the first argument double number = (LUA->GetNumber( 1 )); // Get the first argument if (number > 9.0) // If the number is over 9... { LUA->PushBool( true ); // push true... } else { LUA->PushBool( false ); // otherwise, push false. } return 1; // How many values we are returning } ``` ## Defining Our C++ Function in Lua So now that we have or function created, we need a way to call it in Lua. This is the easy part. ``` GMOD_MODULE_OPEN() { LUA->PushSpecial( GarrysMod::Lua::SPECIAL_GLOB ); // Push the global table LUA->PushCFunction( MyFirstFunction ); // Push our function LUA->SetField( -2, "MyFirstFunction" ); // Set MyFirstFunction in lua to our C++ function LUA->Pop(); // Pop the global table off the stack return 0; } ``` This sets the Lua variable "MyFirstFunction" to the C++ function "MyFirstFunction", so you can call it in Lua using ``` MyFirstFunction( <number> ) ``` # Calling Lua Function In C++ It is very useful if you know how to call Lua functions using Lua C. In this section I will cover how to calling functions and getting the return values. Now a brief explanation of how Call works. If I did the following: ``` LUA->Call( 0, 0 ); ``` Then it would call my function with 0 arguments and not get a return value. The next example would call with 1 argument and still get no return value: ``` LUA->Call( 1, 0 ); ``` My next example would pass 3 arguments and get 1 return value: ``` LUA->Call( 3, 1 ); ``` Finally my last example would pass 2 arguments and get 4 return values: ``` LUA->Call( 2, 4 ); ``` ## Call and PCall The only difference between Call and PCall is PCall is short for protected call. In other words if something goes horribly wrong, PCall won't freak out but call will. So it's a good habit to use PCall if your arguments aren't constants, but for these examples we will be using Call. ## Calling from the Global Table So calling from the global table is probably what you will use the most. It's very simple to call from the global table and easy. It just doesn't look very pretty. ``` LUA->PushSpecial( GarrysMod::Lua::SPECIAL_GLOB ); // Push the global table LUA->GetField( -1, "print" ); // Get the print function LUA->PushString( "Swag" ); // Push our argument LUA->Call( 1, 0 ); // Call the function LUA->Pop(); // Pop the global table off the stack ``` This would output "Swag" in the console. Below is an example of using multiple arguments. ``` LUA->PushSpecial( GarrysMod::Lua::SPECIAL_GLOB ); // Push the global table LUA->GetField( -1, "print" ); // Get the print function LUA->PushString( "Swag" ); // Push our argument LUA->PushNumber( 1337 ); // Push our second argument LUA->Call( 2, 0 ); // Call the function LUA->Pop(); // Pop the global table off the stack ``` This would output "Swag 1337" in console. Below is an example of a pseudo-practical use. ``` LUA_FUNCTION( MyFirstFunction ) { LUA->CheckType( 1, GarrysMod::Lua::Type::String ); // Make sure a string is the first argument LUA->PushSpecial( GarrysMod::Lua::SPECIAL_GLOB ); // Push the global table LUA->GetField( -1, "print" ); // Get the print function LUA->Push( 1 ); // Push the first argument LUA->Call( 1, 0 ); // Call the function LUA->Pop(); // Pop the global table off the stack return 0; // How many values we are returning } ``` If you wanted to call more than one function you would do the following: ``` LUA->PushSpecial( GarrysMod::Lua::SPECIAL_GLOB ); // Push the global table LUA->GetField( -1, "print" ); // Get the print function LUA->PushString( "Swag" ); // Push our argument LUA->Call( 1, 0 ); // Call the function LUA->GetField( -1, "print" ); // Get the print function LUA->PushString( "Garry feels like a new man." ); // Push our argument LUA->Call( 1, 0 ); // Call the function LUA->GetField( -1, "Msg" ); // Get the Msg function LUA->PushString( "Double swag\n" ); // Push our argument LUA->Call( 1, 0 ); // Call the function LUA->Pop(); // Pop the global table off the stack ``` ## Calling Functions Passed as Arguments Calling functions passed as arguments is very useful. In another section I will cover how to store a function and then call it later, but for now we will start with the basics. ``` LUA_FUNCTION( MyFirstFunction ) { LUA->CheckType( 1, GarrysMod::Lua::Type::Function ); // Make sure the first argument is a function LUA->PushString( "Hey... swag swag swag swag" ); // Push our argument LUA->Push( 1 ); // Push the function LUA->Call( 1, 0 ); // Call the function return 0; } ``` So now if you did something like below in Lua, it would output in console "Hey... swag swag swag swag" ``` MyFirstFunction( print ) ``` ## Getting the Return Value So you know how to call the functions, you just don't know how to get the return value. I will fix that for you. Below is an example of getting the return value of math.abs: ``` LUA->PushSpecial( GarrysMod::Lua::SPECIAL_GLOB ); // Push the global table LUA->GetField( -1, "math" ); // Get the math table LUA->GetField( -1, "abs" ); // Get the abs function from the math table LUA->PushNumber( -666 ); // Push our argument LUA->Call( 1, 1 ); // Call the function and get 1 return value int iReturnValue = (int)LUA->GetNumber( -1 ); LUA->Pop( 3 ); // Pop the global table, the math table, and the return value off the stack ``` Now we have the return value of math.abs( -666 ) in iReturnValue. So iReturnValue is equal to 666. In the following example I get the result of math.abs( 1337 ) then I print it to my console. ``` LUA->PushSpecial( GarrysMod::Lua::SPECIAL_GLOB ); // Push the global table LUA->GetField( -1, "math" ); // Get the math table LUA->GetField( -1, "abs" ); // Get the abs function from the math table LUA->PushNumber( -666 ); // Push our argument LUA->Call( 1, 1 ); // Call the function and get 1 return value LUA->GetField( -3, "print" ); // Get the print function LUA->Push( -2 ); // Push the return value LUA->Call( 1, 0 ); // Call the print function LUA->Pop( 3 ); // Pop the global table, math table, and return value off the stack ``` This would output "666" to my console. # Creating C++ functions for readability & convenience Internally, `LUA_FUNCTION()` is a macro that exposes the `LUA` variable and prepares the encased function for use with Lua. We can pass this `LUA` function to our own functions to create functions that exist solely in the C environment. A function can be defined as follows: ``` // Prints a message using print() inline void LuaPrint(GarrysMod::Lua::ILuaBase* LUA, char* msg) { LUA->PushSpecial(GarrysMod::Lua::SPECIAL_GLOB); // Fetch the global table LUA->GetField(-1, "print"); // Fetch the print function LUA->PushString(msg); // Push the string argument LUA->Call(1, 0); // Call the function with 1 argument and no return value LUA->Pop(); // Pop global table } ``` ...and then called from inside a `LUA_FUNCTION` or even `GMOD_MODULE_OPEN`: ``` GMOD_MODULE_OPEN() { // Prepare our module, load Lua functions, etc... LuaPrint(LUA, "Module successfully loaded!"); } ``` For efficiency, it is important to understand when to use [inline](https://www.geeksforgeeks.org/inline-functions-cpp/) in functions. # A note on userdata & metatables Userdata and metatables are handled differently in Garry's Mod. This helps the engine determine userdata type much faster. First create your metatable (ideally in GMOD_MODULE_OPEN), then create a reference to it and store it globally in a variable. ``` LUA->CreateTable(); LUA->PushCFunction(gcDeleteWrapper); LUA->SetField(-2, "__gc"); LUA->PushCFunction(toStringWrapper); LUA->SetField(-2, "__tostring"); LUA->PushCFunction(indexWrapper); LUA->SetField(-2, "__index"); LUA->PushCFunction(newIndexWrapper); LUA->SetField(-2, "__newindex"); metatable = LUA->ReferenceCreate(); ``` To push your userdata to the stack: ``` GarrysMod::Lua::UserData* ud = ( GarrysMod::Lua::UserData* )LUA->NewUserdata( sizeof( GarrysMod::Lua::UserData ) ); ud->data = pointer_to_your_c_class; ud->type = your_type_id; LUA->ReferencePush( metatable ); LUA->SetMetaTable(-2); ``` To get your userdata from the stack: ``` GarrysMod::Lua::UserData* obj = (GarrysMod::Lua::UserData* )LUA->GetUserdata(position); your_c_class* var = (your_c_class*)(obj->data); ```