[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: test
- From: Mike Ferenduros <mike@...>
- Date: Sun, 07 Nov 2004 20:42:45 +0000
Hello,
I had the same problem, and solved it thusly (although I'm also fairly
new to Lua so there's probably a better way).
Each Lua-visible object has a pointer to a linked-list of Lua references
to it. So each userdata contains not just a pointer to the object, but
also prev and next pointers linking it into that object's list of userdatas.
When the userdata gets garbage-collected, you unlink it from the list,
and when the C object is deleted, you walk through its list of userdatas
and NULL out the object pointer inside each one.
So now when a script passes a stale userdata into a C function, you can
easily detect it because the userdata's enemy pointer will be NULL.
Something like this should work:
typedef struct enemy_ref_s
{
struct userdata_s *prev;
struct userdata_s *next;
class Enemy *enemy;
}
enemy_ref_t;
class Enemy
{
public:
enemy_ref_t *m_refs;
Enemy( void )
{
//Initially has no userdatas pointing to it
m_refs = 0;
}
~Enemy( void )
{
//Invalidate all userdatas pointing to this enemy
for( enemy_ref_t *ref=m_refs; ref; ref=ref->next )
ref->enemy = 0;
}
};
void new_enemy_ud( lua_State *l, Enemy *enemy )
{
//Creates a new Enemy userdata on the stack
enemy_ref_t *ref = (enemy_ref_t *)lua_newuserdata( l,
sizeof(enemy_ref_t) );
luaL_getmetatable( l, "Enemy" ); //this metatable's __gc should
point to 'enemy_ud_gc'
lua_setmetatable( l, -2 );
//Add this ud to the list of refs to this enemy
ref->enemy = enemy;
ref->prev = 0;
ref->next = enemy->m_refs;
if( ref->next ) ref->next->prev = ref;
enemy->m_refs = ref;
}
int enemy_ud_gc( lua_State *l )
{
//Remove from list of userdatas pointing to this enemy
struct userdata_s *ref = (enemy_ref_t *)luaL_checkudata( l, 1,
"Enemy" );
if( ref->prev ) ref->prev->next = ref->next;
if( ref->next ) ref->next->prev = ref->prev;
if( ref->enemy && ud==ud->enemy->m_refs )
ref->enemy->m_refs = ref->next;
return( 0 );
}
This code assumes you've set up a metatable called "Enemy", and it's
__gc points to 'enemy_ud_gc'.
It's a bit long-winded, but it works ok (and should be a lot faster than
scanning all Luas globals).
It might be nicer if each enemy could only have a single userdata
pointing to it, and you always returned the existing reference to a
given enemy instead of creating new ones. However, as a relative newbie
I'm not sure how easy that is to do.
Anyway, hope this helps (or makes sense at least).
Perhaps this is something to address on lua-users.org?
Jens Hassler wrote:
Hi there,
I am using Lua a lot in my game with great success. I exposed my game
API via toLua to my scripts. But now I have a "typical" C/pointer
problem:
In my script I do something like that:
my_enemy = enemy_class:create_enemy()
This returns a userdata (?) pointing to the enemy object which was
created by the C++ function create_enemy.
Now this enemy is destroyed and deleted inside the C++ code and
"my_enemy" is pointing to neverland (and causing a crash when accessed).
So, is there an "elegant" way to set such rogue userdata objects to nil?
Actually I work around this problem with calling a "enemy_dead()" LUA
function from C code when an enemy object is about to be destroyed. In
this function I'm looping through my globals and set the ones pointing
to the enemy to nil.
It'd be perfect If I could reset these variables directly from my C++
code so that all Lua userdata objects will point to nil when the enemy
is destroyed.
Thanks for your help.
Jens