Note that there are some explanatory texts on larger screens.

plurals
  1. POEXTENDS challenge: preprocessor function macros and class-like oop
    text
    copied!<p><strong>Background</strong></p> <p>I've been using the C preprocessor to manage and "compile" semi-large javascript projects with multiple files and build targets. This gives full access to C preprocessor directives like <code>#include</code>, <code>#define</code>, <code>#ifdef</code>, etc. from within javascript. Here's a sample build script so you can test the example code:</p> <pre><code>#!/bin/bash export OPTS="-DDEBUG_MODE=1 -Isrc" for FILE in `find src/ | egrep '\.js?$'` do echo "Processing $FILE" cat $FILE \ | sed 's/^\s*\/\/#/#/' \ | cpp $OPTS \ | sed 's/^[#:&lt;].*// ; /^$/d' \ &gt; build/`basename $FILE`; done </code></pre> <p>Make a <code>src</code> and a <code>build</code> directory, and put the .js files in <code>src</code>.</p> <hr> <p><strong>Convenience Macros</strong></p> <p>Originally, I just wanted the preprocessor stuff for <code>#include</code> and maybe a few <code>#ifdef</code>s, but I got to thinking, wouldn't it be nice to have some convenience macros too? Experimentation ensued.</p> <pre><code>#define EACH(o,k) for (var k in o) if (o.hasOwnProperty(k)) </code></pre> <p>Cool, so now I can write something like this:</p> <pre><code>EACH (location, prop) { console.log(prop + " : " location[prop]); } </code></pre> <p>And it will expand to:</p> <pre><code>for (var prop in location) if (location.hasOwnProperty(prop)) { console.log(prop + " : " location[prop]); } </code></pre> <p>How about foreach?</p> <pre><code>#define FOREACH(o,k,v) var k,v; for(k in o) if (v=o[k], o.hasOwnProperty(k)) // ... FOREACH (location, prop, val) { console.log(prop + " : " + val) } </code></pre> <p>Notice how we sneak <code>v=o[k]</code> inside the <code>if</code> condition so it doesn't disturb the curly braces that should follow the invocation of this macro.</p> <hr> <p><strong>Class-like OOP</strong></p> <p>Let's start with a NAMESPACE macro and an obscure but useful js pattern...</p> <pre><code>#define NAMESPACE(ns) var ns = this.ns = new function() </code></pre> <p><code>new function(){ ... }</code> does some neat stuff. It calls an anonymous function as a constructor, so it doesn't need an extra <code>()</code> at the end to call it, and within it <code>this</code> refers to the object being created by the constructor, in other words, the namespace itself. This also allows us to nest namespaces within namespaces.</p> <p>Here is my full set of class-like OOP macros:</p> <pre><code>#define NAMESPACE(ns) var ns=this.ns=new function() #define CLASS(c) var c=this;new function() #define CTOR(c) (c=c.c=this.constructor=$$ctor).prototype=this;\ function $$ctor #define PUBLIC(fn) this.fn=fn;function fn #define PRIVATE(fn) function fn #define STATIC(fn) $$ctor.fn=fn;function fn </code></pre> <p>As you can see, these macros define many things both in the <code>Variable Object</code> (for convenience) and in <code>this</code> (from necessity). Here's some example code:</p> <pre><code>NAMESPACE (Store) { CLASS (Cashier) { var nextId = 1000; this.fullName = "floater"; CTOR (Cashier) (fullName) { if (fullName) this.fullName = fullName; this.id = ++nextId; this.transactions = 0; } PUBLIC (sell) (item, customer) { this.transactions += 1; customer.inventory.push(item); } STATIC (hire) (count) { var newCashiers = []; for (var i=count; i--;) { newCashiers.push(new Cashier()); } return newCashiers; } } CLASS (Customer) { CTOR (Customer) (name) { this.name = name; this.inventory = []; this.transactions = 0; } PUBLIC (buy) (item, cashier) { cashier.sell(this, item); } } } </code></pre> <hr> <p><strong>What about EXTENDS?</strong></p> <p>So this brings me to the question... how can we implement EXTENDS as a macro to wrap the usual "clone the prototype, copy constructor properties" js prototype inheritance? I haven't found a way to do it outside of requiring the EXTENDS to appear <em>after</em> the class definition, which is silly. This experiment needs EXTENDS or it's useless. Feel free to change the other macros as long as they give the same results.</p> <p>Edit - These might come in handy for EXTENDS; listing them here for completeness.</p> <pre><code>#define EACH(o,k) for(var k in o)if(o.hasOwnProperty(k)) #define MERGE(d,s) EACH(s,$$i)d[$$i]=s[$$i] #define CLONE(o) (function(){$$C.prototype=o;return new $$C;function $$C(){}}()) </code></pre> <p>Thanks in advance for any help, advice, or lively discussion. :)</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload