The C++ interface to Lua implemented in Kyua exposes a lua::state class that wraps the lower-level lua_State* type. This class completely hides the internal C type of Lua to ensure that all calls that affect the state go through the lua::state class.

Things get a bit messy when we want to inject native functions into the Lua environment. These functions follow the prototype represented by the lua_CFunction type:
typedef int (*lua_CFunction)(lua_State*);
Now, let's consider this code:
int
awesome_native_function(lua_State* state)
{
// Uh, we have access to s, so we bypass the lua::state!
... do something nasty ...

// Oh, and we can throw an exception here...
//with bad consequences.
}

void
setup(...)
{
lua::state state;
state.push_c_function(awesome_native_function);
state.set_global("myfunc");
... run some script ...
}
The fact that we must pass a lua_CFunction prototype to the lua_pushcfunction object means that such function must have access to the raw lua_State* pointer... which we want to avoid.

What we really want is the caller code to define a function such as:
typedef int (*cxx_function)(lua::state&)
In an ideal world, the lua::state class would implement a push_cxx_function that took a cxx_function, generated a thin C wrapper and injected such generated wrapper into Lua. Unfortunately, we are not in an ideal world: C++ does not have high-order functions and thus the "generate a wrapper function" part of the previous proposal does not really work.

What we can do instead, though, is to make the creation of C wrappers for these C++ functions trivial. And this is what r42 did. The approach I took is similar to this overly-simplified (and broken) example:
template< cxx_function Function >
int
wrap_cxx_function(lua_State* state)
{
try {
lua::state state_wrapper(state);
return Function(state_wrapper);
} catch (...) {
luaL_error(state, "Geez, don't go into C's land!");
}
}
This template wrapper takes a cxx_function object and generates a corresponding C function at compile time. This wrapper function ensures that C++ state does not propagate into the C world, as that often has catastrophical consequences. (Due to language limitations, the input function must have external linkage. So no, it cannot be static.)

As a result, we can rewrite our original snippet as:
int
awesome_native_function(lua::state& state)
{
// See, we cannot access lua_State* now.

... do something ...
throw std::runtime_error("And we can even do this!");
}

void
setup(...)
{
lua::state state;
state.push_c_function(
wrap_cxx_function< awesome_native_function >);
state.set_global("myfunc");
... run some script ...
}
Neat? I think so, but maybe not so much. I'm pretty sure there are cooler ways of achieving the above purpose in a cleaner way, but this one works nicely and has few overhead.

Subscribe via RSS · Go to posts index

   Delivered by FeedBurner

Comments from the original Blogger-hosted post: