When I change the return value to "void" so the function declaration reads "void getWindowId()" I can call the function fine, as well as all of the other functions in the class (all void).
Is there something I'm not doing right? I tried the property accessors, but I still get that same error message.
C++ console class (gutted with relevant functions only):
class Console { void init(); //This is called once the graphics environment is finished setting up, it creates the script console class. }
void Console::init() //Attach this class to the script console, once the graphics engine is initialized { scriptConsole = graphics->getEngine()->getScriptManager()->getObject("ui","Console","()"); //Create a Console :3 if (scriptConsole == 0) graphics->stop(); else scriptConsole->AddRef(); //Store the object }
//Script Manager
asIScriptObject* ScriptManager::getObject(const std::string& moduleName,const std::string& objectName,const std::string& params) { // Get the object type asIScriptModule *module = engine->GetModule(moduleName.c_str()); if (module == 0) { ss.str(""); ss.clear(); ss<<"Attempted to use non-existent module "<<moduleName.c_str()<<".\n"; log->log(ss.str()); return 0; } asIObjectType *type = engine->GetObjectTypeById(module->GetTypeIdByDecl(objectName.c_str()));
if (type == 0) { ss.str(""); ss.clear(); ss<<"Could not retrieve object type "<<objectName.c_str()<<".\n"; log->log(ss.str()); return 0; } // Get the factory function id from the object type std::string decl = objectName + " @" + objectName + params; int factoryId = type->GetFactoryIdByDecl(decl.c_str());
// Prepare the context to call the factory function asIScriptContext* ctx = prepareContext(factoryId); // Execute the call ctx->Execute();
// Get the object that was created //asIScriptObject* obj = *(asIScriptObject**)ctx->GetAddressOfReturnValue(); asIScriptObject* obj = (asIScriptObject*)ctx->GetReturnAddress(); if (obj == 0) { ss.str(""); ss.clear(); ss<<"Could not instanciate object "<<objectName.c_str()<<" from script.\n"; log->log(ss.str()); return 0; } returnContext(ctx); return obj; //Let the calling function determine if they want to save it or not :3 }
asIObjectType* type = obj->GetObjectType(); // Obtain the id of the class method irr::s32 funcId = type->GetMethodIdByDecl(function.c_str());
// Prepare the context for calling the method asIScriptContext* ctx = prepareContext(funcId);
//Parse types and set arguments //types are: c - 8bit,w - 16bit,i - 32bit,q - 64bit,f - float,d - double,v - arg by Address,V - obj by address for(int i=0;types != '\0';i++) { switch(types) { case 'c': //8bit ctx->SetArgByte(i,(char)va_arg(vl,int)); break; case 'w': //16bit ctx->SetArgWord(i,(short)va_arg(vl,int)); break; case 'i': //32bit ctx->SetArgDWord(i,va_arg(vl,int)); break; case 'q': //64bit int ctx->SetArgQWord(i,va_arg(vl,long long)); break; case 'f': //float ctx->SetArgFloat(i,(float)va_arg(vl,double)); break; case 'd': //double ctx->SetArgDouble(i,va_arg(vl,double)); break; case 'v': //arg by addr ctx->SetArgAddress(i,va_arg(vl,void*)); break; case 'V': //obj by addr ctx->SetArgObject(i,va_arg(vl,void*)); break; default: break; } }
va_end(vl);
r = ctx->Execute(); if( r != asEXECUTION_FINISHED) { // The execution didn't complete as expected. Determine what happened. if( r == asEXECUTION_EXCEPTION) { // An exception occurred, let the script writer know what happened so it can be corrected. ss.str(""); ss.clear(); ss<<"Exception! Line: " << ctx->GetExceptionLineNumber() << " Func: " << engine->GetFunctionDescriptorById(ctx->GetExceptionFunction())->GetDeclaration() << "\nString: " << ctx->GetExceptionString() << ".\n"; log->log(ss.str()); return 0; } }
//Check return type void* ret = 0; switch(retType) { case 'o': { ret = ctx->GetReturnAddress(); return ret; break; } case 'O': { ret = ctx->GetReturnObject(); return ret; } case 'v': { ret = ctx->GetAddressOfReturnValue(); return ret; } }
// Clean up returnContext(ctx);
return 0; }
returnContext() and prepareContext are copies from your game example. It seems to work well. I haven't had time to rewrite the portions that use that code yet. Here is the actual call:
int Console::getWindowId() { int* windowId; if(bBufferDumped == false) //This will be true when the console is ready. { return -1; } else { void* test = graphics->getEngine()->getScriptManager()->exec(scriptConsole,"int getWindowId()",'v',""); (void)0; int justADebuggingBreakpointPlaceholder = 4; //windowId = (int*) } //return *windowId; return -1; }
Before the console gets created, the logger will still try to spit text out to it, which would cause a segfault or other nastyness, so the C++ class stores the text temporarily until the console class gets a signal to dump the text buffer to the script console class after it has been created, which it then does and sets the bBufferDumped flag to true. Then this getWindowId() gets executed whenever the GUI library senses a GUI event, and calls this function. Debugging reveals that my generic pointer, void* test is zero and an exception logged as above.
So, the exec function takes a stored asIScriptObject, grabs the type, and calls the method signature in the second parameter. The third parameter is the return type, which is "return by value address" in this case. The fourth parameter is a string of variable types that the rest of the parameters occupy.
You're calling a script method, but you never inform the actual object that the method is called on in ScriptManager::exec. You need to call ctx->SetObject(obj); before executing the script function. Unless you left out this by mistake when posting here, this is the cause for null pointer exception.
Also, you may want to cache the method id returned by GetMethodIdByDecl(). This function is quite slow as it has to parse the function declaration, and then search for matching methods in order to find the correct one.