-->

Following from my last article on setting up an Alchemy development environment in Flex Builder 3, I wanted to illustrate briefly how objects can be passed to C++ methods using Alchemy (and similarly how the ActionScript properties can be extracted) and how we can return objects to the calling AS3 code.

This article has been prompted since I found very little useful documentation on the web, so hopefully this’ll be useful for other beginners! Other than the C++ API for Alchemy provided by Adobe (which is essential), I’ve found the following links very useful in providing concrete examples of getting started in Alchemy:

These provided the essential lines of code necessary to understand the API, and be able to pass objects to C/C++ and similarly return objects to ActionScript.

The following is some C code to illustrate this. Essentially a flash.geom.Vector3D object is passed to a function, foo, which then returns a copy of the object to the calling function.

#include "AS3.h" AS3_Val foo(void* self, AS3_Val args) {   // declare local variables   double x;   double y;   double z;     // ******* Passing objects as parameters to C++ *******     // Declare AS3 variable   AS3_Val as3Vector;     // Extract variables from arguments array (in this case a flash.geom.Vector3D)   AS3_ArrayValue(args, "AS3ValType", &as3Vector);   // Extract properties from object and store in local variables   AS3_ObjectValue(as3Vector, "x:DoubleType, y:DoubleType, z:DoubleType", &x, &y, &z);   // ******* Returning objects to AS3 *******   // Obtain a class descriptor for the AS3 Vector3D class   AS3_Val vector3DClass = AS3_NSGet(AS3_String("flash.geom"), AS3_String("Vector3D"));   AS3_Val params = AS3_Array("");     // Construct a new AS3 Vector3D object with empty parameters   AS3_Val result = AS3_New(vector3DClass, params);     // Set the x, y and z properties of the AS3 Vector3D object, casting as appropriate   AS3_Set(result, AS3_String("x"), AS3_Number(x));   AS3_Set(result, AS3_String("y"), AS3_Number(y));   AS3_Set(result, AS3_String("z"), AS3_Number(z));   // Release what's no longer needed   AS3_Release(params);   AS3_Release(vector3DClass);     // return the AS3 flash.geom.Vector3D object   return result; } /** * Main entry point for Alchemy compiler. Declares all functions available * through the Alchemy bridge. */ int main() {   // Declare all methods exposed to AS3 typed as Function instances   AS3_Val fooMethod = AS3_Function(NULL, foo);   // Construct an object that contains references to all the functions   AS3_Val result = AS3_Object("foo:AS3ValType", fooMethod);   // Release what's no longer needed   AS3_Release(fooMethod);   // Notify the bridge of what has been created -- THIS DOES NOT RETURN!   AS3_LibInit(result);   // Should never get here!   return 0; }

Let’s take a look at this bit by bit. Starting with the function foo, all functions visible to ActionScript have to be declared in the same way:

