Buena pregunta. Tenía una vaga idea de cómo luabind hace lo que hace, pero no sabía lo suficiente como para responder de manera completa y precisa.Armado con un IDE y depurador empecé a diseccionar la siguiente pieza muy simple:
struct C
{
int i;
int f(int x, const char* s)
};
lua_State* L = luaL_newstate();
open(L);
module(L)
[
class_<C>("C")
.def_readwrite("index", &C::i)
.def("f", &C::f)
];
primero que hay que notar es que L
se pasa a luabind mucho, la llamada a open
crea un par de variables globales en el estado de Lua: la __luabind_classes
de tipo userdata y dos funciones class
y property
. Luabind no parece usar variables globales; todo lo que necesita se guarda en el entorno lua.
Ahora llegamos a module(L)[...]
. El código original es la mejor explicación, primero aquí está module
: lo suficientemente
inline module_ module(lua_State* L, char const* name = 0)
{
return module_(L, name);
}
simple, este es module_
:
class LUABIND_API module_
{
public:
module_(lua_State* L_, char const* name);
void operator[](scope s);
private:
lua_State* m_state;
char const* m_name;
};
Así que lo que hace nuestro pequeño programa es llamada operador [] en la clase module_
con algunas definiciones (ese es el parámetro scope
), pero la clase module_
sabe en qué estado de Lua operar. La clase scope
También es interesante observar (algunas partes se omiten y algunos ligeramente simplificada):
struct LUABIND_API scope
{
//...
explicit scope(detail::registration* reg);
scope& operator,(scope s);
void register_(lua_State* L) const;
private:
detail::registration* m_chain;
};
scope
es la construcción de una lista enlazada de nodos detail::registration
, esa lista proviene del uso de operator,
. Así que cuando uno hace module(L) [class_<...>..., class_<...>...]
, class_
que hereda de scope
inicializa su base de un ejemplo detail::registration
, entonces el operador coma de scope
construye una lista enlazada de todos los registros, esta se pasa a module_::operator[]
que exige scope::register_
que a su vez enumera la cadena y llama register_
en todos esos objetos detail::registration
. El lua_State
siempre se pasa al register_
.
Phew. Ahora veamos qué sucede cuando uno hace class_<C>("C").def("f", &C::f)
. Esto construye una instancia de class_<C>
con un nombre determinado que va en el miembro detail::registration
en class_
. Al llamar al método class_::def
escribe en la estructura de registro y todo eso, pero aquí hay una línea muy interesante profundizar en la cadena de llamada de def
:
object fn = make_function(
L, f, deduce_signature(f, (Class*)0), policies);
Oooh, deduce_signature
, realmente quería ver eso. Ahora quiero unsee, pero la forma en que funciona es la siguiente: por medio de la magia oscura preprocesador ayudado por impulso (BOOST_PP_ITERATE
y algunas otras utilidades) se genera para cada N entre uno y LUABIND_MAX_ARITY lo siguiente:
template <class R, class T, class A1, classA2, ..., classAN>
boost::mpl::vectorN_PLUS_2<R, T, A1, A2, ..., AN> // type of return value
deduce_signature(R(T::*)(A1, A2, ..., AN))
{
return boost::mpl::vectorN_PLUS_2<R, T, A1, A2, ..., AN>()
}
Una vez más, una función como esta se genera para todo N entre 1 y LUABIND_MAX_ARITY que es 10 por defecto. Hay un par de sobrecargas para manejar los métodos const, los wrappers virtuales y las funciones gratuitas, lo que significa que hay alrededor de 50 deduce_signature
funciones que terminan en sus fuentes justo después del preprocesador y antes de que la compilación haya comenzado. A partir de ahí, es tarea del compilador elegir la sobrecarga deduce_signature
correcta para las funciones que pasa al def
y que devolverá el tipo correcto boost::mpl::vectorX
. A partir de ahí, make_function
puede hacer cualquier cosa: tiene una lista [de tiempo de compilación] de tipos de parámetros y, a través de más magia de plantilla, se cuentan, se convierten a valores de Lua y viceversa, y así sucesivamente.
Aquí es donde voy a parar.La investigación se basa en Luabind 0.8.1. Siéntase libre de navegar/depurar el código de Luabind para obtener más respuestas; lleva un tiempo, pero no es tan difícil una vez que se acostumbre al estilo :) Buena suerte.
TL; DR: Magia ... magia negra