🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Function call wrapper

Started by
5 comments, last by Deyja 18 years, 8 months ago
Hi all :) Following my suggestion about hints/tips/code mentioned in this thread: I 've created a wrapper to call script functions from C++, which I think might be useful for some. Here are some examples:

// AngelScript function
void DoThis(int i, double d){}

// Call from C++
asIScriptEngine* engine = ...; // access 'engine' somehow
int functionID = engine->GetFunctionIDByDecl("module", "void DoThis(int, double)"); // I usually keep IDs handy so that I don't have to call this often
VoidExecutor<int, double> exec(engine, functionID); // create the executor class (void return type)
exec.Call(5, 0.314); // make the call with params :)

//-------

// AngelScript function
float DoThat(float f){}

// Call from C++
asIScriptEngine* engine = ...; // access 'engine' somehow
int functionID = engine->GetFunctionIDByDecl("module", "void DoThat(float)"); // I usually keep IDs handy so that I don't have to call this often
Executor<float, int, double> exec(engine, functionID); // create the executor class (float return type)
float f = exec.Call(0.314); // make the call with params and get a return value :)





If there's interest for it, I could post it here (it's a single header file) or I can send it to Andreas so that he can add it somewhere on his site. Until then, it ll be available on request ;) Regards, Yiannis :) PS: Btw, how can I set string arguments or get string return values from the context? It's the only type I haven't implemented yet...
Advertisement
If you wish to pass any registered object type you'll have to use the SetArgObject() method and retrieve the return value with the GetReturnObject() method.

You're welcome to post your implementation for this wrapper here. I will also be happy to put it up on the extras page on the AngelScript site.

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

Thanks for the info. I 'll update the code to support std::string and then post it here.

Regards,
Yiannis.
Well, I didn't add code for std::string in this because I wasn't sure how...
Anyway, here's the code. It contains comments so I guess everything should be clear enough.
I 'll be glad to clarify anything about it.