AS3_Val foo(void* self, AS3_Val args) {

The return value is always an AS3_Val type and a function always receives a pointer along with an AS3_Val type argument. AS3_Val can be thought of as the Object type in AS3 - all concrete types inherit from this so utilities are needed to extract useful information from them.

First of all: extracting objects from the args parameter. Whatever, and however many, arguments are passed we always use the method AS3_ArrayValue to extract the real arguments: arguments are always passed in the form of an Array. In the example we do the following:

  // Declare AS3 variable   AS3_Val as3Vector;     // Extract variables from arguments array (in this case a flash.geom.Vector3D)   AS3_ArrayValue(args, "AS3ValType", &as3Vector);

Here we declare an AS3_Val variable (implying that we are expecting an AS3 Object). If the types passed are primitive types (or String types) we could do the following:

int arg0 = 0; char* arg1 = NULL; double arg2 = 0.0; AS3_ArrayValue(arr, "IntType, StrType, DoubleType", &arg0, &arg1, &arg2);

Here the primitive AS3 types are converted to native C++ primitives.

Coming back to our example, we now have an AS3 Object (stored as an AS3_Val type) and we’d like to extract data from it. This is done by making an AS3_ObjectValue call.

  // Extract properties from object and store in local variables   AS3_ObjectValue(as3Vector, "x:DoubleType, y:DoubleType, z:DoubleType", &x, &y, &z);

Here we pass the extracted Object, declare the identifiers and types of properties and a list of local variables to store the value in. For the Vector3D we want to obtain the x, y and z properties which are converted to local DoubleType values.

To return an ActionScript object, we need to perform a lookup for the class name with the relevant namespace. From this we can get a class descriptor which can then be instantiated.

  // Obtain a class descriptor for the AS3 Vector3D class   AS3_Val vector3DClass = AS3_NSGet(AS3_String("flash.geom"), AS3_String("Vector3D"));

Here, for example, we obtain an AS3_Val representing the class flash.geom.Vector3D, using the AS3_NSGet function. We can then create an object from this.

  AS3_Val params = AS3_Array("");     // Construct a new AS3 Vector3D object with empty parameters   AS3_Val result = AS3_New(vector3DClass, params);

This object is create with no parameters using the AS3_New function. Again, the result is stored in the base AS3_Val type. Having created a new object we can then modify its properties.

  // Set the x, y and z properties of the AS3 Vector3D object, casting as appropriate   AS3_Set(result, AS3_String("x"), AS3_Number(x));   AS3_Set(result, AS3_String("y"), AS3_Number(y));   AS3_Set(result, AS3_String("z"), AS3_Number(z));

The AS3_Set function takes an AS3 Object, a property identifier and a value, correctly cast to the required AS3 value type, in this case an AS3_Number type. Note that I’ve tried to pass these values in the params Array but have never been successful - if anyone has any tips on doing this I’d be happy to hear from you.

So, now we have an AS3 object created and its properties set. Now we need to do a bit of memory management before returning the object. This is done using the AS3_Release function.

  // Release what's no longer needed   AS3_Release(params);   AS3_Release(vector3DClass);

Finally, we return the created object:

  // return the AS3 flash.geom.Vector3D object   return result;

To export the function foo to ActionScript, the following main function is called - the Alchemy compiler always looks for this function, so its here that we always declare our interface. For the above example the following is necessary:

int main() {   // Declare all methods exposed to AS3 typed as Function instances   AS3_Val fooMethod = AS3_Function(NULL, foo);   // Construct an object that contains references to all the functions   AS3_Val result = AS3_Object("foo:AS3ValType", fooMethod);   // Release what's no longer needed   AS3_Release(fooMethod);   // Notify the bridge of what has been created -- THIS DOES NOT RETURN!   AS3_LibInit(result);   // Should never get here!   return 0; }

Without going into too many details, essentially we create Function objects containing the native C/C++ methods. These are then given an AS3 interface using the AS3_Object function and then passed to the AS3_LibInit function which provides the entry point from ActionScript.

The following ActionScript class shows how we call the Alchemy compiled functions, passing a Vector3D object and obtaining another one in return.

package {   import cmodule.vector.CLibInit;     import flash.display.Sprite;   import flash.geom.Vector3D;   public class Test extends Sprite {     public function Test() {       // Create the Alchemy bridge to C++ methods       var loader:CLibInit = new CLibInit;       var alchemyTest:Object = loader.init();       var vector:Vector3D = new Vector3D(0.123, 0.456, 0.789);       var returnVector:Vector3D = alchemyTest.foo(vector);             trace("Return vector = (" + returnVector.x + ", " + returnVector.y + ", " + returnVector.z + ")");           }   } }

Our connection to the C/C++ code is performed by creating a new CLibInit function and calling init.

      // Create the Alchemy bridge to C++ methods       var loader:CLibInit = new CLibInit;       var alchemyTest:Object = loader.init();

This creates a basic ActionScript Object. The functions cannot be seen at the time of compilation (at least not in Flex Builder) but are obtained at run time. The resulting function call on foo is shown as follows:

      var vector:Vector3D = new Vector3D(0.123, 0.456, 0.789);       var returnVector:Vector3D = alchemyTest.foo(vector);

As required, we pass a Vector3D and get a new Vector3D object in return.

And that’s it! If you’d like to have a look at the source and the project set-up together (using the same automake and ant files as discussed in the previous article) then you can find them here.

This is a very quick introduction to passing and returning objects using Alchemy. The API provides many more possibilities but this articles aims to illustrate some of the basic, but essential, functions available - for other beginners I hope this is useful! In my next article I’ll look at some of the capabilities of Alchemy.

2 Responses to “Passing/Returning objects to/from C++ using Alchemy”

Pradeek said...

Why is the void* self parameter necessary? What does it do? Is it necessary only when arguments are passed? Thanks.

Tim Knip said...

void *self apparently can be use to “keep state”. Whatever that means :-) If one passes something other then NULL to AS3_Val fooMethod = AS3_Function(NULL, foo); you can grab that from *self. My guess would be it can be used to “keep” values in between function calls.

-->