Page 1 of 1

Call CELX methods from a C++ module running inside Celestia

Posted: 08.10.2008, 08:18
by Johan
Hi all,

I am doing some experiment on Celx-scripts, I’m new to lua programming and I have a few questions about it.

Actually here is what I’m trying to do: I’d like to develop a C++ module which can interact with Celestia through Celx-scripts.

Therefore I built a lua module made in C++ with a Qt interface that I can call from a Celx-script. When Celestia “ticks” the script it replaces the main loop of my module:

Code: Select all

require("qtTest")

hooks = {}
function hooks:tick( dt )
   assert(qtTest.tick)
   time = celestia:gettime()
   qtTest.tick(time)
end

--------------------
-- register hooks
--------------------
celestia:setluahook( hooks )

--------------------
-- init
--------------------
assert(qtTest.init)
qtTest.init(1,nil)


I wrapped the two functions qtTest.tick() and qtTest.init() with Swig and it works well.

Now I would like to call Celx-methods from my module. First, when I initialized my module with luaopen_qtTest (lua_State* L) I stored the lua state for future use. Then for example I’d like to call "celestia:gettimescale" lua method from my C++ code, but I don’t know how to do this. Here is what I try to do:

Code: Select all

double timeScale;
lua_getglobal(L, "celestia:gettimescale");
lua_call(L,0,0);
timeScale = (double)lua_tonumber(L, -1);


Actually I'm sure that lua_getglobal isn't the right way to do, I made different try but for the moment all my tests end with a crash of Celestia when I try to lua_call a function… I don’t exceed in lua yet and I wonder if I have to get the "celestia" object first and then call the gettimescale function on it, but I don’t know the way to do it. I had a lookwithin the source code of Celestia, I saw the CelxLua::ClassNames array, so I'm looking this way. Would you help me understand how this great engine works? :)

Cheers,

Johan (SpaceBel)

Re: Call CELX methods from a C++ module running inside Celestia

Posted: 08.10.2008, 11:37
by selden
Johan,

Have you looked at the documentation for Lua's C API?

http://www.lua.org/pil/24.html

Re: Call CELX methods from a C++ module running inside Celestia

Posted: 08.10.2008, 12:21
by Johan
Hi, I was using a confusing tutorial about lua. I follow your advice of reading the official documentation and I'll get back with my questions.

Re: Call CELX methods from a C++ module running inside Celestia

Posted: 08.10.2008, 17:21
by Johan
I made a new try after reading the official documentation and Celestia code. The concept is hard to understand for a beginner but I think I've got the idea :


Code: Select all

lua_pushstring(L, "class_celestia");
lua_rawget(L, LUA_REGISTRYINDEX);
   
lua_pushstring(L, "gettimescale");
lua_rawget(L, -2);
   
if (lua_iscfunction(L,-1))
{
   QMessageBox::information( this, "Exemple::s_clicked",
                  QString("lua_iscfunction=True" )
                  )   ;
   ///////CRASH/////////
   lua_call(L,0,0);
   timeScale = (double)lua_tonumber(L, -1);

   QMessageBox::information( this, "Exemple::s_clicked",
                  QString("timeScale : *" ) + QString::number(timeScale) + QString("*" )
                  )   ;
}
else
{
   QMessageBox::information( this, "Exemple::s_clicked",
                  QString("lua_iscfunction=False" )
                  )   ;      
}


I get the message "lua_iscfunction=True" but it crashes when I make the lua_call. Can you help me find what I am doing wrong ?

Re: Call CELX methods from a C++ module running inside Celestia

Posted: 09.10.2008, 09:03
by chris
I'm not sure what's going wrong in your code, but you can use lua_pcall instead of lua_call to make debugging easier. lua_pcall is a 'protected' call. If something fails, it will return a non-zero value. A helpful error string will be on the top of the stack. Here's a sample usage:

Code: Select all

    lua_pushstring(costate, KbdCallback);
    lua_gettable(costate, LUA_GLOBALSINDEX);
    lua_pushstring(costate, c_p);
    timeout = getTime() + 1.0;
    if (lua_pcall(costate, 1, 1, 0) != 0)
    {
        cerr << "Error while executing keyboard-callback: " << lua_tostring(costate, -1) << "\n";
        result = false;
    }
    else
    {
        // success!
    }


The arguments to lua_pcall are: lua state, number of arguments passed, number of results, and stack index of an error handler function (0 in the above code, indicating no error handler.) Using lua_pcall should prevent a crash; if you post the error message, I should be able to figure out what's going wrong.

