Using a Preprocessor w/ Flash

For the past couple months, I have been using a preprocessor, The C Preprocessor, in my Flash development. I first heard about doing this from my friend Kenny Bunch, and then later when I was looking for info I came across this post on Mike Chambers blog.

Now that I have completed a couple projects using a preprocessor, I can honestly say that I would hate to do without it. The benefits are tremendous.

First, let me explain very briefly what a preprocessor does. A preprocessor simply makes a pass over your source files and modifies them based on directives that you inserted into your code. On my machine, I keep all my .as files under a src directory. When I run the preprocessor, it reads each of these files, does it's magic, and outputs new copies to a folder called build. My swf is compiled using the files in the build folder, which no longer contain any of the preprocessor instructions.

Here are some of the benefits of using a preprocessor in your development:

Conditional compiliation

With a preprocessor, you can incorporate special conditionals in your code. The code contained in these conditionals will only be included if a certain switch is set when you run the preprocessor. The most common use that I have had for this is to include a lot of debugging code that I don't want to have in the release version. Here is an example:

Actionscript
  1. public function Add(item):Void{
  2.  
  3.   #ifdef DEBUG
  4.     if( _list[item] )
  5.       trace("Item already in list: " + item);
  6.   #endif
  7.                
  8.     _list[item] = item;
  9. }

Now if I run the compiler with -D DEBUG, this extra code will be included. But, if I don't include -D DEBUG, then that code will be removed completely from my source file. This is great because during development I can include all these extra checks, but for the release version I can instantly drop them, along with any performance overhead they incurred.

Here's how the preprocessed code would look if you did include -D DEBUG:

Actionscript
  1. public function Add(item):Void{
  2.  
  3.  
  4.     if( _list[item] )
  5.       trace("Item already in list: " + item);
  6.  
  7.                
  8.     _list[item] = item;
  9. }

Here's how the preprocessed code would look if you did not include -D DEBUG:

Actionscript
  1. public function Add(item):Void{
  2.  
  3.  
  4.  
  5.  
  6.  
  7.                
  8.     _list[item] = item;
  9. }

A second use for conditional compilation that I have found is when you need to export multiple versions of your project. Recently, I worked on a project that was to be released in two phases. I used a WEEK2 flag to separate code that was specific to each version. Likewise, I used a CHEAT flag to enable/disable cheats.

Constants

Using constants, you can tell the preprocessor to replace every occurence of a certain variable with some other value. The difference between this and using the static keyword with a variable is that the preprocessor is "hard coding" that value where ever it occurs. With the static keyword, Flash still has to look up the value of the variable at run time each time it appears. This is a small performance gain that could be valuable in key portions of your code. Here is a generic example:

Actionscript
  1. #define HERO_DAMAGE 10
  2.  
  3. myHero.TakeDamage(HERO_DAMAGE);

After running the preprocessor, that code would translate to this:

Actionscript
  1. myHero.TakeDamage(10);

One benefit of constants that I really like is that you can use integers where you might have otherwise used strings, without giving up readability. For example:

Actionscript
  1. #define STATE_ATTACK 1
  2. #define STATE_CHASE 2
  3. #define STATE_DEAD 3
  4.  
  5. myMonster.SetState(STATE_DEAD);

Macros

I use macros the least, but in certain situations they come in really handy. Macros are kind of like constants, but whenever they occur they expand out their value, which can also include variables. If constants are a way to inline variables, then macros are like a way to inline functions. Here's an example, taken from this implementation of a heap:

Actionscript
  1. #define Left(x) ((x<<1)+1)
  2. #define Right(x) ((x<<1)+2)
  3.  
  4.  
  5. function Heapify(A,i,heap_size){
  6.  
  7.   var l = Left(i);
  8.   var r = Right(i);
  9.  
  10.   // rest of function not included
  11. }

After running the preprocessor, this could would be changed to this:

Actionscript
  1. function Heapify(A,i,heap_size){
  2.  
  3.   var l = ((i<<1)+1);
  4.   var r = ((i<<1)+2);
  5.  
  6.   // rest of function not included
  7. }

Now, you could have written Left and Right functions, but the extra function call would make them considerably slower than these macros, which are arguably just as clear. ***If you are interested in using macros, then I advise you to read up on the "pitfalls" of macro design, because it's easy to make some mistakes.

Incorporating the Preprocessor Into Your Build System

In order to use a preprocessor, you basically need some script that can recursively walk through your directory structure and run the preprocessor on each file. You could write a shell script, a python script, or a variety of other scripts. I do all my development with Emacs, so I wrote a function in Emacs that does this:

LISP
(defun as-preprocess(src_folder build_folder file_regexp cpp-args)
  •   ;; Make sure that src_folder exists.
  •   ;; If it doesn't, exit.
  •   (if (not (file-directory-p src_folder))
  •       (let ((msg (format "as-preprocess: Source folder: %s does not exist!!!" src_folder)))
  •         (print msg)
  •         (message msg))
  •     ;; Recursively travel the directory structure.
  •     (let ((files (directory-files src_folder t nil t)))
  •       (dolist (f files)
  •         (let ((basename (file-name-nondirectory f)))
  •         ;; If this is a directory, and is not "." or ".."
  •         (if (and (file-directory-p f)
  •                       (not (equal basename "."))
  •                       (not (equal basename "..")))
  •           (as-preprocess f (concat build_folder "/" basename) file_regexp cpp-args)
  •             ;; f is not a directory. Check if it matches the regexp.
  •             (when (string-match file_regexp basename)
  •               ;; Match
  •               ;; Create build folder, if it doesn't exist.
  •               (when (not (file-directory-p build_folder))
  •                 (print (format "Build folder does not exist. Creating %s" build-folder))
  •                 (make-directory build_folder t))
  •                 ;; Run the preprocessor
  •                 (let ((build-file (concat build_folder "/" basename)))
  •                   (progn
  •                     (print (format "Processing: %s" basename))
  •                     (print (shell-command-to-string (format "cpp %s -P %s %s" cpp-args f build-file))))))))))))
  • So that's my survey of using a preprocessor with Flash. It has become an indespensible addition to my build process. It might take a little effort to incorporate into your build system, but the gains are well worth it.

    2 Responses to “Using a Preprocessor w/ Flash”

    1. daniel Says:

      Informative post, thanks!

      I’m wondering if the preprocessor in your post has anything to do with the mystical post-processor functionality which is supposed to be new in Flash 8?

      Here’s a Flash Magazine article which briefly mentions post-processors:
      http://www.flashmagazine.com/1133.htm

    2. austin Says:

      Thanks for the link! I didn’t know that feature was in there. It’s cool that Macromedia is adding in hooks like that. I’m sure that people will be coming out with all sorts of cool plug-ins that use that.

      Of course, the preprocessor is different than that because it runs before the code is compiled by Flash. It’s too bad that they didn’t include a “pre” hook too. That would’ve made it a lot easier for people to start using a preprocessor.