The forums have permanently moved to forum.kirupa.com. This forum will be kept around in read-only mode for archival purposes. To learn how to continue using your existing account on the new forums, check out this thread.


Page 1 of 2 12 LastLast
Results 1 to 15 of 25

Thread: C Preprocessor Fun

  1. #1
    164
    posts
    Registered User

    C Preprocessor Fun

    In case you want some nifty 'hard' constants, preprocessor-controlled expansion of debug/instrumented/release/code, templates, assertions, to add language elements to AS3, handy-dandy macros of all sorts, and to obfuscate release build output for decompilers, AND you want to develop in a 'friendly' environment that will show you the ORIGINAL source code, instead of the CPP output, read on.

    BlankProject.zip
    The Makefile script is fairly straight-forward, as makefiles go. It has two lower-level scripts (debug.mak and release.mak) that can be invoked with 'make -f debug.mak', or that can be invoked from the main Makefile (make debug).

    There's one build rule to make .as3 files into .as files, tidy them up a bit and place them in either the as or asd folder.

    There's one rule to invoke mxmlc if any of the .as files were updated.

    There is an extra step to translate the error output so Error and Warning callouts reference the .as3 files in the 'src' folder instead of the 'as' or 'as3' folder, so your friendly IDE can pull up the right file when you have an error.

    There is a collection of batch files to invoke make in various ways.

    There's a 'pp.h' in the include directory with a few handy preprocessor toys in it. The assert macro should invoke 'enterDebugger()', but I can't seem to find that function in the (free) Flex SDK. What it should be short of that is a call to some readily accessible exported function to stop and wait for user response, or induce a runtime error that brings the debugger up without causing other compile-time or runtime problems.

    The project layout
    ./
    ./include
    ./src
    ./as
    ./asd
    ./bin
    • ./include Contains the preprocessor headers. Everything in the ./include folder becomes a dependency for every as file.
    • ./as and ./asd contain the preprocessed output for release and debug, respectively.
    • ./src Contains the source code. Files in this path end with '.as3' instead of the conventional '.as'. This is for two reasons. Foremost, it's simpler to tell make to translate files that have different extensions. Second, it bugs me that AS1, AS2, AS3 are all completely different languages, yet they all have the same .as extension. Well, not in this project, buddy. Anyway, every 'as3' file found there will be preprocessed into as or asd.
    • ./bin Contains the .swf output.
    • The project root (./) contains some handy batches and such.
    • The ./as, ./asd and ./bin folders are all treated as scratch folders. The Makefile will nuke 'em and re-create empty ones whenever you 'clean'. You should clean once when you initially start up a project.
    The attached zip file is a 'Hello World' app that gets preprocessed and made.

    You'll note (once you've made it) that after it's been build, it builds again VERY fast until mxmlc has to get involved. It very quickly works through the dependencies and doesn't invoke mxmlc unless it has to.

    Requirements

    First, to play along with the example, you'll need a few minor things... A passing familiarity with command line tools (especially Unix/Linux ones) is a big plus for understanding this.

    GNU Make
    This does the old C 'make' script processor. I may be showing my age, but I don't know 'Ant', and I do know 'Make'. Anyway, it's the concept that counts.

    GNU CPP
    This is the GNU C preprocessor. It inserts text from macros into source code where you drop references to those macros, optionally with parameters.

    GNU sed
    The Stream Editor. This tool is so absolutely handy, you won't be able to live without it... if you ever figure out how to use it. This is needed to do some text translations on the preprocessor and MXMLC output.

    Optional: GNU indent
    A 'Pretty Print' tool that will make CPP output more readable, when you're debugging complex preprocessor output. It's in the Makefile, but commented out, because running it will make the line numbers mismatch on the error call-outs, and you're not debugging big preprocessor macro-spew very often unless you're actively developing code generating template macros.

    GNU make, sed and indent are already in most major Linux distros, and probably already in later Linux-based Mac machines. The Makefile makes some Windows system calls, so that'll need to change for Linux/Mac.

    Under Windows, you can get the MinGW msysDTK-1.0.1.exe for make, then unzip gcc-core-3.4.2-20040916-1.tar.gz into tht same path for gcc, then grab the balance of the missing tools from 'GnuWin32' (and don't neglect their 'dependencies'). Basically we assume they're installed and in the path somehow.
    http://www.mingw.org/
    http://gnuwin32.sourceforge.net/packages/

    Under Windows, don't use the 'sed' from the old 'unxutils' Windows project. It's bent.

    Cygwin has all of these tools, but you're on your own for any Windows/Linux path problems that I got when I tried to run Flex under it.

    Optional: jEdit
    This is the 'friendly' editor that I use. It's JAVA based, like Eclipse, but the current version comes with an Actionscript (AS2) syntax filter, and it's very lightweight (compared to Eclipse, anyway).
    http://www.jedit.org/

    Plugins you'll need: Console, ErrorList
    Plugins you'll want: BufferTabs, Project Viewer
    Things you'll need to do:

    1. Add an 'mxmlc' error pattern to the Console plugin in Plugins->Plugin Options->Console->Error Patterns, click the '+' at the bottom of the Error patterns list.
    • Name: mxmlc
    • Error Regexp: (.*)[(](\d+)[)]: col: (\d+) Error: (.*)$
    • Warning Regexp: (.*)[(](\d+)[)]: col: (\d+) Warning: (.*)
    • Filename: $1
    • Line number: $2
    • Error message: $4
    2. Go into Utilities->Global Options->Editing
    • Set up your tab preferences
    • Set up 'actionscript' settings to recognize '.as3' (File name glob: *.{as,as2,as3})
    3. You'll probably also want to go to Utilities->Global Options->Shortcuts and add these keyboard shortcuts, if you're a Windows user.
    • Close: Ctrl-F4
    • Go to Next Buffer: Ctrl-Tab
    • Go to Previous Buffer: Ctrl-Shift-Tab
    Attached Files Attached Files
    Last edited by pingnak; April 5th, 2007 at 08:58 PM. Reason: Left a step out

  2. #2
    good read, but i dunno if i'll have a big need for it

  3. #3
    164
    posts
    Registered User

    What can be done

    One big 'need' in a major project is the 'assert()' macros. These do 'sanity checks' on parameters and game state that are compiled out at runtime. If you're not familiar with 'assert', do some web searches for articles on its use. Actionscript does some things (like runtime type checking) for you, but it won't diagnose emergent behavior on larger systems, as you will need to.

    Also, the use of other kinds of 'trace' and 'debug only' functions and data besides the one natively supported by the compiler.

    Conditional compilation is another big plus. You can put all sorts of level-editing and diagnostic tools directly into the game code, and then make the game with or without them by changing a single preprocessor flag.

    Then there's constructing enumerations and state machines. A lot of very repetitive code to implement in order to have a typed list of const ints to play with, and then make sure it's all debuggable.

    This is the utterly cheapest (computationally speaking) enumeration possible. Under Actionscript, any other way, you may as well do a list of strings if you're going to compare 'constants', because ultimately you're doing string comparisons and binrary searches to 'find' references to 'const' data in classes. If you want to switch off an interger instead, and make sure there is no time-consuming string comparisons, then this will install constants in the code while still looking at text when working on it.

    Code:
    #define MyEnum_first    1
    #define MyEnum_second    2
    #define MyEnum_third    3
    #define MyEnum_fourth    4
    ...
    var test: int = MyEnum_third; 
    switch( test )
    {
        case MyEnum_first:
        case MyEnum_second:
        case MyEnum_third:
        case MyEnum_fourth:
    }
    Which becomes...
    Code:
    
    
    
    ...
    var test: int = 3; 
    switch( test )
    {
        case 1:
        case 2:
         case 3:
         case 4:
     }
    This has some innate advantages if you're switching behavior based on data, and doing lots of it. Also, if someone decompiles your binary, they get a lists and switches containing numbers, instead of the actual labels, which would be very readable. You can make a 'type safe' enumeration, but the constants end up being wrapped in classes... and then you have all of that interpretive overhead to deal with.

    Another useful construct that I often use is when I have lots of related data to define that would have to appear scattered all over the place to be 'relevant'. Then there's binding messaging and data file formats to other languages. I can write one body of code that expands to the right language on client and server. This means less to 'go wrong' with the protocol getting out of whack.

    Code:
    record_begin(XYZ)
        record_var(    z,    float32,    0.0f )
        record_var(    y,    float32,    0.0f )
        record_var(    x,    float32,    0.0f )
    record_end(XYZ)
    
    container_set(starmap,XYZ)
    
    record_begin(Space)
        record_string( name, "unnamed" )
        record_obj( stars, starmap )
    record_end(Space)
    By defining and redefining 'record_' macros, and including the macros over and over, I can generate classes, data declarations, data validation, dispatch tables, whole serializing protocols. Maybe I want to define what the code does on Client AND Server, and these are different languages. I can define the record_ macros, with any language elements I like, to generate AS, C, Java, Python, whatever is necessary. A whole protocol can emerge from a common include file, or set of include files. Data declarations, compact serial code, database, XML and common application glue all in one shot. And (a much bigger problem in languages like C or C++) the constructor/destructor code can be generated instead of hand-written. A lot fewer copy/paste errors, and errors induced by interruptions that cause you to forget to make identical changes in a dozen places in the code base.

    Perhaps I'd rather manufacture some 'untyped' Object data container classes from templates and factories than hand implement them each 'one to a file' in ActionScript? A lot of primitive little things that can be operated on without worrying about getting a million details right, AND type safety can be added, and made 'debug-only'.

    It adds virtually no time at all to a large compile, and subtracts a lot of hours of head scratching and staring at binary dumps on large projects with a lot of serialized data being moved around and exchanged.

    I've already done all of this in C/C++ in various projects. It's a technique that works very nicely once you have the macro behavior and object model nailed down. It also makes very small and fast code, because it does the same things as you'd hand-code, rather than using a 'general purpose' code library to apply rules and make 'whatever' from 'whatever'.

  4. #4
    164
    posts
    Registered User

    Ha!

    OK, a slightly more useful Makefile variant with more documentation inside and a slightly better example.

    The first thing I ran into with the original was that it only supported one directory. If you have a couple of named packages to import, this could be a problem. It was for me.

    This new version supports adding a list of directories to a LIBDIRS variable, so the script can find AS3 files in other places in the main GAMEDIR folder. Unless your project is laid out really silly, this should work with only a minor tweak or two to the Makefile to let it know where to look for your favorite imports, which won't change much from project to project once it's set up.

    It still reports the ORIGINAL .as3 file for errors, and gets the path right, too.

    The example file illustrates some example trace/assertions, and if you look in the as or asd files, you'll see the release or debug versions of the output .as files.

    Also assert works now. It basically forces a runtime error that the debug player spots and causes it to halt fdb on the appropriate line.

    When I get my finite state machine class template pulled together, I'll add it to the example, along with a few more goodies.
    Attached Files Attached Files

  5. #5
    I have a suggestion

  6. #6
    make a site with all those tutes dude

  7. #7
    164
    posts
    Registered User
    So, you want me to go stir up trouble some place else?

    Anyway, yeah I set up the sf.net project submission (already had an old project) and will have to wait for it to be 'approved'.

    There is a bug on the line numbering I'll have to work out. GNU CPP doesn't leave empty white space where you #ifdef out a bunch of lines, which buggers all the lines call-outs later in the file. MSVC cl /e does leave the lines, but it also leaves some extra random white space on each line to eat.

    I'll probably add support to invoke other CPP variants, as some of them will doubtless be better behaved and/or have features more conducive to my evil intents. I seem to recall the old Watcom C compiler would actually 'cook' its preprocessed output. If you did this...

    #define THREE 3
    #define FOUR 4
    #define TWELVE (THREE * FOUR)

    TWELVE would yield (12), not (3 * 4) as other CPP variants do.

    If the number of parameters that might need to be customized climbs, I might add a -include statement to the Makefile to suck in an external settings file. When there's more than one person working on a project, it really sucks when everybody has their own private version of the build scripts checked out to maintain local settings, mostly when a change is made and everybody suddenly has to integrate that.

    I seem to recall Microsoft's preprocessor (cl /E) behaved differently, and could make a skew for that, too.

    Failing all of that, something like grabbing the source code for one of the various open source CPP variants and beating it with a big hammer for an afternoon would sort the issue out. Being able to go...

    #if CONDITION
    // A hundred lines of code
    #else // CONDITION
    // A couple hundred lines of code
    #endif // CONDITION

    Is helpful, especially when testing/profiling and seeing if your new code is 'better' than some other code. Flip the condition, and all the code scattered all over the project magically gets built or not. Then it's easy to go back through and track down all of the #if/#else/#endif conditionals and permanently rip the 'bad code' out.
    Last edited by pingnak; April 10th, 2007 at 12:53 PM.

  8. #8
    164
    posts
    Registered User
    OK, changed some stuff around.

    After encountering some problems (conditional code was getting collapsed and causing mismatches further along in the file) and examining some other preprocessor outputs (MSVC/Watcom cl /e), I determined that the simplest and most robust thing to do would be to go ahead and let CPP make its #line directives, and then write a little tool to interpret that spew and make the AS output match the AS3 input every time, line for line.

    Fortunately for me, this wasn't pure madness, and it worked almost the first time.

    So now the dependencies include a C compiler. Not a big change, since GNU cpp won't work without gcc installed, anyway, and you may as well have a working C compiler. They're handy.

    The mxmlc error output is still redirected to an 'errors' file and translated in the Makefile by a couple of sed regular expression search/replaces.

    Comments are correctly preserved in the CPP output.

    Added a README.txt file to the root of the project with the instructions, including gvim setup help. Also in that README is a better place to get the MinGW tools, including a nice automatic download/installer.
    Attached Files Attached Files

  9. #9
    Fascinating stuff, I never thought you could use those tools with flex. Is it possible to use some of these methods to create a full-fledged AS3 obfustcator?

  10. #10
    164
    posts
    Registered User
    Why, in most people's uneducated opinion, 'obfuscation' is the C preprocessor's middle name!

    Most http://www.ioccc.org/ submissions contain macros for a reason.

    Don't go there unless you can appreciate PURE UNADULTERATED EVIL.

    Code:
    // Obfuscate.h
    #ifndef OBFUSCATE_H
    #define  OBFUSCATE_H
    #ifndef DEBUG
    /*
     * Redefine member function and data labels to MY data only...
     */
    #define doCycle fOo
    #define doEvent Foo
    #define myAICycler Z2
    #define x Foo
    #define y fo0
    #define z foO
    // And on and on - Just take care not to overlap things and cause naming conflicts
    
    #endif // DEBUG
    #endif // OBFUSCATE_H
    Note how case sensitivity can make variable names very difficult to follow. For any three letter label, there are eight kinds of capitalization to place on it. For four letter labels, 16. Also use letters/numbers that look vaguely similar, like 6G 5S 2Z, 7T 0O.

    The preprocessor will only do one pass at things, so if you are redefining X to Y, you can (nay:MUST!) redefine Y to X, or something else.

    While you can't use this technique to mask the labels of system calls (they'll have to match, or the code won't find 'em), anything you do in-game can be masked. You can maybe do things like make pointers to system functions that might 'give away' your function's intended use, and make people go fish around for things a little more in order to
    read your code, too. Then you can have three or four different aliases in the release code for 'setPixel' (or whatever) that all evaluate to the same thing, so it becomes less clear where the custom rendering is really happening.

    Also, local functions and small global functions can be replaced with macros, masking the nature of all manner of internal 'goodness' in your own source code, replacing it with, well lots of horrible, repetitive spew.

    For instance I actually plan to do my 2D/3D math based on macro functions with loose variables and anonymous {x:Number, y:Number, z:Number} objects for speed anyway (it takes less time to do most such operations than to find and execute the static function call to do the operations from a Class). Ironically, this can sometimes make the code easier to follow in the debugger, since you don't have to follow in/out of function calls to see what happens.

    Code:
    // Some example (not necessarily well thought out) 'Point' code for inside a function.
    // If you want to obfuscate the output further, you could redefine _x, _y and _z as well, 
    // so things that reference them as class members don't expand very readably.
    #define PT_x( label ) ppConcat(label,_x) 
     #define PT_y( label ) ppConcat(label,_y)
        #define PT_z( label ) ppConcat(label,_z)
       #define XY_Decl( label ) var PT_x( label ):Number; var PT_y( label ):Number;
       #define XYZ_Decl( label ) XY_Decl( label ) var PT_z( label ):Number;
    #define XY_Decl_Mbrs( access, label ) access var PT_x( label ):Number; access var PT_y( label ):Number;
        #define XYZ_Decl_Mbrs( access, label ) XY_Decl_Mbrs( access, label ) access var PT_z( label ):Number;
    #define XY_Scale( label, scalar ) { PT_x( label )*=scalar; PT_y( label )*=scalar;}
    #define XYZ_Scale( label, scalar ) { XY_Scale( label, scalar ) PT_z( label )*=scalar;}
    #define XY_Neg( label ) { PT_x( label )=-PT_x( label ); PT_y( label )=-PT_y( label );}
    #define XYZ_Neg( label ) { XY_Neg( label ) PT_z( label )=-PT_z( label );}
    #define XY_Sub( label, subLabel ) { PT_x( label )-=PT_x( subLabel ); PT_y( label )-=PT_y( subLabel );}
     #define XYZ_Sub( label, subLabel ) { XY_Sub( label, subLabel ) PT_z( label )-=PT_z( subLabel );}
    // And on and on...
    // This doesn't consume any extra space in the swf for unused code
    // This expands literally in place for function-like macro invocation
    // There can be THOUSANDS (if you need them), and they won't clutter the runtime 
    // namespaces that are searched for functions/constants/values.
    Certain kinds of iteration become easier to write, too. If you haven't used a 'foreach' macro in C for iterations, you've missed out. This puts a line or two of code into your
    source file, but it expands to all kinds of nice stuff. This came out of a piece of template-
    driven graphics code that made 2D primitives from pixel formats, and provided some additional tools to do common iterations in a friendly manner.
    Code:
    /**
     * \brief Fixed point Bresenham-like line calculator
     * \param label Label to prepend to allow for multiple instances
    **/
    #define IncLine(label)\
        int    ppConcat(label,_x1);\
        int    ppConcat(label,_y1);\
        int    ppConcat(label,_x2);\
        int    ppConcat(label,_y2);\
        int    ppConcat(label,_x);\
        int    ppConcat(label,_y);\
        int    ppConcat(label,_dx);\
        int    ppConcat(label,_dy);\
        int    ppConcat(label,_remain);\
    
    /**
     * \brief Initialize incremental line calculations to step point-by-point
     * \param ilabel Describes label from IncLine with any instance indirection attached to it (i.e. 'mydata->label')
     * \param x1,y1 First point in line
     * \param x2,y2 Last point in line (inclusive)
    **/
    #define IncLine_Init_Step(ilabel, x1,y1, x2,y2 ) \
    {\
        ppConcat(ilabel,_x1) = (x1);\
        ppConcat(ilabel,_y1) = (y1);\
        ppConcat(ilabel,_x2) = (x2);\
        ppConcat(ilabel,_y2) = (y2);\
        ppConcat(ilabel,_y) = ppConcat(ilabel,_y1)<<16;\
        ppConcat(ilabel,_x) = ppConcat(ilabel,_x1)<<16;\
        ppConcat(ilabel,_dx) = ppConcat(ilabel,_x2) - ppConcat(ilabel,_x1);\
        ppConcat(ilabel,_dy) = ppConcat(ilabel,_y2) - ppConcat(ilabel,_y1);\
        ppConcat(ilabel,_dx) = further(ppConcat(ilabel,_dx),1);\
        ppConcat(ilabel,_dy) = further(ppConcat(ilabel,_dy),1);\
        ppConcat(ilabel,_remain) = max(abs(ppConcat(ilabel,_dx)),abs(ppConcat(ilabel,_dy)));\
        ppConcat(ilabel,_dx) = (ppConcat(ilabel,_dx)<<16)/ppConcat(ilabel,_remain);\
        ppConcat(ilabel,_dy) = (ppConcat(ilabel,_dy)<<16)/ppConcat(ilabel,_remain);\
    }
    
    /**
     * \brief Step to next coordinate
     * \param ilabel Describes label from IncLine with any instance indirection attached to it (i.e. 'mydata->label')
    **/
    #define IncLine_Step(ilabel)\
    {\
        if( ppConcat(ilabel,_remain) > 0 )\
        {\
            ppConcat(ilabel,_x) += ppConcat(ilabel,_dx);\
            ppConcat(ilabel,_y) += ppConcat(ilabel,_dy);\
            ppConcat(ilabel,_remain)--;\
        }\
    }
    
    /**
     * \brief Iterate each point in IncLine
     * \param ilabel Describes label from IncLine with any instance indirection attached to it (i.e. 'mydata->label')
     * \param iteratorx What to call the x iterator
     * \param iteratory What to call the y iterator
     * \param iteratornx What to call the iterator for the 'next' x coordinate
     * \param iteratorny What to call the iterator for the 'next' y coordinate
    **/
    #define IncLine_foreach_spans(ilabel, iteratorx, iteratory, iteratornx, iteratorny ) \
        int iteratorx = ppConcat(ilabel,_x1);\
        int iteratory = ppConcat(ilabel,_y1);\
        int iteratornx, iteratorny;\
        IncLine_Step(ilabel);\
        IncLine_XY(ilabel, iteratornx, iteratorny);\
        for( ; ppConcat(ilabel,_remain)--; iteratorx = iteratornx, iteratory = iteratorny, \
            iteratornx = (ppConcat(ilabel,_x) += ppConcat(ilabel,_dx))>>16, \
            iteratorny = (ppConcat(ilabel,_y) += ppConcat(ilabel,_dy))>>16 )
    This places a complex and error-prone iteration that might otherwise need to be a function with a callback inline with surrounding function code, so it shares local variables that also don't need to be wrapped up on the call stack. Now imagine finding the unnamed output from that mess in your decompiler output.

    Yes, in Actionscript you can make a local function, but not in C, and your local function... it would probably need to do a callback, and everything needs to be put on the stack and popped off, etc. to call it. Also this code isn't particularly efficient in AS, or even CPUs with GOOD floating point implementations. The right shifting is faster than integer divide, but all in all, it should be done with 'Number' types and fewer procedural steps to shift/mask the fixed point values.

    For the sake of obfuscation, math by direct binary manipulation is a good choice, as is breaking up sin/cos/tan calls to their equivalent arithmetical steps, so the fact that you're doing these calculations is masked. Also, matrix math can be wrapped up nicely in macros, and remove the iteration of points in favor of step-by-step calculations. In the resulting code, you get a long list of adds and multiplies.

    In pp.h in that zip file I keep uploading, there are some VERY good unrolled macro iterations provided as well. Specifically, ppCombination, which does unique permutations for shorter comparisons, and ppOps, which can be used for all manner of evil, but mostly things like brewing up constants from a friendlier form.

    Here are some more macros for iterating.
    Code:
    /*
     * Basic permutation iteratiors
     *
     * The following iterate on variables that you privide in a relatively 
     * efficient manner.
     *
     * p1,p2,pn Can be integers, pointers into an array or stl random access iterator like objects
     * begin,end Are similar to members gotten from stl container type begin,end
     */
    
    /**
     * \brief Do exponential sets of p1,p2
     * Iterates combinations of p1,p2 like a counter; so begin of 0 and end of 10 would count from 00 to 99.
     * exponents_2( p1,p2, 0,3 ) -> 00,01,02,10,11,12,20,21,22
     * Combinations come up sorted from p1->p2
     * \param p1,p2 Loop variables to iterate combinations with
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define exponents_2( p1,p2, begin,end )\
        for( (p1) = (begin); (p1) < (end); ++(p1) )\
        for( (p2) = (begin); (p2) < (end); ++(p2) )\
    
    /**
     * \brief Return count of iterations exponents_2 represents
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define exponents_2_calc(begin,end)    ( ((end)-(begin)) * ((end)-(begin)) )
    
    /**
     * \brief Do arrangements of p1,p2
     * Iterates all arrangements of p1,p2; 1,2 and 2,1 appear, but not 1,1 or 2,2
     * arrangements_2( p1,p2, 0,4 ) -> 01,02,03,10,12,13,20,21,23,30,31,32
     * Combinations come up sorted from p1->p2
     * Basically, does the exponential iteration and filters out items that contain matches
     * \param p1,p2 Loop variables to iterate combinations with
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define arrangements_2( p1,p2, begin,end )\
        exponents_2( p1,p2, begin,end )\
        if( (p1) != (p2) )
    
    /**
     * \brief Return count of iterations arrangements_2 represents
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define arrangements_2_calc(begin,end)    ( ((end)-(begin)) * ((end)-(begin)-1) )
    
    /**
     * \brief Do combinations/boxed arrangements of p1,p2
     * Only iterates unique combinations; i.e. 1,2 and 2,1 will not both appear
     * combinations_2( p1,p2, 0,4 ) -> 01,02,03,12,13,23
     * Combinations come up sorted from p1->p2
     * \param p1,p2 Loop variables to iterate combinations with
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define combinations_2( p1,p2, begin,end )\
        for( (p1) = (begin); (p1) < ((end)-1); ++(p1) )\
        for( (p2) = (p1)+1; (p2) < (end); ++(p2) )
    
    /**
     * \brief Return count of iterations combinations_2 represents
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define combinations_2_calc(begin,end)    ( ((end)-(begin)) * ((end)-(begin)-1) / (2) )
    
    
    
    /**
     * \brief Do exponential sets of p1,p2,p3
     * Iterates combinations of p1,p2,p3 like a counter; so begin of 0 and end of 10 would count from 000 to 999.
     * Combinations come up sorted from p1->p3
     * \param p1,p2,p3 Loop variables to iterate combinations with
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define exponents_3( p1,p2,p3, begin,end )\
        for( (p1) = (begin); (p1) < (end); ++(p1) )\
        for( (p2) = (begin); (p2) < (end); ++(p2) )\
        for( (p3) = (begin); (p3) < (end); ++(p3) )\
    
    /**
     * \brief Return count of iterations exponents_3 represents
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define exponents_3_calc(begin,end)    ( ((end)-(begin)) * ((end)-(begin)) * ((end)-(begin)) )
    
    /**
     * \brief Do non-unique integer permutations of p1,p2,p3
     * Iterates all arrangements of p1,p2,p3; 1,2,3 and 3,2,1 appear, but not 1,1,3 or 2,2,2
     * Combinations come up sorted from p1->p3
     * Basically, does the exponential iteration and filters out items that contain matches
     * \param p1,p2,p3 Loop variables to iterate combinations with
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define arrangements_3( p1,p2,p3, begin,end )\
        exponents_3( p1,p2,p3, begin,end )\
        if( ppCombination3( &&, ppNOTEQUAL, p1,p2,p3 ) )
        //if( (p1) != (p2) && (p1) != (p3) && (p2) != (p3) )
        
    /**
     * \brief Return count of iterations arrangements_3 represents
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define arrangements_3_calc(begin,end)    ( ((end)-(begin)) * ((end)-(begin)-1) * ((end)-(begin)-2) )
    
    /**
     * \brief Do combinations/boxed arrangements of p1,p2
     * Only iterates unique combinations; i.e. 1,2,3 and 3,2,1 will not both appear
     * Combinations come up sorted from p1->p3
     * \param p1,p2,p3 Loop variables to iterate combinations with
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define combinations_3( p1,p2,p3, begin,end )\
        for( (p1) = (begin); (p1) < ((end)-2); ++(p1) )\
        for( (p2) = (p1)+1; (p2) < ((end)-1); ++(p2) )\
        for( (p3) = (p2)+1; (p3) < (end); ++(p3) )
    
    /**
     * \brief Return count of iterations combinations_3 represents
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define combinations_3_calc(begin,end)    ( ((end)-(begin)) * ((end)-(begin)-1) * ((end)-(begin)-2) / (3*2) )
    
    
    
    /**
     * \brief Do exponential sets of p1,p2,p3,p4
     * Iterates combinations of p1,p2 like a counter; so begin of 0 and end of 10 would count from 0 to 9999.
     * Combinations come up sorted from p1->p2
     * \param p1,p2,p3,p4 Loop variables to iterate combinations with
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define exponents_4( p1,p2,p3,p4, begin,end )\
        for( (p1) = (begin); (p1) < (end); ++(p1) )\
        for( (p2) = (begin); (p2) < (end); ++(p2) )\
        for( (p3) = (begin); (p3) < (end); ++(p3) )\
        for( (p4) = (begin); (p4) < (end); ++(p4) )\
    
    /**
     * \brief Return count of iterations exponents_4 represents
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define exponents_4_calc(begin,end)    ( ((end)-(begin)) * ((end)-(begin)) * ((end)-(begin)) * ((end)-(begin)) )
    
    /**
     * \brief Do non-unique integer permutations of p1,p2,p3,p4
     * Iterates all arrangements of p1,p2,p3,p4; 1,2,3,4 and 4,3,2,1 appear, but not 1,1,3,4 or 2,2,2,3
     * Combinations come up sorted from p1->p4
     * Basically, does the exponential iteration and filters out items that contain matches
     * \param p1,p2,p3,p4 Loop variables to iterate combinations with
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define arrangements_4( p1,p2,p3,p4, begin,end )\
        exponents_4( p1,p2,p3,p4, begin,end )\
        if( ppCombination4( &&, ppNOTEQUAL, p1,p2,p3,p4 ) )
        //if( (p1) != (p2) && (p1) != (p3) && (p1) != (p4) && (p2) != (p3) && (p2) != (p4) && (p3) != (p4) )
    
    /**
     * \brief Return count of iterations arrangements_4 represents
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define arrangements_4_calc(begin,end)    (((end)-(begin))*(((end)-(begin))-1)*(((end)-(begin))-2)*(((end)-(begin))-3))
    
    
    /**
     * \brief Do unique integer permutations of p1,p2,p3,p4
     * Only iterates unique combinations; i.e. 1,2,3,4 and 4,3,2,1 will not both appear
     * Combinations come up sorted from p1->p4
     * \param p1,p2,p3,p4 Loop variables to iterate combinations with
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define combinations_4( p1,p2,p3,p4, begin,end )\
        for( (p1) = (begin); (p1) < ((end)-3); ++(p1) )\
        for( (p2) = (p1)+1; (p2) < ((end)-2); ++(p2) )\
        for( (p3) = (p2)+1; (p3) < ((end)-1); ++(p3) )\
        for( (p4) = (p3)+1; (p4) < (end); ++(p4) )
    
    /**
     * \brief Return count of iterations combinations_4 represents
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define combinations_4_calc(begin,end)        ((((end)-(begin))*(((end)-(begin))-1)*(((end)-(begin))-2)*(((end)-(begin))-3))/(4*3*2))
    
    
    /**
     * \brief Do exponential sets of p1~p5
     * Iterates combinations of p1,p2 like a counter; so begin of 0 and end of 10 would count from 00000 to 99999.
     * Combinations come up sorted from p1->p5
     * \param p1,p2,p3,p4,p5 Loop variables to iterate combinations with
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define exponents_5( p1,p2,p3,p4,p5, begin,end )\
        for( (p1) = (begin); (p1) < (end); ++(p1) )\
        for( (p2) = (begin); (p2) < (end); ++(p2) )\
        for( (p3) = (begin); (p3) < (end); ++(p3) )\
        for( (p4) = (begin); (p4) < (end); ++(p4) )\
        for( (p5) = (begin); (p5) < (end); ++(p5) )\
    
    /**
     * \brief Return count of iterations exponents_5 represents
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define exponents_5_calc(begin,end)    ( ((end)-(begin)) * ((end)-(begin)) * ((end)-(begin)) * ((end)-(begin)) * ((end)-(begin)) )
    
    /**
     * \brief Do unique integer permutations of p1~p5
     * Iterates all arrangements of p1,p2,p3,p4; 1,2,3,4,5 and 5,4,3,2,1 appear, but not 1,1,3,4,5 or 2,2,2,3,4
     * Combinations come up sorted from p1->p4
     * Basically, does the exponential iteration and filters out items that contain matches
     * \param p1,p2,p3,p4,p5 Loop variables to iterate combinations with
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define arrangements_5( p1,p2,p3,p4,p5, begin,end )\
        exponents_5( p1,p2,p3,p4,p5, begin,end )\
        if( ppCombination5( &&, ppNOTEQUAL, p1,p2,p3,p4,p5 ) )
        //if( (p1) != (p2) && (p1) != (p3) && (p1) != (p4) && (p1) != (p5) && (p2) != (p3) && (p2) != (p4) && (p2) != (p5) && (p3) != (p4) && (p3) != (p5) && (p4) != (p5) )
    
    /**
     * \brief Return count of iterations arrangements_5 represents
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define arrangements_5_calc(begin,end)    (((end)-(begin))*(((end)-(begin))-1)*(((end)-(begin))-2)*(((end)-(begin))-3)*(((end)-(begin))-4))
    
    /**
     * \brief Do combinations/boxed arrangements of p1,p2
     * Only iterates unique combinations; i.e. 1,2,3,4,5 and 5,4,3,2,1 will not both appear
     * Combinations come up sorted from p1->p5
     * \param p1,p2,p3,p4,p5 Loop variables to iterate combinations with
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define combinations_5( p1,p2,p3,p4,p5, begin,end )\
        for( (p1) = (begin); (p1) < ((end)-4); ++(p1) )\
        for( (p2) = (p1)+1; (p2) < ((end)-3); ++(p2) )\
        for( (p3) = (p2)+1; (p3) < ((end)-2); ++(p3) )\
        for( (p4) = (p3)+1; (p4) < ((end)-1); ++(p4) )\
        for( (p5) = (p4)+1; (p5) < (end); ++(p5) )
    
    /**
     * \brief Return count of iterations combinations_5 represents
     * \param begin First in series 
     * \param end Last in series, like stl end() (count == (end-begin))
    **/
    #define combinations_5_calc(begin,end)        ((((end)-(begin))*(((end)-(begin))-1)*(((end)-(begin))-2)*(((end)-(begin))-3)*(((end)-(begin))-4))/(5*4*3*2))
    To anyone comfortable with it, the obvious true middle name of "C Preprocessor" is 'pro'.

    Also just as obviously, MTASC or HAXE can have a preprocessor slapped on with this technique.

  11. #11
    I have to admit that i don't understand half the stuff you wrote but the hardcore programmer types are sure to find it useful.

    So, if I wanted to obfuscate the identifiers in a big project (>100 classes, several packages)
    what would be the easiest way? Would it work if I defined aliases for every variable/class/method at the start of the main application file? Or would I need to do it separately for each .as file and be careful to match outside references with their obfuscated aliases? And if I did it right, then I can work with my unobfuscated code and it will compile to obfuscated code, correct?

  12. #12
    164
    posts
    Registered User
    Quote Originally Posted by hghgsf View Post
    I have to admit that i don't understand half the stuff you wrote but the hardcore programmer types are sure to find it useful.

    So, if I wanted to obfuscate the identifiers in a big project (>100 classes, several packages)
    what would be the easiest way? Would it work if I defined aliases for every variable/class/method at the start of the main application file? Or would I need to do it separately for each .as file and be careful to match outside references with their obfuscated aliases? And if I did it right, then I can work with my unobfuscated code and it will compile to obfuscated code, correct?
    Easiest way: 'obfuscate.h' file with all the classes and class member redefined. Group the definitions per class. Test it ONE class at a time.

    Add '#include "obfuscate.h"' to every file, or add a forced include of obfuscate.h to the preprocessor command line.

    As long as you wrap the definitions in your obfuscate.h with an '#ifdef DEBUG' conditional, your source code should look exactly the same. In the debugger it should look exactly the same.

    In the release build, it will be spaminated. In the 'as' folder with the release mode intermediate as output, you can view the 'damage' to its readability and decide if you're going far enough. Keep in mind that local variables shouldn't need to be redefined; they'll be nicely anonymous already in a non-debug build.

    If you haven't already, you can download a trial version of an AS2 decompiler and see what that code looks like.

  13. #13
    164
    posts
    Registered User
    Bug!

    The MinGW GNU cpp (I don't know about other GCC revisions) that I use will double-space the comments if the input file is DOS encoded (\r\n for newlines). This naturally buggers the line count, and errors mismatch the original source.

    Making the source files 'unix encoded' (\n newlines) makes the problem go away.

  14. #14
    164
    posts
    Registered User

    Sourceforge.net

    Hey, it popped up on my list of sf projects. No notification or anything.
    http://sourceforge.net/projects/flex2cpp/

    To get current/future versions, go to code->SVN Browse, and grab the 'BlankProject.zip' file.

  15. #15
    164
    posts
    Registered User
    Another little trick that's super handy in speed-critical code is that the containing function shares its local variables with its local functions. This is like passing ALL of the locals in the outer function on the stack to the contained one, so it's an awfully handy little gimick.

    In fdb, when you 'info local' the locals of a local function, the containing function's locals are invisible. They're accessible by the code, but they're not accessible by the debugger. That's a shame, but what can you do? Not use local functions?.

    No! Just work around it.

    Code:
    #include "pp.h"
    ...
    
    function outerFunc( lut:Array ) : void
    {
        var item:Object;
        #define COMMON_LOCALS \
            var x:int;\
            var y:int;\
            var cel:Object;\
        // Local variables shared in locals called by this
        ppRelease(COMMON_LOCALS)
    
        var dispatch:Array = 
        [
            function():void
            {
                ppDebug(COMMON_LOCALS)
                 x = 4;
                y = 5;
                cel = map.celGet(x,y);
                item.doSomething(cel,x,y);
            },
            function():void
            {
                ppDebug(COMMON_LOCALS)
                  x = 6;
                y = 7;
                cel = map.celGet(x,y);
                item.doSomething(cel,x,y);
            },
        ];
        for each(item in myArray)
            dispatch[item.index]();
    }
    In this brainless pseudocode example, it wouldn't make much of a difference, but with a lot of common/similar things to do that are dispatched through an array instead of switched, all in a function that's called a lot, the overhead for allocating, constructing, destructing, dereferencing and cleaning up those local variables adds up.

    Keeping them all in one macro allows us to put them in the local functions where they can be debugged and browsed, or in the outer layer function where they can be instantiated just once, according to whether we need maximum debugging or maximum performance.

    Another little thing, look at that repetitive code! Maybe there's something you can do with the preprocessor without adding another local function call into the stack?

    Code:
    #include "pp.h"
    ...
    
    function outerFunc( lut:Array ) : void
    {
        var item:Object;
        #define COMMON_LOCALS \
            var x:int;\
            var y:int;\
            var cel:Object;\
        // Local variables shared in locals called by this
        ppRelease(COMMON_LOCALS)
    
        #define COMMON_OPERATIONS(cx,cy)\
            ppDebug(COMMON_LOCALS)\
             cel = map.celGet(x,y);\
             item.doSomething(cel,x,y);
    
        var dispatch:Array = 
        [
            function():void
            {
                COMMON_OPERATIONS(4,5);
            },
            function():void
             {
                COMMON_OPERATIONS(6,7);
            },
        ];
        for each(item in myArray)
        {
            assert(item.index < dispatch.length);
            dispatch[item.index]();
        }
    }
    Now you have some common operations 'wrapped' in a way that they get copied/pasted into the code wherever they're needed. It allows you to make small changes that multiply effects.

    Of course, you can't 'step' common operations like that macro. It all expands to one line of mashed-together code. You reserve this sort of trick for common initialization and simple operations that you've already worked the kinks out of and don't expect any trouble from, or things you've canned to generate code.


    ...

    Oh, BTW, I made an 'installer' for that sourceforge project that grabs the required tools off the web and installs them all wherever you tell it to.

Page 1 of 2 12 LastLast

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  

Home About kirupa.com Meet the Moderators Advertise

 Link to Us

 Credits

Copyright 1999 - 2012