C++ & Lua - Function Arguments

Skill

C++ & Lua - Function Arguments

Posted in:

As we have been realizing here at SOTC, using Lua with C++ is really the only way to make Lua work for you. Lua itself is really not that useful, but used in conjunction with other languages, it can be quite powerful. It past tutorials, we have gone over how to use Lua with C++. However, we have yet to go over how to pass arguments back and forth between the two. This is what we will cover today.

So in our last Lua tutorial, we discovered some new ways to get Lua and C++ to play nice together. Besides learning a new technique to load the Lua interpreter, we also went over how to run functions between the two. Running functions can be complex in itself, so much so that we left out how to pass arguments back and forth. Today we hope to remedy that.

Before we start, you should open or acquire our VS project from the last tutorial. I will be adding to its content, so you will be slightly lost if you don't have that project. Go ahead and get it, then open that sucka up.

As of right now, we have a function in Lua, that calls a function from C++. The Lua function is then called from C++. Essentially we have a big loop that passed some information between the two. Of course, your functions can do just about anything you want, but one of the basic needs of a function is arguments. To start with, we are going to do a simple argument pass from C++ to Lua.

Before we start, it is important at this point to mention a vital Lua concept. If you remember from the last tutorial, we used a function called lua_pushstring(). This is an odd sounding function to be sure, but it shows us something fascinating about the interactions of Lua and C++. The Lua interpreter actually uses a queue to process different actions. When you are "pushing" a variable with a function, you are actually adding it to what is called "the stack".

Now this stack is critical for really any Lua interaction in C++. Whenever we pass information back and forth, we are stacking this data, and then we have to retrieve it one by one. This makes arguments a bit tricky, but we can start with something simple.

Ok, so now we can finally get into some code. The first thing we are going to cover is passing an argument to a Lua function. This is the simplest way to pass arguments. All we are going to do is "push" something onto the stack before we call the function. But before we go throw data around, we need to modify our Lua. If I recall correctly, our current Lua function looks like this:

function lFunction()
  print (cppFunction())
end

As we can clearly see, this function doesn't even take arguments right now. of course this is an easy fix:

function lFunction(luaArg)
  print(luaArg)
  print (cppFunction())
end

All we are adding is the printing of an argument. Lua will actually convert really just about anything into a string, so it really doesn't matter what we pass to the stack. It could be a number, table, or a string. To keep things simple, we will pass a string. To do this, all we have to do is call lua_pushstring() right before we call the function:

lua_pushstring(L, "Hello from C++!");

lua_call(L, 1, 0);

The good observer will also note a slight difference in our lua_call() call. Instead of two zeros for the second two arguments, we have a one. What we are doing here is telling Lua to expect one argument for the call, and it takes it from the top of the stack. So, in effect, you put something on the stack, then Lua snatches it for the function call. Not that difficult to understand.

So if we run this code as it is, we will get two of the "Hello from C++" messages, like so:

The Output

Now things get a little tricky. To pass something from Lua to C++, we now have to start grabbing things from the stack, instead of just pushing things on it. This not only requires new functions, but now we start using pointers to that data we need, and we all know how fun pointers can be.

So, let's start with the easiest part first, and modify our Lua function once again:

function lFunction(luaArg)
  print(luaArg)
  print (cppFunction("Hello from Lua!"))
end

Just one small change, we pass a string to our C++ function. Implementing this can be tricky, especially when strings are on the mind. In truth we are going to point to some character data. But to review, let's go over what our C++ function looks like right now:

static int cppFunction(lua_State *L)
{
  lua_pushstring(L, "Hello from C++!");

  return 1;
}

Yeah, that's it. We are pushing a string our to Lua, in effect returning a string from the function. Then we return the number of objects we are returning from the C++ function itself. Really not much to it.

The funniest part about this whole process, is that the C++ function's arguments are not going to change. What we do is just pull things from the lua stack. Doing this requires a set of functions, which I have dubbed the "to-blah" functions. Each function will return a value from a specified index on the stack, converted to the type the function refers to. For this case, we are using the lua_tostring() function. Strangely enough, this is the one function we really need:

static int cppFunction(lua_State *L)
{
  const char *cppArg = lua_tostring(L, 1);
  printf( cppArg );
  printf( "\n" );

  lua_pushstring(L, "Hello from C++!");

  return 1;
}

Ok, so what we are doing here is basically we are retrieving a pointer to the data at stack index 1. Since the stack is indexed beginning with one, we are pointing to the last data adding to the stack, hopefully the argument passed to us from lua. In this un-complex example, everything works out just fine. Once we have the data, all we do is print it. The heart of a simple example.

So what exactly do we get? This:

More Output!

That is some pretty funky output huh? We have "Hello from Lua!" sandwiched in two "Hello from C++!"s. But as silly as this may look, it is exactly what we are looking for. The first thing we do in this whole tangled process is pass a "Hello from C++!" to Lua. Then we call our Lua function.

Inside this Lua function, the first thing we do is print the argument passed, resulting in our first "Hello from C++!". Then we call the C++ function, and print its results. However, before we return a value from the C++ function, we print the argument passed to it, resulting in the "Hello from Lua!". After that, it is as simple as printing the value returned from the C++ function, which is done in Lua. This whole crazy process gives us our output.....whew.

So this is basically how you handle argument passing between Lua and C++. Obviously it will get a little crazy if you start using a bunch of functions and a bunch of arguments, but in the end it adds so much to their interactions. It is time for me to move on to the next tutorial, but just remember: when you need programming help, all you have to do is Switch On The Code.

Anonymous
10/21/2009 - 18:17

"Lua itself is really not that useful"?

Not a very good start to an article. Clearly not familiar with the language.

reply

The Hairiest
10/22/2009 - 13:03

Well, if we talk about real world applications here, Lua by itself is hardly ever used. What that statement means it that Lua's power comes from it's ability to easily connect with other languages. For example, World of Warcraft uses Lua, but the interpreter is built into the WoW core. I have also used some small game engines that use Lua as a scripting component, again, using Lua in conjunction with other languages. You don't really see a Lua game, one which is strait Lua script.

This is opposed to a scripting Language such as JavaScript which you can really just start programming away without any need to fool around with plugging it into another language.

reply

Anonymous
12/06/2009 - 17:32

This tutorial was very helpful, Thanks!

reply

Anonymous
02/26/2010 - 16:27

erhfdbdfh

reply

Anonymous
02/26/2010 - 16:28

waa ku salaamay abaayo macaan niyadana qeyr u sheeg

reply

Add Comment

Put code snippets inside language tags:
[language] [/language]

Examples:
[javascript] [/javascript]
[actionscript] [/actionscript]
[csharp] [/csharp]

See here for supported languages.

Javascript must be enabled to submit anonymous comments - or you can login.

Sponsors