$LastChangedDate: 2011-03-17 19:59:52 -0400 (Thu, 17 Mar 2011) $

-- See PITFALLS at bottom --

REMEMBER: Indexs of tables passed between C++ and Lua must be ## ZERO-BASED ##.
Lua scripts must define special Lua classes such as Vec3 with zero-based indexs
because passing array-like one-based Lua tables to zero-based C++ is wrong.

Note: Avoid writing LUA_/lua_ to avoid naming conflicts with Lua itself.

Module for the simulator's interface to Lua scripts.
Also represents the Lua interpreter as the Lua class singleton.

Source code files:
------------------
bind_*.lua    Macros, written in Lua, for expanding C++ code
              that is the binding between C++ and Lua.

bind_*.cc     Auto-generated.

lua.hh        Lua singleton class that abstracts the Lua interpreter for use by C++.

lua_lang.hh   This includes headers of the Lua language itself.

macros.hh     Macros/functions to assist and simplify writing
              glue code between C++ and Lua.

Macroization (auto-generation) of bindings:
-------------------------------------------
The bindings from C++ to Lua (ironically) are written as macros in Lua.
That is, Lua is used as a macro-processing language.
A cmake makefile runs tools/autobind.lua that auto-generates C++ files.
The purpose of macroization is to reduce the C++ binding code (which has grown large),
and to facilitate any future changes to C++ binding code.

C++ inheritance of methods is macroized by string substitution of C++ class names.
An example is bind_object_common.lua for the Object hierarchy.
Specifically for bind_dyna.lua etc, InheritMethod() does string substituion
of C++ class names in bind_object_common.lua.

Adding a new C++/Lua binding:
-----------------------------
1. Copy-and-modify an existing bind*.lua.
   If an Object-derivative, use bind_dyna.lua as a template
   as it has special code to auto-inherit many Object methods.
2. touch bind*.cc as an empty file.
3. Add line to register the binding at lua_bind/register.hh
4. Add lines to build/lua_bind/CMakLists.txt
5. Check that bind*.cc was auto-generated.

[OLD]
Adding a new C++ class to the interface:
[superceded by autobind.lua]
----------------------------------------
- Create a new bind_<CLASS>.cc file.
- Write a new RegisterCLASS() function.
- Add a call to RegisterCLASS() in the Lua ctor at lua.cc:
  Lua::Lua( void )
  {
    // Register with Lua all C++ function and class interfaces.
    RegisterFunctions( mLua );
    RegisterCLASS( mLua );
  }
- Add dummy RegisterCLASS() to the tests/lua/src/lua.cc.

autobind.lua format:
--------------------
bindTable = {
class             = [ name of C++ and Lua class (same name) ]
typesig           = [ TYPESIG_<class> ]
luaCanInstantiate = [ false if a singleton else true ]
}

Lua manipulates C++ objects as reference-counted pointers.
----------------------------------------------------------
To avoid C++ new/delete problems, instances of C++ classes
are created as reference-counted pointers.
C++ classes are exported to Lua as metatables.
The __gc() method is called by Lua's garbage-collector.
__gc() is implemented by DESTROY_USERDATA_SHPTR() to decrement the reference-count.
C++ delete is not called explicitly thus avoiding problems
if the simulator is still using a C++ object.
This is implemented by allocating a Lua "userdata" object.
Then C++ "placement new" is used to construct an shptr smart-pointer
at that memory location.

Implementation notes:
---------------------
- C++ should pop in reverse order, but push in normal order:
  And C++ must tell Lua how many items it pushed.

  Aircraft_SetPosition( lua_State* L )
  {
    fp z = POP_FP( L );  // reverse order
    fp y = POP_FP( L );
    fp x = POP_FP( L );
  }

  Aircraft_GetPosition( lua_State* L )
  {
    WorldVertex pos = aircraft->GetPosition();
    lua_pushnumber( L, pos[XX] );
    lua_pushnumber( L, pos[YY] );
    lua_pushnumber( L, pos[ZZ] );
    return 3;  // stack items to return
  }

- Suppressing Lua type errors (optional table members):
  The code in macros.cc is written expecting that table members are required.
  If a type error is detected, lua_checktype() will do a longjump back to Lua
  and raise an error in Lua (this stops execution of the C++ binding code).

  If table members are optional, the auto-class AutoSuppressLuaError is used
  to temporarily disable errors in Lua and instead throw a C++ exception
  which allows C++ to probe if a table member is present using try/catch.

  AutoSuppressLuaError works similar to std::auto_ptr.
  Its ctor and dtor toggles a suppress-error flag which affects CHECK_TYPE_ERROR().

  INLINE LuaNumber
  GET_MEMBER_AS_NUMBER( lua_State* L, const char* memberName, const bool optional = false )
  {
    AutoSuppressLuaError autoSuppressLuaError( optional );
    CHECK_TYPE_ERROR( L, -1, LUA_TTABLE );
    lua_getfield( L, -1, memberName );
    return POP_NUMBER( L );  // also calls CHECK_TYPE_ERROR()
  }

- C binding that returns multiple values:

  The top stack item is popped into the first (leftmost) variable, so the order is:

  local id,data = c.Func()

  lua_pushnumber( L, id );
  lua_pushnumber( L, data );
  return 2;  // stack items to return

PITFALLS AND COMMON MISTAKES:
-----------------------------

- Cannot mix Lua metatables in the Lua binding code:

  This code cannot be made to work.

  "GetObject" is a Lua function bound to the "Missile" metatable.
  Trying to return userdata bound to the "Object" metatable won't work.
  The userdata will never be garbage-collected resulting in Objects that persist
  (guessing the reason has to do with how Lua calls registered metatable methods).

  The solution was to replace C++ composition with C++ deriviation
  so that Missile _is_ an Object (so that classes won't be mixed).

  ..........................
  bind_missile.lua

  #define BIND_CLASS_NAME      "Missile"
  #define BIND_METATABLE_NAME  LUABIND_METATABLE_STRING(BIND_CLASS_NAME)

  func_GetObject = {
      comments = "object Missile:GetObject() / for Lua to compare pointer and release Object",
      code = [[
        shptr<Missile> missile = POP_USERDATA_AS_SHPTR<Missile>( L );
        CHECK_TYPESIG(missile,TYPESIG_MISSILE);
        shptr<Object> object = missile->GetObject();
        CHECK_TYPESIG(object,TYPESIG_OBJECT);
          return NEW_USERDATA_SHPTR<Object>( L, object, BIND_METATABLE_NAME );
                                    ^^^^^^^  <--------> ^^^^^^^^^^^^^^^^^^^
      ]]
  },

Code snippets:
--------------

................................................................................
// A Lua table with optional table members:
// If a table member is missing, GET_MEMBER_AS_type() will get and push nil.
// Since nil isn't of "type", it will throw a C++ exception,
// but it will leave a "nil" on the stack.  Therefore, the Lua stack
// should be saved and restored in such cases.

    code = [[
        // Restore stack in case a C++ exception is thrown during
        // during stack operations involved in accessing optional table members.
        SAVE_STACK( L );
        ...
        // .mPos
        try
        {
            hudTextArgs.mPos[XX] = GET_MEMBER_AS_NUMBER( L, "x", OPTIONAL );
            hudTextArgs.mPos[YY] = GET_MEMBER_AS_NUMBER( L, "y", OPTIONAL );
        }
        catch( const LuaException& ) { } // ignore
        RESTORE_STACK( L );
................................................................................
