Advertisement

AngelScript and Fastdelegates

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

Hi,

I have a scenario where AngelScript is used in a DLL library (AngelScript itself is built and linked to as a static library) and I would like to register certain object methods in the application using my DLL with AngelScript. I would, however, not want to expose the application to the AngelScript API but rather expose a very simple API through my DLL, to prevent the app from having to link against AngelScript directly. The way I would want to register the methods is through fastdelegates (as that is what I am using for most other things).

I was wondering if someone has managed to register a fastdelegate directly with AngelScript. For example, can I somehow pass a fastdelegate to RegisterObjectMethod() instead of having to use the normal asMETHOD macros?

If that cannot be easily done, is there a way to register the same C++ method with two different AngelScript methods, and then somehow be able to figure out (on the C++ side) which AngelScript method was called.

Eg.

Can I register "void foo(int i)" and "void foo2(int i)" in AngelScript with "void foo(int i, int ID)" on the C++ side and then have the ID parameter automatically filled in with 0 when foo() is called and 1 when foo2() is called (or some other way to determine which method was called in AS)? If this was possible I could then forward the call to the correct fastdelegate myself.

Cheers!

What does your fastdelegate implementation look like?

I assume it is a class that holds a pointer to the real object and and has a method for invoking the delegated method on the real object (similar to std::function).

You should be able to register this as if it was a method by doing the following:

engine->RegisterObjectMethod("obj", "void func()", asMETHOD(FastDelegate, operator()), asCALL_THISCALL_OBJLAST, fastdelegateObj);

AngelScript will then call the operator() on the fastdelegateObj, which will in turn forward the call to the actual method on the real object received as parameter to the operator() method.

If you wish to use a single C++ implementation registered multiple times, and at runtime figure out what each call from the script is expected to do, then you'll need to use the asCALL_GENERIC calling convention. With this you'll get an asIScriptGeneric instance from the script engine that you can use to query the arguments and the function that the script expected to call.

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

Advertisement

Since there have been quite a few views but no answers, let me rephrase the question a bit.

So I have an app that links to a dll. The dll contains an app framework that uses a number of 3rd party middleware. All of these pieces of middleware are linked as static libraries to the dll, AngelScript being among those. AS is used quite extensively inside the dll but every now and then I would like to expose a class from the app (ie. exe) using the dll to AS. My needs are fairly basic. I need to register a few classes (not ever created or destroyed in AS, always just passed from C++ to an AS global variable) to AS, have the AS code call some of the methods with basic parameter types (bool, int, float, string) and also be able to call AS functions from the exe code.

Since the exe has no idea about AngelScript (only the dll links to it) I cannot use the normal AS API. I would not want to expose the raw AS API to the exe since it only needs to do a small number of things, and it would require me to rethink how I link to AS in the first place.

Calling the AS functions from the exe code is not a problem, since it is easy to write a wrapper API that forwards the calls and registrations to the AS API (the registration is just strings after all). However, registering C++ methods gets trickier since the DLL code would need to know about the exe code (which obviously it does not).

So, is there a way to register methods or even just global functions (that can act as proxies to methods) from the exe code as AS-callable functions using the AS API only inside the dll? Eg. can I somehow pass the output of the macros such as asMETHOD or asFUNCTION as a parameter from the exe code to the dll code, to then be registered by the dll normally?

Ah too slow :)

That sounds pretty much exactly like what I need. I'll check it out and let you know how it turns out (for the record, or for other people wanting to do the same thing).

I am using Don Clugston's fastdelegates: http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible

Thank you!

Actually, I don't think the asCALL_THISCALL_OBJLAST calling convention is going to work in this case.

engine->RegisterObjectMethod("obj", "void func()", asMETHOD(FastDelegate, operator()), asCALL_THISCALL_OBJLAST, fastdelegateObj);

The problem with doing the above with the fastdelegates I am using is that I package together a class instance and a pointer to a member of that instance into a fast delegate. It can then be called by supplying only the "actual" function parameters and the fastdelegate takes care of the this pointer. Since the registering above is supposed to register a method on a type rather than a method on an object instance I cannot pass that kind of delegate to RegisterObjectMethod().

Or am I thinking about this the wrong way?

I might be able to register a global proxy function that accepts the object pointer as an additional pointer, and that then calls the actual method on the class...

OK. Then perhaps you should really be registering the fastdelegate as if it was a global function, rather than an object method.

If so, you would register it with the following:

engine->RegisterGlobalFunction("void func()", asMETHOD(FastDelegate, operator()), asCALL_THISCALL_ASGLOBAL, fastdelegateObj);

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

Advertisement

Hmm, I don't see how registering it as a global function helps. It is a method after all.

I was able to get it working. It's not quite as flexible as I want and I have to jump through some hoops, but now I can register methods on a class in the exe code to be called from AS without the code ever seeing the AS API, and without the AS runtime ever seeing the exe class code.

So if I have a class method such as void AIBehavior::moveToGameObject(const String& gameObjectName); on the exe side that I want to be able to call from AS the first thing I do is wrap it in a proxy function like this (this would be one of the hoops mentioned above):


void moveToGameObjectProxy(const String& gameObjectName, void* self)
{
    static_cast<AIBehavior*>(self)->moveToGameObject(gameObjectName);
}

On the dll side I have a few typedefed delegates for registering methods with different signatures. The one above accepts one string and returns void so I will be using this one:


typedef FastDelegate2<const String&, void*, void> ScriptComponentMethodString;

I would like to be able to register any method with any signature, but I don't think that is gonna work across the exe-dll border (this would be the not-so-flexible part mentioned above). Let me know if you figure out how to register any method without the dll code seeing the exe code.

I then create a delegate object and register the proxy function like this:


// On the exe side:
static ScriptComponentMethodString moveToGameObject = &moveToGameObjectProxy;

// ... the delegate is passed from the exe to the dll along with the strings below ...

// On the dll side:
scriptEngine->RegisterObjectMethod("AIBehavior", "void moveToGameObject(const string &in)", 
    asMETHOD(ScriptComponentMethodString, operator()), asCALL_THISCALL_OBJLAST, &moveToGameObject);

The delegate object is static so that the code can be called for multiple script engines if needed (client and server side in my case) and the delegate wont go out of scope.

I can now pass an instance of AIBehavior through a void-pointer from the exe to the dll to be picked up by a script and have that script call moveToGameObject().

The only thing AngelScript needs to know is the address of the ScriptComponentMethodString::operator() and the address of the moveToGameObject, and then the declaration of the function signature as a string. You ought to be able to pass those to the dll without the dll having to implement them on its own.

You can have the dll construct the asSFuncPtr manually from the method pointer instead of using the macro asMETHOD. That way the dll doesn't have to declare the FastDelegate2 templates thus you gain a bit of flexibility.

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

What would the argument type of the method pointer be in that case? In this particular case it is something like "void (ScriptComponentMethodString::*)()" but how do I pass _any_ method pointer type across the exe-dll border? Usually I would use templates for this kind of thing but that is not an option because of the exe/dll split. AFAIK using a void pointer for method pointers is not a good idea as they don't always fit inside a void pointer.

This topic is closed to new replies.

Advertisement