Binding C/C++ functions to Lua is a tedious, error prone and time consuming task when done by hand. A custom C++ introspection system can aide in the automation of binding any callable C or C++ function or method a breeze. Once such a functor-like object exists the act of binding a function to Lua can look like this, as seen in a CPP file:

// Bind a C style global function, or static function BIND_FUNCTION( FuncNameInC ); // Optionally can provide string name by hand BIND_FUNCTION( "NameInLua", FuncNameInC ); // Bind a class or struct method BIND_FUNCTION( "NameInLua", Class::Method ); 1 2 3 4 5 6 7 8 // Bind a C style global function, or static function BIND_FUNCTION ( FuncNameInC ) ; // Optionally can provide string name by hand BIND_FUNCTION ( "NameInLua" , FuncNameInC ) ; // Bind a class or struct method BIND_FUNCTION ( "NameInLua" , Class :: Method ) ;

The advantage of such a scheme is that only a single CPP file would need to be modified in order to expose new functionality to Lua, allowing for efficient pipe-lining of development cycles.

Another advantage of this powerful functor is that communication and game logic can be quickly be created in a script, loaded from a text file, or even setup through a visual editor. Here is a quick example of what might be possible with a good scripting language:

// Called when target emits the Death message EnemyOnTargetDeath( self ) { // Flash for visual feedback self.Flash( ) // Acquire a new target from global GameLogic system GameLogic.FindNewTarget( self ) } EnemyInit( self, target ) { self.target = target // Know when target emits the Death message, call the // EnemyOnTargetDeath function self.Subscribe( target, "Death", EnemyOnTargetDeath ) } EnemyUpdateRoutine( self ) { constant minDist = 10 PauseSeconds( 1 ) if(self.target.Distance( self ) > minDist) self.Attack( self.target ) PauseSeconds( 2 ) else self.MoveTowards( self.target ) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 // Called when target emits the Death message EnemyOnTargetDeath ( self ) { // Flash for visual feedback self . Flash ( ) // Acquire a new target from global GameLogic system GameLogic . FindNewTarget ( self ) } EnemyInit ( self , target ) { self . target = target // Know when target emits the Death message, call the // EnemyOnTargetDeath function self . Subscribe ( target , "Death" , EnemyOnTargetDeath ) } EnemyUpdateRoutine ( self ) { constant minDist = 10 PauseSeconds ( 1 ) if ( self . target . Distance ( self ) > minDist ) self . Attack ( self . target ) PauseSeconds ( 2 ) else self . MoveTowards ( self . target ) }

In the above example a simple enemy is supposed to follow some target object. If the target is close enough then the enemy damages it. If the target dies, the enemy flashes a bright color and then acquires a new target.

The key here is the message subscription within the initialization routine. During run-time objects can subscribe to know about messages emitted by any other object!

So by now hopefully one would have seen enough explanation of function binding to understand how powerful it is. I’ve written some slides on the topic available in PDF format here (do note that these slides were originally made for a lecture at my university):

Download (PDF, 447KB)