Using printf within a SWC

A SWF built with gcc/g++ using the -emit-swf option has a default Console that prints stdout into a TextField on the screen (and to the Flash log file). However when you build a C/C++ library into a SWC using the -emit-swc flag there is no default Console. This means that if code in your library calls printf() no output will be shown. This post demonstrates how to use a custom Console to show the output of printf() calls from within a SWC.

This post assumes you already know how to compile a SWC in FlasCC. If you do not know how see the samples/05_SWC sample application that ships with the FlasCC SDK.

Let’s start with a very simple C library that will be compiled into a SWC:

#include <stdio.h>
#include "AS3/AS3.h"
 
void doSomething() __attribute__((used,
    annotate("as3sig:public function doSomething():void"),
    annotate("as3package:MyLibrary")));
 
void doSomething()
{
    printf("SUCCESS\n");
}
 
int main()
{
    // The SWC still needs a main() function.  
    // See the code comments in samples/05_SWC for more details.
    AS3_GoAsync();
}

This library exposes a single function called doSomething() that simply uses printf() to write the string “SUCCESS”. First let’s compile this library into a SWC using the following command:

/path/to/flascc/usr/bin/gcc MyLibrary.c -emit-swc=MyLibrary -o MyLibrary.swc

Now this SWC is ready to be dropped into an ActionScript project in Flash Builder. If you were to then call that function from the ActionScript application you would notice that no output was written to the screen or traced out. In order to see this output we need to tell the library which object is available to receive the data. This is done by setting the CModule.vfs.console property to an Object that implements the ISpecialFile interface.

Here is a sample ActionScript application which itself implements ISpecialFile:

package {
 
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.events.Event;
 
    import MyLibrary.CModule;
    import MyLibrary.vfs.ISpecialFile;
 
    public class SampleApplication extends Sprite implements ISpecialFile {
 
        private var tf:TextField;
 
        public function SampleApplication() {
            addEventListener(Event.ADDED_TO_STAGE, initCode);
        }
 
        private function initCode(e:Event):void {
            tf = new TextField();
            addChild(tf);
            tf.appendText("SWC Output:\n");
 
            // set the console before starting
            MyLibrary.CModule.vfs.console = this;
 
            // start the FlasCC library
            MyLibrary.CModule.startAsync(this);
 
            // call a C++ function that calls printf
            MyLibrary.doSomething();
        }
 
        /**
         * The PlayerKernel implementation will use this function to handle
         * C IO write requests to the file "/dev/tty" (e.g. output from
         * printf will pass through this function). See the ISpecialFile
         * documentation for more information about the arguments and return value.
         */
        public function write(fd:int, bufPtr:int, nbyte:int, errnoPtr:int):int
        {
            var str:String = CModule.readString(bufPtr, nbyte);
            tf.appendText(str);
            trace(str);
            return nbyte;
        }
 
        /** See ISpecialFile */
        public function read(fd:int, bufPtr:int, nbyte:int, errnoPtr:int):int { return 0; }
        public function fcntl(fd:int, com:int, data:int, errnoPtr:int):int { return 0; }
        public function ioctl(fd:int, com:int, data:int, errnoPtr:int):int { return 0; }
    }
}

The other ISpecialFile methods are not fully implemented and left as an exercise to the reader. See the documentation of ISpecialFile and the FlasCC reference guide for more information on working with a custom Console.