partially define a table in C++, and partially in script

classic Classic list List threaded Threaded
11 messages Options
Reply | Threaded
Open this post in threaded view
|

partially define a table in C++, and partially in script

Shea Martin-3
//I want to do something like this:
Person = {}
Person.mt = {}

function Person:new( pname )
  Person.mt.__index = Person
  return setmetatable( { name = pname }, Person.mt )
end

function Person:talk( mesg )
  print( self.name .. " says '" .. mesg .. "'." )
end

function Person:do_stuff()
  self:talk( "hello" )
end

fred = Person:new( "Fred" )
fred:do_stuff()

//I have a person 'class' I would like to partially define in my C++ program.
//I would like to the rest of it to be defined in a lua script.

//this function is registered as GetNewPerson( name )
static int l_newPerson( lua_State* vm )
{
  luaL_checktype( vm, -1, LUA_TSTRING );
  std::string name = lua_tostring( vm, -1 );
  lua_pop( vm, 1 );

  lua_newtable( vm );

  lua_pushstring( vm, "Person:name" );
  lua_string( vm, name.c_str() );
  lua_settable( vm, -3 );

  lua_pushstring( vm, "Person:talk" );
  lua_pushclosure( vm, l_person_talk, 0 );
  lua_settable( vm, -3 );

  luaL_newmetatable( vm, "Person.mt" );

  lua_pushstring( vm, "__index" );
  lua_pushstring( vm, "Person" );
  lua_settable( vm, -3 );

  return 1;
}

//now I would like the script to define Person:do_stuff.
//Thus my script would be shortened to:

function Person:do_stuff()
  self:talk( "hello" )
end

fred = GetNewPerson( "Fred" )
fred:do_stuff()


I hope this makes sense...
Thanks,

~S


Reply | Threaded
Open this post in threaded view
|

Re: partially define a table in C++, and partially in script

Rici Lake-2

On 2-Feb-07, at 3:44 PM, Shea Martin wrote:

//I want to do something like this:
Person = {}
Person.mt = {}

function Person:new( pname )
  Person.mt.__index = Person
  return setmetatable( { name = pname }, Person.mt )
end

function Person:talk( mesg )
  print( self.name .. " says '" .. mesg .. "'." )
end

function Person:do_stuff()
  self:talk( "hello" )
end

fred = Person:new( "Fred" )
fred:do_stuff()

//I have a person 'class' I would like to partially define in my C++ program.
//I would like to the rest of it to be defined in a lua script.

