I mentioned earlier that I’m writing Orcs vs. Martians in a home-brewed programming language, Meta-C. Meta-C gives me a chance to experiment with the design of a programming language, to try to make programming easier.
To me, “easier programming” means
- less code is required to implement a particular feature
- the code is easier to maintain and refactor as the app evolves
So I made Meta-C to have the same syntax as C++, but modified it to be less verbose, and to require fewer changes when the architecture of the code evolves. (Excuse me for going a bit off-subject about a programming language probably nobody will ever use but me, but I like this stuff : )
Here’s an example that illustrates some of the improvement possible over C++. Orcs vs. Martians has a function that looks for the nearest enemy base. It’s a small function, and it does three fairly common things; it:
- iterates over a container (all the entities in the game map)
- tests elements of a certain type (”Bases”)
- returns the “best” element by some criteria (the nearest base)
Comparing the C++ and the Meta-C implementations of this function is pretty revealing. First, here’s the C++:
01 Base* Base::getBaseToAttack()
02 {
03 Base* ret = NULL;
04 for (set<Entity*>::iterator ent = gMap->getEnts().begin();
05 ent != gMap->getEnts().end();
06 ent++) {
07 Base* base = dynamic_cast<Base*>(*ent);
08 if (base)
09 if (isEnemy(base) && (!ret || distTo(base) < distTo(ret)))
10 ret = base;
11 }
12 return ret;
13 }
Now, here’s the Meta-C:
01 Base* getBaseToAttack()
02 {
03 for gMap.ents,Base*
04 if isEnemy(base) && (!$ || distTo(base) < distTo($))
05 $ = base;
06 }
With Meta-C, there’s much less code! Mostly, because
- Meta-C’s “for” keyword natively understands STL containers. When its argument is an STL container, it automatically generates that ugly STL iteration code
- “for” also takes an optional element type (”Base*”), when you only want to iterate over elements of that type
- The “$” symbol means the return value of the function. If used, Meta-C automatically generates its declaration and a return statement for it in the function
- other minor things you can probably pick out
Not only is there less code, Meta-C requires less re-work as the application evolves. For example, if the following changes happened, here’s what the impact would be on the two implementations of the function.
- if the type of the container changed, e.g. from a std::set to a std::list, the C++ implementation would need a change to its “for” loop. The Meta-C wouldn’t need any change.
- if the function’s return type changed from Base* to something else, the C++ function would need changes in four places. Meta-C would need changes in two places.
- if a dedicated “gMap.bases” container was created that contained only bases, and you wanted to iterate over that instead of the “gMap.ents” container that contains all game entities, the C++ function would require several changes: changing the two instances of “gMap->getEnts()” in the for loop to “gMap->getBases()”; deleting the lines doing the dynamic_cast operation and testing its result; and un-indenting the 2nd if statement. Meta-C would only require one change: from “for gMap.ents,Base*” to “for gMap.bases”.
I like this language :)
We now return you to your regularly scheduled blog.