/*  LICENSE:  This code is provided as-is. No warranties are given. Use at your own risk.  You may do with this code whatever you want except presenting it as yours or  removing this license.    Copyleft 2005, Yiannis Mandravellos <mandrav _AT_ codeblocks _DOT_ org>*/#ifndef AS_EXECUTOR_H#define AS_EXECUTOR_H#include <iostream>#include <angelscript.h>/* A dummy struct. Used as default parameter in the following templated calls, to allow for up to 10 parameters in the call.*/struct DummyOperand {};/*    The following functions set the context's arguments before execution.    They 're templated to allow calling the correct SetArg* function based on    the param's type.*/template <typename OP> inline void ExecuteSetArg(asIScriptContext* ctx, int arg, OP op){ /* shouldn't reach here */ }template <> inline void ExecuteSetArg(asIScriptContext* ctx, int arg, DummyOperand op){ /* do nothing */ }template <> inline void ExecuteSetArg(asIScriptContext* ctx, int arg, asQWORD op){ ctx->SetArgQWord(arg, op); }template <> inline void ExecuteSetArg(asIScriptContext* ctx, int arg, asDWORD op){ ctx->SetArgDWord(arg, op); }template <> inline void ExecuteSetArg(asIScriptContext* ctx, int arg, double op){ ctx->SetArgDouble(arg, op); }template <> inline void ExecuteSetArg(asIScriptContext* ctx, int arg, float op){ ctx->SetArgFloat(arg, op); }template <> inline void ExecuteSetArg(asIScriptContext* ctx, int arg, void* op){ ctx->SetArgObject(arg, op); }/*    The following functions return the context's return value after execution.    They 're templated to allow calling the correct GetReturn* function based on    the return value's type.*/template <typename RET> inline RET ExecuteGetRet(asIScriptContext* ctx){ /* shouldn't reach here */ }template <> inline asQWORD ExecuteGetRet(asIScriptContext* ctx){ return ctx->GetReturnQWord(); }template <> inline asDWORD ExecuteGetRet(asIScriptContext* ctx){ return ctx->GetReturnDWord(); }template <> inline double ExecuteGetRet(asIScriptContext* ctx){ return ctx->GetReturnDouble(); }template <> inline float ExecuteGetRet(asIScriptContext* ctx){ return ctx->GetReturnFloat(); }template <> inline void* ExecuteGetRet(asIScriptContext* ctx){ return ctx->GetReturnObject(); }/** Executes a function taking as many as 10 parameters.  *  * This is the version that doesn't return a value (void).  * Use it like this  * (for a function taking a double and an integer parameter):  *  * VoidExecutor<double, int> exec(engine, functionID);  * exec.Call(5.331, 1);  *  * If you need more control of the process, here are more steps  * for the same example:  *  * VoidExecutor<double, int> exec(engine, functionID);  * exec.SetArguments(5.331, 1);  * asIScriptContext* ctx = exec.GetContext();  * // do whatever with the context  * ctx->Execute();  * ctx->Release();  *  * Notice that in the long version, you *must* release the context  * yourself. It is normally released in Call()...  */template <typename OP1 = DummyOperand, typename OP2 = DummyOperand,          typename OP3 = DummyOperand, typename OP4 = DummyOperand,          typename OP5 = DummyOperand, typename OP6 = DummyOperand,          typename OP7 = DummyOperand, typename OP8 = DummyOperand,          typename OP9 = DummyOperand, typename OP10 = DummyOperand>class VoidExecutor{    private:        asIScriptContext* m_pCtx;    public:        /** Constructor.          *          * Creates and prepares a context for execution.          * If you don't use Call(), you should release the context yourself by calling          * GetContext()->Release()...          *          * @param The engine.          * @param functionID The function's ID to call. You can get this by a call to GetFunctionIDByDecl().          * @param releaseContextWhenDone If this is true (default), the context will be released when          * done with it. If false, it is up to you to release it.          */        VoidExecutor(asIScriptEngine* engine, int functionID)            : m_pCtx(0)        {            // create the context            m_pCtx = engine->CreateContext();            if (!m_pCtx)            {                std::cerr << "Couldn't create context...\n";                return;            }            // prepare the script function            int r = m_pCtx->Prepare(functionID);            if (r < 0)            {                m_pCtx->Release();                std::cerr << "Couldn't prepare context...\n";                return;            }        }        /** Destructor.          *          * The context is release only when you use Call().          * If you haven't , you should release it yourself...          */        ~VoidExecutor(){}        /** Returns the created context (NULL if invalid). */        inline asIScriptContext* GetContext(){ return m_pCtx; }        /** Set the execution arguments.          * Up to 10 arguments can be set.          */        void SetArguments(OP1 op1 = DummyOperand(), OP2 op2 = DummyOperand(),                          OP3 op3 = DummyOperand(), OP4 op4 = DummyOperand(),                          OP5 op5 = DummyOperand(), OP6 op6 = DummyOperand(),                          OP7 op7 = DummyOperand(), OP8 op8 = DummyOperand(),                          OP9 op9 = DummyOperand(), OP10 op10 = DummyOperand())        {            if (!m_pCtx) return;            ExecuteSetArg<OP1>(m_pCtx, 0, op1); ExecuteSetArg<OP2>(m_pCtx, 1, op2);            ExecuteSetArg<OP3>(m_pCtx, 2, op3); ExecuteSetArg<OP4>(m_pCtx, 3, op4);            ExecuteSetArg<OP5>(m_pCtx, 4, op5); ExecuteSetArg<OP6>(m_pCtx, 5, op6);            ExecuteSetArg<OP7>(m_pCtx, 6, op7); ExecuteSetArg<OP8>(m_pCtx, 7, op8);            ExecuteSetArg<OP9>(m_pCtx, 8, op9); ExecuteSetArg<OP10>(m_pCtx, 9, op10);        }        /** Make the call.          *          * This runs all the necessary steps:          * - Sets the arguments,          * - Executes, and          * - Releases the context          *          * Up to 10 parameters can be used.          */        void Call(OP1 op1 = DummyOperand(), OP2 op2 = DummyOperand(),                  OP3 op3 = DummyOperand(), OP4 op4 = DummyOperand(),                  OP5 op5 = DummyOperand(), OP6 op6 = DummyOperand(),                  OP7 op7 = DummyOperand(), OP8 op8 = DummyOperand(),                  OP9 op9 = DummyOperand(), OP10 op10 = DummyOperand())        {            if (!m_pCtx) return;            SetArguments(op1, op2, op3, op4, op5, op6, op7, op8, op9, op10);            m_pCtx->Execute();            m_pCtx->Release();            m_pCtx = 0;        }};/** Executes a function taking as many as 10 parameters.  *  * This is the version that allows for a return a value.  * Use it like this  * (for a function taking a double and an integer parameter,  * returning a float):  *  * Executor<float, double, int> exec(engine, functionID);  * float f = exec.Call(5.331, 1);  *  * If you need more control of the process, here are more steps  * for the same example:  *  * Executor<float, double, int> exec(engine, functionID);  * exec.SetArguments(5.331, 1);  * asIScriptContext* ctx = exec.GetContext();  * // do whatever with the context  * ctx->Execute();  * float f = exec.GetReturnValue();  * ctx->Release();  *  * Notice that in the long version, you *must* release the context  * yourself. It is normally released in Call()...  */template <typename RET,          typename OP1 = DummyOperand, typename OP2 = DummyOperand,          typename OP3 = DummyOperand, typename OP4 = DummyOperand,          typename OP5 = DummyOperand, typename OP6 = DummyOperand,          typename OP7 = DummyOperand, typename OP8 = DummyOperand,          typename OP9 = DummyOperand, typename OP10 = DummyOperand>class Executor{    private:        asIScriptContext* m_pCtx;    public:        /** Constructor.          *          * Creates and prepares a context for execution.          * If you don't use Call(), you should release the context yourself by calling          * GetContext()->Release()...          *          * @param The engine.          * @param functionID The function's ID to call. You can get this by a call to GetFunctionIDByDecl().          */        Executor(asIScriptEngine* engine, int functionID)            : m_pCtx(0)        {            // create the context            m_pCtx = engine->CreateContext();            if (!m_pCtx)            {                std::cerr << "Couldn't create context...\n";                return;            }            // prepare the script function            int r = m_pCtx->Prepare(functionID);            if (r < 0)            {                m_pCtx->Release();                std::cerr << "Couldn't prepare context...\n";                return;            }        }        /** Destructor.          *          * The context is release only when you use Call().          * If you haven't , you should release it yourself...          */        ~Executor(){}        /** Returns the created context (NULL if invalid). */        inline asIScriptContext* GetContext(){ return m_pCtx; }        /** Set the execution arguments.          * Up to 10 arguments can be set.          */        void SetArguments(OP1 op1 = DummyOperand(), OP2 op2 = DummyOperand(),                          OP3 op3 = DummyOperand(), OP4 op4 = DummyOperand(),                          OP5 op5 = DummyOperand(), OP6 op6 = DummyOperand(),                          OP7 op7 = DummyOperand(), OP8 op8 = DummyOperand(),                          OP9 op9 = DummyOperand(), OP10 op10 = DummyOperand())        {            if (!m_pCtx) return;            ExecuteSetArg<OP1>(m_pCtx, 0, op1); ExecuteSetArg<OP2>(m_pCtx, 1, op2);            ExecuteSetArg<OP3>(m_pCtx, 2, op3); ExecuteSetArg<OP4>(m_pCtx, 3, op4);            ExecuteSetArg<OP5>(m_pCtx, 4, op5); ExecuteSetArg<OP6>(m_pCtx, 5, op6);            ExecuteSetArg<OP7>(m_pCtx, 6, op7); ExecuteSetArg<OP8>(m_pCtx, 7, op8);            ExecuteSetArg<OP9>(m_pCtx, 8, op9); ExecuteSetArg<OP10>(m_pCtx, 9, op10);        }        /** Make the call.          *          * @return RET          *          * This runs all the necessary steps:          * - Sets the arguments,          * - Executes, and          * - Releases the context          *          * Up to 10 parameters can be used.          */        RET Call(OP1 op1 = DummyOperand(), OP2 op2 = DummyOperand(),                  OP3 op3 = DummyOperand(), OP4 op4 = DummyOperand(),                  OP5 op5 = DummyOperand(), OP6 op6 = DummyOperand(),                  OP7 op7 = DummyOperand(), OP8 op8 = DummyOperand(),                  OP9 op9 = DummyOperand(), OP10 op10 = DummyOperand())        {            if (!m_pCtx) return;            SetArguments(op1, op2, op3, op4, op5, op6, op7, op8, op9, op10);            m_pCtx->Execute();            RET r = GetReturnValue();            m_pCtx->Release();            m_pCtx = 0;            return r;        }        /** Returns the return value (valid only after execution). */        inline RET GetReturnValue(){ if (m_pCtx) return ExecuteGetRet<RET>(m_pCtx); else return (RET)0; }};#endif // AS_EXECUTOR_H


Regards,
Yiannis :)
Hey, about that Code Blocks IDE, are you affiliated with it or just like it and want to advertise it?
Quote: Original post by Rain Dog
Hey, about that Code Blocks IDE, are you affiliated with it or just like it and want to advertise it?


Heh, probably both. I 'm its author ;)
Hey, that's actually pretty awesome.

This topic is closed to new replies.

Advertisement