LUA scripts

Started by
28 comments, last by Zorbfish 17 years ago
ok, so even a simple example ends up not being too simple. Here is an example which creates OO datatypes in LUA without C:

local Stick = {}function newStick()	local stick = {}	setmetatable(stick, Stick)	return stickendfunction isStick(stick)	if getmetatable(stick) == Stick then		return true	else		return false	endendfunction Person_throwStick(person)	assert( isPerson(person) )	if person.stick ~= nil then		print(person.name .. " threw a stick")		person.stick = nil	else		print(person.name .. " must hold a stick before throwing it")	endendfunction Person_holdStick(person, stick)	assert( isPerson(person) )	assert( isStick(stick) )	person.stick = stick	print( person.name .. " is now holding a stick ")endlocal Person = {__index = {throwStick = Person_throwStick, holdStick = Person_holdStick}}function isPerson(person)	if getmetatable(person) == Person then		return true	else		return false	endendfunction newPerson(name)	assert( type(name) == "string" )	local person = {}	setmetatable(person, Person)	person.name = name	return personend-- how it can be usedlocal p = newPerson("bob")local s = newStick()p:holdStick(s)p:throwStick()p:throwStick()-- results in:-- bob is now holding a stick -- bob threw a stick-- bob must hold a stick before throwing it


So, this is a nice sample using just lua. In your case, the Person table, the Stick table, the Person_ functions, etc would all be done in C. This way, the metatable would point to cfunctions instead of lua functions. Keep in mind that the p:holdStick(s) <==> p.holdStick(p, s)

define the metatables like this:
// these are the methods that MidiOut objects havestatic const luaL_reg MidiOut_meta[] = {	{"__gc",	MidiOut_gc},	{"noteOn",	MidiOut_noteOn},	{"noteOff",	MidiOut_noteOff},	{"sendMessage", MidiOut_sendMessage},	{NULL, 		NULL}};static int MidiOut_register(lua_State* L){	luaL_newmetatable(L, MIDIOUT);	lua_pushliteral(L, "__index");	lua_newtable(L);	luaL_openlib(L, 0, MidiOut_meta, 0);	lua_rawset(L, -3);	lua_pop(L, 1);	return 1;}


this is from my midi module which had a MidiOut object with the member functions noteOn, noteOff, and sendMessage. MidiOut_noteOn, etc are all defined and coded as you would expect as a typical C/Lua wrapper function.

here are some wrapper functions for creating new objects, pushing them onto the stack, safely pulling them off of the stack, etc.

#define MIDIOUT "MidiOut"// some helper methods for accessing RtMidiOut:static RtMidiOut* toMidiOut(lua_State *L, int index){	RtMidiOut **midiout = (RtMidiOut**)lua_touserdata(L, index);	if(midiout == NULL)		luaL_typerror(L, index, MIDIOUT);	return *midiout;}static RtMidiOut* checkMidiOut(lua_State* L, int index){	RtMidiOut** midiout;	RtMidiOut* ret;	luaL_checktype(L, index, LUA_TUSERDATA);	midiout = (RtMidiOut**)luaL_checkudata(L, index, MIDIOUT);	if(midiout == NULL)		luaL_typerror(L, index, MIDIOUT);	ret = *midiout;	if(!ret)		luaL_error(L, "null RtMidiOut*");	return ret;}static RtMidiOut** pushMidiOut(lua_State* L, RtMidiOut* mo){	RtMidiOut** midiout = (RtMidiOut**)lua_newuserdata(L, sizeof(RtMidiOut*));	*midiout = mo;	luaL_getmetatable(L, MIDIOUT);	lua_setmetatable(L, -2);	return midiout;}static int MidiOut_noteOn(lua_State *L){	RtMidiOut* midiout = checkMidiOut(L, 1);	midiout->noteOn( ... );	return 0;}


Let me know if you have any more questions. I only recently figured all of this stuff out, so it will definitely help me get a clearer understanding of it all.
Advertisement
Quote:Original post by Dwiel
ok, so even a simple example ends up not being too simple. Here is an example which creates OO datatypes in LUA without C:

