Upcoming Events
Southwest Gaming Expo
11/20 - 11/22 @ Dallas, TX

Workshop on Network and Systems Support for Games (NetGames 2009)
11/23 - 11/25 @ Paris, France

ICIDS 2009 Interactive Storytelling
12/9 - 12/11 @ Guimarăes, Portugal

Global Game Jam
1/29 - 1/31  

More events...


Quick Stats
6434 people currently visiting GDNet.
2341 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!



Link to us

Link to us

  Intel sponsors gamedev.net search:   

Exporting C++ functions to Lua


Execution

This is moving along nicely! We’ve got a function object, and a vector of values of the correct types, so now all we need is a way to call the function with the parameters.

I banged my head against this problem for a little while, until I remembered a trick that I’d used a while back, that involved partial template specialization and a one line superstar called Int2Type.

template<int T> struct Int2Type {};
So, what does Int2Type do? Well, you create an instance of it with an integer, and you’ve got yourself a unique type for that integer value. “Um, yeah” I hear you saying, hoping that I’ll show you where I’m going with this. How about if we combine this with another metafunction from function_types, namely arity? This will give us unique types for each arity, and specializing a template on these types will allow us to create unique methods to invoke for each number of parameters.

Hopefully some code will be less confusing than that description!

/**
* The executor is a class with operator() specialized on the class Int2Type<N>, where N is the
* arity of the function we're calling. By specializing on the arity, we know the number of parameters
* the function expects, and can extract these from the vector<boost::any> and cast to the correct type
*/ 
template<class Fn, class ParamTypes>
struct Executor;

// 0 parameters
template<class ParamTypes>
struct Executor<Int2Type<0>, ParamTypes> {
  template<class Fn> void operator()(Fn& fn, const vector<boost::any>& /*params*/) {
    fn();
  }
};

// 1 parameter
template<class ParamTypes>
struct Executor<Int2Type<1>, ParamTypes> {
  template<class Fn> void operator()(Fn& fn, const vector<boost::any>& params) {
    fn( boost::any_cast<at<ParamTypes, int_<0> >::type>(params[0]) );
  }
};

// 2 parameters
template<class ParamTypes>
struct Executor<Int2Type<2>, ParamTypes> {
  template<class Fn> void operator()(Fn& fn, const vector<boost::any>& params) {
    fn( 
      boost::any_cast<at<ParamTypes, int_<0> >::type>(params[0]),
      boost::any_cast<at<ParamTypes, int_<1> >::type>(params[1]) );
  }
};
To be able to get the “unboxed” value from the boost::any value, we need to call boost::any_cast to cast it to the type we want. Getting the correct type for a parameter at any given index is done using the “at” metafunction which returns the type at the specified index in a typelist (using the int_<N> construct to specify the index)3. This type is passed as the template parameter that any_cast wants.

And once again we update our Execute method slightly to create the proper instance of Executor and call it.

// Typedef for the executor (based on the arity of the function)
typedef Executor< Int2Type<boost::function_types::function_arity<T>::value>, ParamTypes> Executor;

int Execute(lua_State* lua_state) const {
  AnyParams params;
  boost::mpl::for_each<ParamTypes>(Extractor(lua_state, params));
  Executor f;
  f(fn_, params);
  return 0;
}
We’ve now got type safe extraction and execution of functions with an arbitrary number of parameters (well, as many as we specialize the executor for), and only one piece of the puzzle remains.

Handling Return Values

The final problem, pushing eventual return values back on the Lua stack, actually consists of two smaller problems. First, we have to determine if the function we’re exporting has a return value at all; the reason for this is that we need separate code paths for handling the void and the non void return value cases. Pseudo code would look something like:
if (function_has_return_value) {
  return_value = function();
  push_on_lua_stack(return_value);
} else {
  function();
}
Ideally we want this resolved at compile time, as all the information needed is known then. The second problem is knowing that we have a return value, to push it on the stack.

Finding out if we have a return value or not is solved by combining function_types magic to get the type of the result, and using the is_void metafunction found in boost.type_traits to determine if it’s void.

typedef typename result_type<T>::type ResultType;
enum { IsVoidResult = boost::is_void<ResultType>::value };
If we view the pseudo code above as a case of overloading, with the if branch implemented in one function body, and the else branch in another, we just need to figure out how to call the correct branch, depending on the value of IsVoidResult. It’s time for Int2Type again!
// Typedef for the executor (based on the arity of the function)
typedef Executor< Int2Type<function_arity<T>::value>, ParamTypes, ResultType > Executor;

// Executer for void return type
int Execute(lua_State* lua_state, Int2Type<true>) const {
  AnyParams params;
  for_each<ParamTypes>(Extractor(lua_state, params));
  Executor f;
  f(fn_, params);
  return 0;
}

// Executer for non void return type
int Execute(lua_State* lua_state, Int2Type<false>) const {
  AnyParams params;
  for_each<ParamTypes>(Extractor(lua_state, params));
  Executor f;
  AddReturnValue(lua_state, f(fn_, params));
  return 1;
}

static int callback(lua_State* lua_state){
  const CommandT* this_ptr = reinterpret_cast<CommandT*>(lua_tointeger(lua_state, lua_upvalueindex(1)));
  return this_ptr->Execute(lua_state, Int2Type<IsVoidResult>());
}
We create two versions of Execute, one for the void case, and one for the non void case. We then use Int2Type to create an instance of either Int2Type<true> or Int2Type<false>, depending on the value of the enum IsVoidResult, and overloading takes care of calling the correct version.

Once this was solved, pushing the correct value to the Lua stack was a simple matter of overloading AddReturnValue for the types we wanted to be able to return, and a call to the correct lua_pushXXX function.

void AddReturnValue(lua_State* lua_state, const bool value) {
  lua_pushboolean(lua_state, value ? 1 : 0);
}

void AddReturnValue(lua_State* lua_state, const int value) {
  lua_pushinteger(lua_state, value);
}
And finally, we’re done (Yata!)

Future Work

Any time you start working on something fun, a million ideas for improvements pop into your head. Here are a couple of things that I might experiment with in the future.
  • Named parameters, and a single dispatch function. A friend at work was working on a system where he just exported a single function from C++ called dispatch, and that function took two parameters: the name of an internal function to call, and a table containing pairs of parameter names and values. This would require adding support for tables, amongst other things.
  • Support for member functions. Right now, we just handle global functions, but it should be fairly simple to extend to support member function pointers as well. This will require handling objects going out of scope and unregistering their callbacks when they do.
  • Multiple return values. Might be more fun than useful, but I’m interested in seeing if I can get Boost.Tuple to interact with this system, and allow for multiple return values on the Lua stack.
  • Use the Preprocessor Library for generating the execute code. That code is just repetitive and error prone to write for a greater number of parameters, and seems like a great fit for the Preprocessor Library.
  • Understand what’s going on under the hood of FunctionTypes. I think this can be summarized with *gulp*.

Sample Code

The sample code is a single .cpp file along with a .lua file that calls the exported functions. To be able to compile, you need to have Lua and Boost installed, with correct include paths set up. I’m using Lua 5.1 and Boost 1.36.

References

Lua
Boost, various libraries
Luabind
Modern C++ Design, Andrei Alexandrescu
C++ Template Metaprogramming, David Abrahams, Aleksey Gurtovoy

3 Using boost::any is a bit of overkill, as we know when we extract the parameters that we’re getting the correct types, so we don’t need the extra type safety/overhead that any_cast gives us



Contents
  Page 1
  Page 2

  Source code
  Printable version
  Discuss this article