Advertisement

AngelScript and Fastdelegates

Started by March 28, 2016 12:12 PM
12 comments, last by WitchLord 8 years, 7 months ago

True, an arbitrary method pointer can have different sizes. But in your case the method pointer to the FastDelegate's operator() method will always be of the same size regardless of the arguments (on MSVC it will have the size of a simple pointer), but in your case you're probably best to typedef a simple class method as it will be more portable.

// Declare a simple dummy class so the compiler can determine what a method pointer would look like,

// i.e. it can see that the method pointer won't need to keep track of multiple or virtual inheritances

class CSimpleDummy {};

typedef void (CSimpleDummy::*SIMPLEMETHOD_t)();
// Take the address of the FastDelegate's class method like this:
SIMPLEMETHOD_t methodAddr = reinterpret_cast<SIMPLEMETHOD_t >(&ScriptComponentMethodString::operator());

The methodAddr can then be passed from the exe to the dll just like any other primitive type. AngelScript will take care of interpreting the method pointer and setting up the arguments for the call dynamically based on what you tell it in the function declaration string when registering the method with the engine.

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

That's interesting. What about delegates for methods with return values? Will the "void (CSimpleDummy::*SIMPLEMETHOD_t)()" signature work for those too?

And thank you for explaining this, I know it stopped being about AngelScript some time ago but still you kept helping :)

Advertisement

And to answer my own question, yes it seems to work with return values. So I take that the only thing that matters in this case is the syntax string?

So, to document this whole thing (and hopefully get feedback if you see anything potentially bad) this is what I am doing. In the dll, in a header I have to the following helpers:


class ScriptMethodAddressDummy { };
typedef void(ScriptMethodAddressDummy::*ScriptMethodAddress)();

struct ScriptMethodPointer
{
	template <typename T>
	ScriptMethodPointer(T& delegate)
	{
		delegateObjectAddress = &delegate;
		methodAddress = reinterpret_cast<ScriptMethodAddress>(&T::operator());
	}

	void* delegateObjectAddress;
	ScriptMethodAddress methodAddress;
};

Except for the dummy suggested by Andreas I made a small class that can be passed any fastdelegate of any kind and it extracts the needed addresses out of it. My registration function (also on the dll side) now looks like this:


void ScriptedComponent::registerScriptMethod(asIScriptEngine* scriptEngine, const char* typeName, const char* scriptSyntax, const ScriptMethodPointer& methodPointer)
{
	int32_t r = scriptEngine->RegisterObjectMethod(typeName, scriptSyntax, asSMethodPtr<sizeof(ScriptMethodAddress)>::Convert(methodPointer.methodAddress), asCALL_THISCALL_OBJLAST, methodPointer.delegateObjectAddress);
	assertf(r >= 0, "Failed to register method \"%s\" of type \"%s\" with the script engine.", scriptSyntax, typeName);
}

Does that look OK? Particularily the asSMethodPtr part.

On the exe side, let's say I have the following method I want to extract:


void TankAIDrivingBehavior::moveToGameObject(StringHash gameObjectNameHash);

I first make the proxy and delegate.


namespace
{
	void moveToGameObjectProxy(StringHash gameObjectNameHash, void* self)
	{
		static_cast<TankAIDrivingBehavior*>(self)->moveToGameObject(gameObjectNameHash);
	}
	FastDelegate2<unsigned int, void*, void> moveToGameObjectDelegate = &moveToGameObjectProxy;
}

Then all I need to do is register the method with the following code:


ScriptedComponent::registerScriptMethod(scriptEngine, "TankAIDrivingBehavior", "void moveToGameObject(uint)", ScriptMethodPointer(moveToGameObjectDelegate));

Voila!


And thank you for explaining this, I know it stopped being about AngelScript some time ago but still you kept helping

You're welcome. :)

Your final implementation appears to be correct. I see nothing wrong with it.

The construction of the proxy and delegate can most likely be transformed into a template so you don't have to write this for every method your wish to register with AngelScript. I'm no wizard with templates and I can't say exactly how it would be done, but you can probably get an idea from the automatic wrapper templates for constructing proxies for AngelScript's generic calling convention (this code was provided by SiCrane on this forum).

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