Script Call Q

Started by
3 comments, last by WitchLord 18 years, 2 months ago
How easy would be to implement a call in to the engine for calling a script function as calling an exported function from a dll: therefore instead:

int funcId = engine->GetFunctionIDByDecl(0, "TYPE foo(TYPE1, TYPE2)");
r = ctx->Prepare(funcId);
TYPE1 a;
TYPE2 b;
ctx->SetArgType1(0, a);
ctx->SetArgType2(1, b);
r = ctx->Execute();
TYPE returnValue = ctx->GetReturnType();
to have something like.

typedef TYPE (*PFN)(TYPE2, TYPE3);
TYPE pFoo =  engine->GetProcAddress(0, "TYPE foo(TYPE1, TYPE2)");
TYPE retval = (pFoo)(a,b);
MCO
Advertisement
I think witchlord talked about this once actually. I wouldn't want to mudle about with spoofing function pointers to do that; but it wouldn't be difficult to wrap the script up behind a functor interface. The interface would be something like

Functor f = engine->GetFunction(blah);f(arguments);
A functor interface isn't hard to write. However, AngelScript's public API doesn't seem to expose enough type information to write a good functor interface. Here's the (incomplete) code I used to create a functor interface:
void intrusive_ptr_add_ref(asIScriptEngine * t) {  t->AddRef();}void intrusive_ptr_release(asIScriptEngine * t) {  t->Release();}void intrusive_ptr_add_ref(asIScriptContext * t) {  t->AddRef();}void intrusive_ptr_release(asIScriptContext * t) {  t->Release();}typedef boost::intrusive_ptr<asIScriptEngine> EnginePtr;typedef boost::intrusive_ptr<asIScriptContext> ContextPtr;class Var {  public:    Var(asIScriptContext & context) {      void * ptr = context.GetReturnObject();      if (ptr) {        storage_.as_pointer = ptr;        is_object_ = true;      } else {        storage_.as_qword = context.GetReturnQWord();        is_object_ = false;      }    }        operator float() {      return storage_.as_float;    }    operator double() {      return storage_.as_double;    }    template <typename T>     operator T *() {      if (is_object_) return static_cast<T *>(storage_.as_pointer);      return 0;    }    template <typename T>    operator T() {      return static_cast<T>(storage_.as_qword);    }  private:    union Data {      asDWORD as_dword;      asQWORD as_qword;      float   as_float;      double  as_double;      void *  as_pointer;    };    Data storage_;    bool is_object_;};class CallableObject {  public:    CallableObject(asIScriptEngine & engine, const char * module, const char * function)      : engine_(&engine), func_id_(engine.GetFunctionIDByDecl(module, function))    {      assert(engine_); // null references evil      if (func_id_ < 0) {        std::stringstream sstr;        sstr << "Function: " << function << " not found.";        throw std::runtime_error(sstr.str());      }    }        Var operator()(void) {      ContextPtr context(engine_->CreateContext());      if (!context.get())        throw std::runtime_error("Unable to create context");            int r = context->Prepare(func_id_);      if (r < 0)        throw std::runtime_error("Failed to prepare the context");              r = context->Execute();            return Var(*context);    }    template <typename Arg1>    Var operator()(Arg1 arg1) {      ContextPtr context(engine_->CreateContext());      if (!context.get())        throw std::runtime_error("Unable to create context");            int r = context->Prepare(func_id_);      if (r < 0)        throw std::runtime_error("Failed to prepare the context");              set_arg(*context, 0, arg1);              r = context->Execute();            return Var(*context);    }    template <typename Arg1, typename Arg2>    Var operator()(Arg1 arg1, Arg2 arg2) {      ContextPtr context(engine_->CreateContext());      if (!context.get())        throw std::runtime_error("Unable to create context");            int r = context->Prepare(func_id_);      if (r < 0)        throw std::runtime_error("Failed to prepare the context");              set_arg(*context, 0, arg1);      set_arg(*context, 1, arg2);              r = context->Execute();            return Var(*context);    }  private:    static void set_arg(asIScriptContext & context, asUINT arg, int i) {      set_arg_worker(context, arg, static_cast<asDWORD>(i));    }    static void set_arg(asIScriptContext & context, asUINT arg, float f) {      set_arg_worker(context, arg, f);    }    static void set_arg(asIScriptContext & context, asUINT arg, double d) {      set_arg_worker(context, arg, d);    }      static void set_arg_worker(asIScriptContext & context, asUINT arg, asDWORD dword) {      int r = context.SetArgDWord(arg, dword);      if (r < 0) {        std::string err_type;        switch (r) {          case asINVALID_ARG:            err_type = "too many arguments";            break;          case asINVALID_TYPE:            err_type = "invalid type";            break;          default:            assert(false);        }        throw std::runtime_error("Error calling function: " + err_type);      }    }    static void set_arg_worker(asIScriptContext & context, asUINT arg, asQWORD qword) {      int r = context.SetArgQWord(arg, qword);      if (r < 0) {        std::string err_type;        switch (r) {          case asINVALID_ARG:            err_type = "too many arguments";            break;          case asINVALID_TYPE:            err_type = "invalid type";            break;          default:            assert(false);        }        throw std::runtime_error("Error calling function: " + err_type);      }    }    static void set_arg_worker(asIScriptContext & context, asUINT arg, float f) {      int r = context.SetArgFloat(arg, f);      if (r < 0) {        std::string err_type;        switch (r) {          case asINVALID_ARG:            err_type = "too many arguments";            break;          case asINVALID_TYPE:            err_type = "invalid type";            break;          default:            assert(false);        }        throw std::runtime_error("Error calling function: " + err_type);      }    }    static void set_arg_worker(asIScriptContext & context, asUINT arg, double d) {      int r = context.SetArgDouble(arg, d);      if (r < 0) {        std::string err_type;        switch (r) {          case asINVALID_ARG:            err_type = "too many arguments";            break;          case asINVALID_TYPE:            err_type = "invalid type";            break;          default:            assert(false);        }        throw std::runtime_error("Error calling function: " + err_type);      }    }      static void set_arg_worker(asIScriptContext & context, asUINT arg, void * ptr) {      int r = context.SetArgObject(arg, ptr);      if (r < 0) {        std::string err_type;        switch (r) {          case asINVALID_ARG:            err_type = "too many arguments";            break;          case asINVALID_TYPE:            err_type = "invalid type";            break;          default:            assert(false);        }        throw std::runtime_error("Error calling function: " + err_type);      }    }    EnginePtr engine_;    int func_id_;};

And here's how to use it:

CallableObject calc(*engine, 0, "float calc(float, float)");
float ret_val = calc(6.55f, 4.55f);

Now, here's the bugger, calling with:

float ret_val = calc(6.55, 4.55);

will cause an exception because setting a double as an argument when a float is specified as the type causes a runtime error. There are ways around this but they all seem to come in two varieties: either horribly inefficient or require modifying the AngelScript public API. Or you could live with needing to be very precise about the argument types. I personally didn't find any of the choices too attractive, so I never bothered to finish up the functor code.
Thank you.
MCO
Another script function wrapper has been written by mandrav and can be found here: http://www.gamedev.net/community/forums/topic.asp?topic_id=350859.

I've been thinking about different ways of making it easier to call script functions. Maybe I'll use some of the ideas presented by SiCrane and mandrav.

AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game

This topic is closed to new replies.

Advertisement