You can just completely mimic the Lua implementation; that's got to be the simplest approach. See my response to the "Checking type of userdata" thread, which has some code which you shouldn't copy verbatim :) (since it's identifying classtypes in a dumb way in order to prove a point), but might help you get started. The "new" function defined there creates an empty userdata, but it could just as well have created a table. The "make" class method creates a new class and returns the metatable for that class, which can then get filled in with instance methods, either in C (using luaL_register), or in Lua. Code sample at http://primero.ricilake.net/lua/zara.c


Reply | Threaded
Open this post in threaded view
|

Re: partially define a table in C++, and partially in script

Shea Martin-3
Sorry to be dense, but I am still not following.

My lua script is simply this:

function Event:start()
  self:log( self.name .. " started" )
end

My C is something like this:

if( luaL_newmetatable( vm, "Event.mt" ) )
{
	//trying to duplicat this functionality
  //Event = {}
  //Event.mt = {}
  //
  //function Event:new( pname )
  //  Event.mt.__index = Event
  //  return setmetatable({  name = pname }, Event.mt)
  //end

	//the following two lines are yours
	//I am not quite sure fo their purpose
	lua_pushvalue( vm, -1 ); //put mt on stack
	lua_setfield( vm, -2, "__index" );

	//this is what makes sense to me
	//but it does not work either
	lua_pushstring( vm, "Event" );
	lua_pushstring( vm, "__index" );
	lua_settable( vm, -3 );
}

lua_newtable( vm );

lua_pushstring( vm, "Event:log" );
lua_pushcclosure( vm, l_event_log, 0 );
lua_settable( vm, -3 );

lua_pushstring( vm, "Event:name" );
lua_pushstring( vm, eventname );
lua_settable( vm, -3 );

lua_setmetatable( vm, -2 );

luaL_dofile( vm, "test.lua" );

lua_getfield( vm, LUA_-1, "Event:start" );
lua_pcall( vm,  1, 2, 0 );


When I run my code, I get this interpreter error: " attempt to index global 'Event' (a nil value)".

I am a little lost, this being my first work with and dynamically prototyped language, and also having to explicitly manage the stack.

~S


Reply | Threaded
Open this post in threaded view
|

Re: partially define a table in C++, and partially in script

Doug Rogers-4
Shea Martin wrote:

> Sorry to be dense, but I am still not following.
> lua_pushstring( vm, "Event:log" );
> lua_pushcclosure( vm, l_event_log, 0 );
> lua_settable( vm, -3 );
> lua_pushstring( vm, "Event:name" );
> lua_pushstring( vm, eventname );
> lua_settable( vm, -3 );

The code above is the same as saying

mt["Event:log"] = l_event_log
mt["Event:name"] = event_name

Note that luaL_newmetatable() places the table's name in the registry,
not in the global table, so 'mt' above is really just the anonymous
table that's left on the top of the stack.

These are understandable newbie mistakes. Just (re)read the sections of
PiL that contain C examples - chapters 25 and 26 in particular. Also,
you may want to (re)read the section on methods (the ':' syntactical
sugar), chapter 16. Note that ':' only works for function calls.

What you really want is a function with key "log", and a string with key
"name" to hold the name field. I suspect that what you *really* want is
the 'name' field to be present in an instance of an event, not in the
class (table "Event" plus its metatable).

// Untested...
// Create global table to hold "Event".
lua_newtable( vm);

// Create metatable.
luaL_newmetatable( vm, "Event.mt");
lua_pushstring( vm, "log" );
lua_pushcclosure( vm, l_event_log, 0 );  // Need a closure?
lua_settable( vm, -3 );

lua_pushvalue( vm, -2);    // Get copy of table Event to top.
lua_setfield( vm, -2, "__index");  // mt.__index = Event

lua_setmetatable( vm, -2);  // Attach metatable.

lua_pushcfunction( vm, l_event_new);  // Sample below.
lua_setfield( vm, -2, "new");
lua_setglobal( vm, "Event");   // Put main table in module.

Most likely you will also need some sort of mechanism for creating
objects of that type. I called that 'new' above. In that function you
will use luaL_getmetatable() to retrieve the metatable by name from the
registry, then you'll attach that to a new table, possibly like this
(untested):

int l_event_new(lua_State* L, const char* event_name)
{
  lua_newtable(L);                    // local obj = {}
  luaL_getmetatable(L, "Event.mt");   // setmetatable(obj, mt)
  lua_setmetatable(L, -2);
  lua_pushstring(L, event_name);     // obj.name = event_name
  lua_settable(L, -2, "name");
  return 1;                           // return obj
}

-- In Lua:
ev = Event.new('attack')
ev:log()

Those are some untested hints. You may want to invest the time (it's not
too long) to understand what is happening in the Lua-only version of
this, then see how it is implemented using the C API. It may be that
'log' should be a global function rather than an object method.

Good luck!

Doug

-- 
Innovative Concepts, Inc. www.innocon.com 703-893-2007 x220

Reply | Threaded
Open this post in threaded view
|

Re: partially define a table in C++, and partially in script

Shea Martin-3
Good luck!

Doug


Thanks, great reply. I am functional in many languages. After learning the first couple, I have been able to become sufficient in subsequent languages in hours. Lua is a different paradigm than I am used to; I keep trying to 'cheat' with Lua... It looks like I better quit looking for shortcuts. (Part of the issue is the fact I am embedding it as I learn it.)

Thanks for the advice,

~S


Reply | Threaded
Open this post in threaded view
|

RE: partially define a table in C++, and partially in script

Jerome Vuarand-2
Shea Martin wrote:
> Thanks, great reply.  I am functional in many languages.  
> After learning the first couple, I have been able to become 
> sufficient in subsequent languages in hours.  Lua is a 
> different paradigm than I am used to; I keep trying to 
> 'cheat' with Lua...  It looks like I better quit looking for 
> shortcuts. (Part of the issue is the fact I am embedding it 
> as I learn it.)

You will soon discover that Lua has its own shortcuts that allow to do
things almost impossible in most languages.


Reply | Threaded
Open this post in threaded view
|

Re: partially define a table in C++, and partially in script

Romulo Bahiense
In reply to this post by Doug Rogers-4
Doug Rogers wrote:
(...)

int l_event_new(lua_State* L, const char* event_name)
{
  lua_newtable(L);                    // local obj = {}
  luaL_getmetatable(L, "Event.mt");   // setmetatable(obj, mt)
  lua_setmetatable(L, -2);
  lua_pushstring(L, event_name);     // obj.name = event_name
  lua_settable(L, -2, "name");
  return 1;                           // return obj
}

Shouldn't it be something like...

int l_event_new( lua_State* L )
{
    luaL_checkstr(L, 1);
    lua_newtable(L);                    // local obj = {}
    luaL_getmetatable(L, "Event.mt");   // setmetatable(obj, mt)
    lua_setmetatable(L, -2);
    lua_pushvalue(L, 1);                // obj.name = event_name
    lua_settable(L, -2, "name");
    return 1;                           // return obj
}

?

--rb

Reply | Threaded
Open this post in threaded view
|

Re: partially define a table in C++, and partially in script

Doug Rogers-4
In reply to this post by Doug Rogers-4
Doug Rogers wrote:

> luaL_newmetatable( vm, "Event.mt");
> ...
>   luaL_getmetatable(L, "Event.mt");   // setmetatable(obj, mt)

As an aside, I prefer not to use '.' in my metatable names because I
find it confusing. I usually use "object_mt", such as "bstring_mt".

Since the name will only appear in the registry it can never be
retrieved from Lua code, so there's not much danger in its being
misunderstood by Lua end users. For experienced Lua C API programmers
the '.' is like a huge beacon that says "NOTICE ME! I'm not a normal
table entry!" I see that as a moderate benefit.

I still prefer not to use it.

Doug

-- 
Innovative Concepts, Inc. www.innocon.com 703-893-2007 x220


Reply | Threaded
Open this post in threaded view
|

Re: partially define a table in C++, and partially in script

Doug Rogers-4
In reply to this post by Romulo Bahiense
Romulo Bahiense wrote:
> Shouldn't it be something like...
> int l_event_new( lua_State* L )
> {
>     luaL_checkstr(L, 1);
>     lua_newtable(L);                    // local obj = {}
>     luaL_getmetatable(L, "Event.mt");   // setmetatable(obj, mt)
>     lua_setmetatable(L, -2);
>     lua_pushvalue(L, 1);                // obj.name = event_name
>     lua_settable(L, -2, "name");
>     return 1;                           // return obj
> }

You're right. In addition to adding the checks above, Shea, you will
need to get the name from the Lua stack if this is being called from Lua
directly (as it was in my example code). If you are creating the value
from C code, though, you would pass the name in as a const char* then
push it on the stack.

Doug

-- 
Innovative Concepts, Inc. www.innocon.com 703-893-2007 x220


Reply | Threaded
Open this post in threaded view
|

Re: partially define a table in C++, and partially in script

Shea Martin-3
In reply to this post by Doug Rogers-4
Doug Rogers wrote:
Shea Martin wrote:

Sorry to be dense, but I am still not following.
lua_pushstring( vm, "Event:log" );
lua_pushcclosure( vm, l_event_log, 0 );
lua_settable( vm, -3 );
lua_pushstring( vm, "Event:name" );
lua_pushstring( vm, eventname );
lua_settable( vm, -3 );

The code above is the same as saying

mt["Event:log"] = l_event_log
mt["Event:name"] = event_name

Note that luaL_newmetatable() places the table's name in the registry,
not in the global table, so 'mt' above is really just the anonymous
table that's left on the top of the stack.

These are understandable newbie mistakes. Just (re)read the sections of
PiL that contain C examples - chapters 25 and 26 in particular. Also,
you may want to (re)read the section on methods (the ':' syntactical
sugar), chapter 16. Note that ':' only works for function calls.

What you really want is a function with key "log", and a string with key
"name" to hold the name field. I suspect that what you *really* want is
the 'name' field to be present in an instance of an event, not in the
class (table "Event" plus its metatable).

// Untested...
// Create global table to hold "Event".
lua_newtable( vm);

// Create metatable.
luaL_newmetatable( vm, "Event.mt");
lua_pushstring( vm, "log" );
lua_pushcclosure( vm, l_event_log, 0 );  // Need a closure?
lua_settable( vm, -3 );

lua_pushvalue( vm, -2);    // Get copy of table Event to top.
lua_setfield( vm, -2, "__index");  // mt.__index = Event

lua_setmetatable( vm, -2);  // Attach metatable.

lua_pushcfunction( vm, l_event_new);  // Sample below.
lua_setfield( vm, -2, "new");
lua_setglobal( vm, "Event");   // Put main table in module.

Most likely you will also need some sort of mechanism for creating
objects of that type. I called that 'new' above. In that function you
will use luaL_getmetatable() to retrieve the metatable by name from the
registry, then you'll attach that to a new table, possibly like this
(untested):

int l_event_new(lua_State* L, const char* event_name)
{
  lua_newtable(L);                    // local obj = {}
  luaL_getmetatable(L, "Event.mt");   // setmetatable(obj, mt)
  lua_setmetatable(L, -2);
  lua_pushstring(L, event_name);     // obj.name = event_name
  lua_settable(L, -2, "name");
  return 1;                           // return obj
}

-- In Lua:
ev = Event.new('attack')
ev:log()

Those are some untested hints. You may want to invest the time (it's not
too long) to understand what is happening in the Lua-only version of
this, then see how it is implemented using the C API. It may be that
'log' should be a global function rather than an object method.

Good luck!

Doug


I want to pass an Event object to a function called 'start', which is defined in the script. I used your example for defining Event. Instead of registereing l_event_new, I just create the new table on the stack, and do a pcall( vm, 1, 1, 0 ). It seems that everything works except my member function 'log'. Index 'log' is nil, ie, not defined.



void DefinePlayerEvent( lua_State* vm )
{	
	lua_newtable( vm );

	lua_pushstring( vm, "log" );
	lua_pushcclosure( vm, l_event_log, 0 );
	lua_settable( vm, -3 );

	lua_pushvalue( vm, -2 );  //get Event from global space
	lua_setfield( vm, -2, "__index" ); // mt.__index = Event

	lua_setmetatable( vm, -2 ); //attach metatable

	//lua_setglobal( vm, "Event" );
}

void PutPlayerEventOnLuaStack( lua_State* vm, const char* eventname, int player_id, float x, float y )
{
	lua_newtable( vm );
	luaL_getmetatable( vm, "Event.mt" );
	lua_setmetatable( vm, -2 );

	
	lua_pushinteger( vm, player_id );
	lua_setfield( vm, -2, "player_id" );

	lua_pushnumber( vm, x );
	lua_setfield( vm, -2, "x" );
	
	lua_pushnumber( vm, y );
	lua_setfield( vm, -2, "y" );

	lua_pushstring( vm, eventname );
	lua_setfield( vm, -2, "name" );
}

Still reading, not sure what I am missing here.

~S


Reply | Threaded
Open this post in threaded view
|

Re: partially define a table in C++, and partially in script

Shea Martin-3
Shea Martin wrote:

void DefinePlayerEvent( lua_State* vm )
{ lua_newtable( vm );

    lua_pushstring( vm, "log" );
    lua_pushcclosure( vm, l_event_log, 0 );
    lua_settable( vm, -3 );

    lua_pushvalue( vm, -2 );  //get Event from global space
    lua_setfield( vm, -2, "__index" ); // mt.__index = Event

    lua_setmetatable( vm, -2 ); //attach metatable

    //lua_setglobal( vm, "Event" );
}


Ok, after reading chapter 28, I changed my DefinePlayerEvent() to this:
void DefinePlayerEvent( lua_State* vm )
{	
	luaL_newmetatable( vm, "Event.mt" );

	lua_pushstring( vm, "log" );
	lua_pushcclosure( vm, l_event_log, 0 );
	lua_settable( vm, -3 );

	lua_pushstring( vm, "__index" );
	lua_pushvalue( vm, -2 );
	lua_pushvalue( vm, -2 );
	lua_settable( vm, -3 );
}

and it works!  Thanks, I'll keep reading.

~S