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.