*** Source Snippet Removed ***

So, this is a nice sample using just lua. In your case, the Person table, the Stick table, the Person_ functions, etc would all be done in C. This way, the metatable would point to cfunctions instead of lua functions. Keep in mind that the p:holdStick(s) <==> p.holdStick(p, s)

define the metatables like this:
*** Source Snippet Removed ***

this is from my midi module which had a MidiOut object with the member functions noteOn, noteOff, and sendMessage. MidiOut_noteOn, etc are all defined and coded as you would expect as a typical C/Lua wrapper function.

here are some wrapper functions for creating new objects, pushing them onto the stack, safely pulling them off of the stack, etc.

*** Source Snippet Removed ***

Let me know if you have any more questions. I only recently figured all of this stuff out, so it will definitely help me get a clearer understanding of it all.


Hi Dwiel!
Thanks so much for your answer!!! :)

I understood really well how LUA example code! Thank you very much!

Regarding the C++ example I found it more complicated.
I am not familiar with the commands:
lua_pushliteral(L, "__index");
lua_rawset(L, -3);
lua_pop(L, 1);
or lua_setmetatable(L, -2);

Could you point me as to where did you learn this? Information seems to be very scarce and succinct.

Thank you so much!
CHeers
Rod
Yeah, no problem. I get most of my information from a few sources:

I use this many times a day as a good reference:
http://www.lua.org/manual/5.1/

I use this if I want something more than a reference. It's got some techniques and more explanation:
http://www.lua.org/pil/

Here is a little tutorial about the C/lua API:
http://lua-users.org/wiki/SimpleLuaApiExample

there are quite a few good tutorials on the wiki:
http://lua-users.org/wiki/
and tutorial section:
http://lua-users.org/wiki/TutorialDirectory

Glad to be able to help!

Edit: forgot to linkify

[Edited by - Dwiel on March 22, 2007 8:31:54 PM]
Dwiel thanks for all the info!!! :)
I have been using your links and they are really useful.

Thanks Namatsu, Roots, and Dwiel for all the help received up to now :)

I have a problem now I can't find the solution anywhere..
Let's see if anyone can help me:

I am trying to use a function that returns multiple parameters (with no success yet!):

this is my code in C++
Quote:
static int LUA_Object3D_GetPosition(lua_State* s)
{
Object3D * tempObject = (Object3D *) (lua_touserdata(s,1));

lua_pushnumber(s,tempObject->Position_Now.x);
lua_pushnumber(s,tempObject->Position_Now.y);
lua_pushnumber(s,tempObject->Position_Now.z);

return (3);
}


and this is my code in LUA:
Quote:
local x;
local y;
local z;
x,y,z = Object3D.GetPosition( Iterator1_Object3D);
Font.PrintCentered(0.1,"Position x:" .. x .."y:" .. y .. "z:" ..z );


.. and I am receiving that x, y , and z contain nil values.
"Cannot concatenate nil values"
What am I doing wrong?

Thanks so much everyone in advance!!!
Cheers,
Rod
Thats really strange. That is exactly how you should do it as far as I can tell. I don't see any obvious bugs. Have you tried adjusting that exact code without the sendond and third pushnumber and returning 1 just to test to make sure that nothing else is causing problems?

Hi Dwiel!
Thanks so much for your response.

Yes it's weird.

Quote:

local x;
local y;
local z;
x,y,z = Object3D.GetPosition( Iterator1_Object3D);
Font.PrintCentered(0.1,"Position x:" .. x .."y:" .. y .. "z:" ..z );

The strangest part is that x gets the correct first pushed value, while y and z get nil.

Quote:
Have you tried adjusting that exact code without the sencond and third pushnumber and returning 1 just to test to make sure that nothing else is causing problems?


You mean like this?:
Quote:
static int LUA_Object3D_GetPosition(lua_State* s)
{
Object3D * tempObject = (Object3D *) (lua_touserdata(s,1));

lua_pushnumber(s,tempObject->Position_Now.x);

return (3);
}

