Jump to content
  • Advertisement
Sign in to follow this  
mandrav

Function call wrapper

This topic is 4691 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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...

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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 :)

Share this post


Link to post
Share on other sites
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 ;)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!