--Chris

Re: Call CELX methods from a C++ module running inside Celestia

Posted: 09.10.2008, 09:35
by Johan
Thank you for your answer. It's still crashing when I call lua_pcall so I wonder if I'm in the same lua state as Celestia. As I'm in a dll loaded by celx-script, maybe I'm not sharing the same memory as Celestia?

Re: Call CELX methods from a C++ module running inside Celestia

Posted: 09.10.2008, 10:18
by chris
Johan wrote:Thank you for your answer. It's still crashing when I call lua_pcall so I wonder if I'm in the same lua state as Celestia. As I'm in a dll loaded by celx-script, maybe I'm not sharing the same memory as Celestia?

If pcall isn't working, then it does indeed sound like you're using the wrong lua state. How are you getting the lua state object?

--Chris

Re: Call CELX methods from a C++ module running inside Celestia

Posted: 09.10.2008, 12:04
by Johan
As you see in my first post, I wrapped the two functions I call in my celx-script:

int init(int argc, char *argv[]);
int tick(double);


Swig generates a qtTest_wrap.cpp from my qtTest.cpp which defines the function.

In this qtTest_wrap.cpp Swig creates a SWIGEXPORT int SWIG_init(lua_State* L) wich is renamed into qtTest_init(lua_State* L). This function is the one that require("qtTest") call in lua code, I guess.

So I added the line gCelestiaLuaState = L; to store the lua state in my code.

My module is composed by qtTest.cpp (my code), and qtTest_wrap.cpp (the wrapping), compiled into qtTest.dll and linked to lua5.1.dll.

NB: the module initialisation:

Code: Select all

/* Forward declaration of where the user's %init{} gets inserted */
void SWIG_init_user(lua_State* L );
   
#ifdef __cplusplus
extern "C" {
#endif
/* this is the initialization function
  added at the very end of the code
  the function is always called SWIG_init, but an eariler #define will rename it
*/
SWIGEXPORT int SWIG_init(lua_State* L)
{
  gCelestiaLuaState = L; /// <----------------------------THE LINE I ADDED -------------------------
  int i;
  /* start with global table */
  lua_pushvalue(L,LUA_GLOBALSINDEX);
  /* SWIG's internal initalisation */
  SWIG_InitializeModule((void*)L);
  SWIG_PropagateClientData();
  /* add a global fn */
  SWIG_Lua_add_function(L,"swig_type",SWIG_Lua_type);
  SWIG_Lua_add_function(L,"swig_equals",SWIG_Lua_equal);
  /* begin the module (its a table with the same name as the module) */
  SWIG_Lua_module_begin(L,SWIG_name);
  /* add commands/functions */
  for (i = 0; swig_commands[i].name; i++){
    SWIG_Lua_module_add_function(L,swig_commands[i].name,swig_commands[i].func);
  }
  /* add variables */
  for (i = 0; swig_variables[i].name; i++){
    SWIG_Lua_module_add_variable(L,swig_variables[i].name,swig_variables[i].get,swig_variables[i].set);
  }
  /* set up base class pointers (the hierachy) */
  for (i = 0; swig_types[i]; i++){
    if (swig_types[i]->clientdata){
      SWIG_Lua_init_base_class(L,(swig_lua_class*)(swig_types[i]->clientdata));
    }
  }
  /* additional registration structs & classes in lua */
  for (i = 0; swig_types[i]; i++){
    if (swig_types[i]->clientdata){
      SWIG_Lua_class_register(L,(swig_lua_class*)(swig_types[i]->clientdata));
    }
  }
  /* constants */
  SWIG_Lua_InstallConstants(L,swig_constants);
  /* invoke user-specific initialization */
  SWIG_init_user(L);
  /* end module */
  lua_pop(L,1);  /* tidy stack (remove module table)*/
  lua_pop(L,1);  /* tidy stack (remove global table)*/
  return 1;
}

Re: Call CELX methods from a C++ module running inside Celestia

Posted: 10.10.2008, 08:59
by Johan
I think I'm getting the right Lua state because :

Code: Select all

   lua_pushstring(L, "celestia-scriptpath");
   lua_gettable(L, LUA_REGISTRYINDEX);


returns me "celxx/testIHM.lua" which is effectively my script.

I'm looking if I really do the good things to get the Celestia methods with my code above, I'm still learning Lua ;)


edit:after my lua_pushstring(L, "gettimescale"); I try to get the C function to get but I get un nil pointer.