I haven't tried that, but I have been using lua_pushnumber with return(1) -alas 1 parameter- with no problem. But returning more than one parameter doesn't seem to work....
Is it a problem of the function definition or that I load it into a LUA table in C++?

What do you suggest me to try? I don't know where to look at..

Thanks so much for all your help, I really appreciate every word you write to answer me :)

Cheers
Rod
Hmm, very strange. What do you mean by "I load it into a LUA table in C++"?

Here is some code that is working in my program that accomplishes what you are talking about.
static int luamidi_getMessage(lua_State *L){	std::vector<unsigned char> message;	int port = luaL_checkint(L, 1);	RtMidiIn* midiin = OpenIn(L, port);	double delta = midiin->getMessage(&message);	if(message.size() == 3)	{		lua_pushnumber(L, message[0]);		lua_pushnumber(L, message[1]);		lua_pushnumber(L, message[2]);		lua_pushnumber(L, delta);		return 4;	}	return 0;}


Again, I'm not sure what you meant by loading into a table, but it may have something to do with the problem. What code are you using to "load into a lua table from C++?"
Ah, the joys of debugging C++ -> Lua.

I suggest creating a unit test. Create a 3D Object Iterator with a known position (hardcode it if possible) to test against.

First, print the value of the position from the C++ pointer first (I used printf). If you get garbage there's probably something wrong with how you're setting up your userdata and should investigate there.

Second, print the values pushed by Lua. If the numbers don't match up there could be something wrong with your implementation of Lua. I vaguely remember something about a certain implementation of Lua not reporting correct number values on the mailing list but could not find the exact posts.

Third, print the values (on the Lua side) obtained from the function. If they passed the second step you should get the same values.

The last case where you concatenate numbers with strings was my initial gut reaction to where the problem might be. I don't see a problem with any of the code you've posted.

static int LUA_Object3D_GetPosition(lua_State* s) {  Object3D * tempObject = (Object3D *)(lua_touserdata(s,1));  printf("%d, %d, %d", tempObject->Position_Now.x,    tempObject->Position_Now.y, tempObject->Position_Now.z);  lua_getglobal(L, "print");  lua_pushnumber(s, tempObject->Position_Now.x);  lua_pushnumber(s, tempObject->Position_Now.y);  lua_pushnumber(s, tempObject->Position_Now.z);  lua_call(L, 3, 0);  lua_pushnumber(s, tempObject->Position_Now.x);  lua_pushnumber(s, tempObject->Position_Now.y);  lua_pushnumber(s, tempObject->Position_Now.z);  return 3;}-- In Lualocal x, y, z = Object3D.GetPosition(Iterator1_Object3D)print(x, y, z)Font.PrintCentered(0.1, "Position x:" .. x .. "y:" .. y .. "z:" .. z)
Thank you so much Dwiel and Zorbfish! :)

I tried all the ideas you guys suggested for isolating the bug... and I FOUND IT!!!! :)

The bug was where I though it would never be. I was so sure, that I didn't even post it in the original message:

To make it short, I was wrapping the Object3D.GetPosition() inside another function, and the problem was some parenthesis '(' ')':

This is not what I use, but to illustrate the problem:
Quote:
function wrapperIncorrect(myObject)
return (Object3D.GetPosition(myObject));
end

x,y,z = wrapperIncorrect(myObject);
--> x gets first return value, and y and z get nil



The correct way (which I really discovered it by chance):

Quote:
function wrapperCorrect(myObject)
return Object3D.GetPosition(myObject);
end

x,y,z = wrapperCorrect(myObject);
--> x, y, and z get correct return values



So it seems that if you encapsulate a function with parenthesis, then you get only the first return value.

I hadn't posted the wrapper as I was sure the error was not there...
Where is that documentation!? I had never read about that parenthesis functionality. Did you guys know?

Thanks SO much for everything!!!
Cheers!
Rod
I'm glad you found the problem. For rodrix, and anyone else who is interested, here's an explanation right from PiL on the truncation of multiple returns from Lua functions. It is about 2/3 of the way down on that page. It's a bit more subtle in the reference manual.

This topic is closed to new replies.

Advertisement