Here is one possible solution:
Each of your game objects derives from a class (say, LuaUser)
which holds a pointer to Lua table. This table represents the
Lua side of the C++ object.
When calling events, the C++ code provides a default (usually
just return without doing anything, or do any straightforward
stuff), and calls a Lua script, if present. This script is a
field of the above mentionned table. When a scripted object
wants to override something, it just sets a new function there
which will get called on the relevant event, which will get
called with, as first parameter, the Lua table corresponding
to this object.
For instance, if the event is on_triggered, and the object is
a bell, then the default C++ routine might just return, or play
a default sound.
If I create a Lua object which wants to override this, its
initialization script (which will receive as a parameter the
newly created table which corresponds to this object) will
place the override there, eg:
Which brings me to the engine extensions which are supplied
to the scripts. PlayFunkySound is one of them. They all (or
almost, where appropriate) get a Lua table as a first parameter,
so they know what scripted object "does" something.
They could be generated (eg with toLua) but mine are hand
Extensions now need to map the table to the original LuaUser.
My first try was to add a _owner field in each Lua table, but
after a short time, I added a void* (really, a LuaUser*) field
in Lua's table structure (in C). Once this LuaUser* pointer is
known, you have the actual C++ state of the object with which
you can do whatever you want (in my game, the world state is
contained in C++, the Lua side taking care of the overridable
game mechanics, and acting on the engine side to reflect it,
as in the Lua side knows whether a door is closed or open, and
tells the engine whether, as a conseqeunce, the given passage
is passable or not).
Hope this helps,