I'm writing an ANE and I'd like to know if anyone has been able to pass a C++ class member function pointer as a callable function from AIR? I have tried this so far with a little bit of C++11 trickery and it's crashing. I've also statically linked the libstdc++ into my library, according to the documentation, in order to ensure that these features I use work correctly. I have code like so:
FakeWorld* world = new FakeWorld(); *numFunctions = 1; memberFunctions = (FRENamedFunction*) malloc(sizeof(FRENamedFunction) * (*numFunctions)); ANEMemberFunction mCreateFakeBody = std::tr1::bind(&FakeWorld::createFakeBody, world, std::tr1::placeholders::_1, std::tr1::placeholders::_2, std::tr1::placeholders::_3, std::tr1::placeholders::_4); ANEFunction* createFakeBody = mCreateFakeBody.target<ANEFunction>(); memberFunctions.name = (const uint8_t*) "createFakeBody"; memberFunctions.functionData = NULL; memberFunctions.function = createFakeBody; FRESetContextNativeData(ctx, (void*)world);
I just realized I'm using C here for allocating the member functions array.. silly me, but I don't think this is the cause of my issue. I refuse to believe that Adobe has built to the Native Extensions portion of the runtime in such a way that I have to cram every single function I want to create (natively) into a global, C formatted namespace (Especially since the documentation says that C is only required for the extenion and context initializing function interfacing and the rest of the code can be done in C++ or objective-C). So please let me know if and how this is possible and I thank you so much in advance for your time!P.
P.S. Currently when I run this code, I can do the object initialization just fine. As soon as I invoke the "createFakeBody" method on the native side, the runtime dies and simply says:
Problem signature: Problem Event Name: BEX Application Name: adl.exe Application Version: 18.104.22.16880 Application Timestamp: 4eb7612e Fault Module Name: StackHash_0a9e Fault Module Version: 0.0.0.0 Fault Module Timestamp: 00000000 Exception Offset: 00000000 Exception Code: c0000005 Exception Data: 00000008 OS Version: 6.1.7601.2.1.0.256.48 Locale ID: 1033 Additional Information 1: 0a9e Additional Information 2: 0a9e372d3b4ad19135b953a78882e789 Additional Information 3: 0a9e Additional Information 4: 0a9e372d3b4ad19135b953a78882e789 Read our privacy statement online: http://go.microsoft.com/fwlink/?linkid=104288&clcid=0x0409 If the online privacy statement is not available, please read our privacy statement offline: C:\Windows\system32\en-US\erofflps.txt
Thanks again for your assitance.
It's been a little while since i've dealt with C++ and not up to speed on tr1 or C++0x, so forgive me if i'm not helping.
The the examples of
std::tr1::bind that i'm seeing out there seem to be either dereferencing the bound 'this' pointer when creating the bound method, i.e. in your case *world instead of world, or using
std::tr1::ref(*world), therefore i believe that bind expects the bound parameters to be passed by reference.
Given that the result of std::tr1::bind is callable (basing that on http://stackoverflow.com/questions/3678884/virtual-member-functions-an d-stdtr1function-how-does-this-work) could you simplify to the following:
memberFunctions.name = (const uint8_t*) "createFakeBody";
memberFunctions.functionData = NULL;
memberFunctions.function = std::tr1::bind(&FakeWorld::createFakeBody, *world, std::tr1::placeholders::_1, std::tr1::placeholders::_2, std::tr1::placeholders::_3, std::tr1::placeholders::_4);
Or for an even simpler starting point, creating a test member function 'helloWorld' in FakeWorld that takes no arguments and using:
memberFunctions.name = (const uint8_t*) "helloWorld";
memberFunctions.functionData = NULL;
memberFunctions.function = std::tr1::bind(&FakeWorld::helloWorld, *world);
Hope this helps.
Thanks for your help. I've actually investigated this alot further since originally posting and the issue is actually that the result of bind:: is not a standard type and while it might be a callable object in C++ (probably by creating a new type and implementing the () operator), it's not a standard pointer but more something like a struct. This is why I attempted using the .target<signature> method which, according to erroneous information I read, said this would produce a standard C type function signature that could be called like a static method or something like that. This however is not the case.
What I've ended up doing is going the brute-force way and just writing C style static callbacks for each method in my native classes, then using the FREContext variable to fetch the pointer to the native class associated with the context. Then from there I can static_cast it back to it's original type and perform whatever ops on it. I wanted to avoid this but it seems to be impossible given the provided interface for AIR and the limited backward compatability in C++ for this. You could probably get this working using some type of Functor and a LOT of template madness. I was going to go down that road but honestly, I just want to get this done and have it work right cross platform. I'm porting box2D over and I need it urgently for a game I have in development so right now doing some crazy template stuff using experimental C++11 features just isn't possible.
Thanks again for posting and try to help me out. I will post a follow up here with links to my source when I'm done so it might help others.
So basically just a follow up, I did end up generating all C-style callbacks... I am now filling in the implementation specific details for each class but I've started pushing code to github, in case anyone is interested in following this or even perhaps contributing???!?!
Europe, Middle East and Africa