Casey Muratori
We all know how to program in C++, don’t we? I mean, we’ve all read a selection of wonderful books by the gaggle of bearded fellows who defined the language in the first place, so we’ve all learned the best ways to write C++ code that solves real-world problems.
First, you look at the real world problem — say, a payroll system — and you see that it has some plural nouns in it: “employees”, “managers”, etc. So the first thing you need to do is make classes for each of these nouns. There should be an employee class and a manager class, at least.
But really, both of those are just people. So we probably need a base class called “person”, so that things in our program that don’t care whether you’re an employee or a manager can just treat you as a person. This is very humanizing, and makes the other classes feel less like cogs in a corporate machine!
There’s a bit of a problem, though. Isn’t a manager also an employee? So manager should probably inherit from employee, and then employee can inherit from person. Now we’re really getting somewhere! We haven’t actually thought about how to write any code, sure, but we’re modeling the objects that are involved, and once we have those solid, the code is just going to write itself.
Wait, shoot — you know what? I just realized, what if we have contractors? We definitely need a contractor class, because they are not employees. The contractor class could inherit from the person class, because all contractors are people (aren’t they?). That would be totally sweet.
But then what does the manager class inherit from? If it inherits from the employee class, then we can’t have managers who work on contract. If it inherits from the contractor class, then we can’t have full-time managers. This is turning out to be a really hard programming problem, like the Simplex algorithm or something!
OK, we could have manager inherit from both classes, and then just not use one of them. But that’s not type-safe enough. This isn’t some sloppy JavaScript program! But you know what? BAM! I’ve got the solution right here: we templatize the manager class. We templatize the manager class on its base class, and then everything that works with manager classes is templatized on that as well!
This is going to be the best payroll system ever! As soon as I get all these classes and templates spec’d out, I’m going to fire up my editor and get to work on the UML diagrams.
It’d be great if everything I just wrote had been farcical, but sadly, there’s actually a lot of programmers in the world who think like this. I’m not talking about “Bob the Intern” — I’m talking about all kinds of programmers, including famous programmers who give lectures and write books. I am also sad to say that there was a time in my life when I thought this way, too. I was introduced to “object oriented programming” when I was 18, and it took me until I was about 24 to realize it was all a load of horseshit (and the realization was thanks in no small part to my taking a job with RAD Game Tools, which thankfully never bought into the whole OOP nightmare).
But despite the fact that many programmers out there have gone through bad phases like this and eventually come to smart conclusions about how to actually write good code efficiently, it seems that the landscape of educational materials out there still overwhelmingly falls into the “objectively bad” category. I suspect this has something to do with the fact that good programming seems very straightforward once you know how to do it, unlike, say, a fancy math technique that retains its sexiness and makes you want to spend the time to post about it. So, although I don’t have any data to back this up, I strongly suspect that experienced programmers rarely spend time posting about how they program because they just don’t think it’s anything special.
But they should! It may not be special, but it’s necessary, and if good programmers don’t start posting about how to do good programming, we’ll never get out of this nasty place where everyone has to go through six years of writing horrible object-oriented programs before they realize they’re wasting their time. So what I’d like to do with this next set of Witness articles is spend some serious word count talking about the purely mechanical process of putting code into a computer, and it is my sincere hope that other experienced programmers out there will take some time to do the same. Personally, I’d love to read more about the techniques actual good programmers out there use when they sit down to code.
To start things off, I am going to detail a straightforward set of code transformations that I did on The Witness’s editor code. In the coming weeks, I’ll move from that into some larger examples where I wrote more pieces from scratch, but the entire time I’ll be focusing solely on code and how it’s structured. Nothing that I’m going to cover has any fancy algorithms or math or anything, it’s all just pure plumbing.
In the built-in editor for The Witness, there is a piece of UI called the “Movement Panel”. It is a floating window with some buttons on it that are used to perform operations on entities like “rotate 90 degrees”. Originally it was quite small and had only a few buttons, but when I started working on the editor, I added a bunch of features that needed to go in the movement panel. This was going to expand its contents considerably, and it meant I had to learn how to add elements to the UI, which I’d never done before. I examined the existing code, which looked like this:
int num_categories = 4;
int category_height = ypad + 1.2 * body_font->character_height;
float x0 = x;
float y0 = y;
float title_height = draw_title(x0, y0, title);
float height = title_height + num_categories * category_height + ypad;
= height;
my_height -= title_height;
y0
{
-= category_height;
y0 char *string = "Auto Snap";
bool pressed =
(x0, y0, my_width, category_height, string);
draw_big_text_buttonif (pressed)
(this);
do_auto_snap}
{
-= category_height;
y0 char *string = "Reset Orientation";
bool pressed =
(x0, y0, my_width, category_height, string);
draw_big_text_buttonif (pressed)
{
// ...
}
}
// ...
The first thing I noticed here was that Jon, the original programmer, did a really nice job setting me up for success with what I was about to do. A lot of times, you open up some code for something simple like this, and you find that it is just a massive tangle of unnecessary structure and indirection. Here, instead, we find an extremely straightforward series of things happening, that read exactly like how you would instruct a person to draw a UI panel: “First, figure out where the title bar should go. Then, draw the title bar. Now, below that, draw the Auto Snap button. If it’s pressed, do auto snapping…” This is exactly how programming should go. I suspect that most anyone could read this code and know what it was doing, and probably intuit how to add more buttons without having to read anything beyond just this excerpt.
However, nice as the code was, it was obviously not set up for doing large amounts of UI, because all the layout work was still being done by hand, in-line. This is mildly inconvenient in the snippet above, but gets more onerous once you consider more complex layouts, like this piece of the UI that has four separate buttons that occur on the same row:
{
-= category_height;
y0 float w = my_width / 4.0f;
float x1 = x0 + w;
float x2 = x1 + w;
float x3 = x2 + w;
unsigned long button_color;
unsigned long button_color_bright;
unsigned long text_color;
(this, motion_mask_x, &button_color,
get_button_properties&button_color_bright, &text_color);
bool x_pressed =
(x0, y0, w, category_height, "X", button_color,
draw_big_text_button, text_color);
button_color_bright(this, motion_mask_y, &button_color,
get_button_properties&button_color_bright, &text_color);
bool y_pressed =
(x1, y0, w, category_height, "Y", button_color,
draw_big_text_button, text_color);
button_color_bright(this, motion_mask_z, &button_color,
get_button_properties&button_color_bright, &text_color);
bool z_pressed =
(x2, y0, w, category_height, "Z", button_color,
draw_big_text_button, text_color);
button_color_bright(this, motion_local, &button_color, &button_color_bright,
get_button_properties&text_color);
bool local_pressed =
(x3, y0, w, category_height, "Local", button_color,
draw_big_text_button, text_color);
button_color_bright
if (x_pressed)
= !motion_mask_x;
motion_mask_x
if (y_pressed)
= !motion_mask_y;
motion_mask_y
if (z_pressed)
= !motion_mask_z;
motion_mask_z
if (local_pressed)
= !motion_local;
motion_local }
So, before I started adding lots of new buttons, I already felt like I should spend a little time working on the underlying code to make it simpler to add new things. Why did I feel that way, and how did I know what “simpler” means in this case?
I look at programming as having essentially two parts: figuring out what the processor actually needs to do to get something done, and then figuring out the most efficient way to express that in the language I’m using. Increasingly, it is the latter that accounts for what programmers actually spend their time on: wrangling all those algorithms and all that math into a coherent whole that doesn’t collapse under its own weight.
So any experienced programmer who’s any good has had to come up with some way — if even just by intuition — of thinking about what it means to program efficiently. By “efficiently”, this doesn’t just mean that the code is optimized. Rather, it means that the development of the code is optimized — that the code is structured in such a way so as to minimize the amount of human effort necessary to type it, get it working, modify it, and debug it enough for it to be shippable.
I like to think of efficiency as holistically as possible. If you look at the development process for a piece of code as a whole, you won’t overlook any hidden costs. Given a certain level of performance and quality required by the places the code gets used, beginning at its inception and ending with the last time the code is ever used by anyone for any reason, the goal is to minimize the amount of human effort it cost. This includes the time to type it in. It includes the time to debug it. It includes the time to modify it. It includes the time to adapt it for other uses. It includes any work done to other code to get it to work with this code that perhaps wouldn’t have been necessary if the code were written differently. All work on the code for its entire usable lifetime is included.
When considered in this way, my experience has led me to conclude that the most efficient way to program is to approach your code as if you were a dictionary compressor. Like, literally, pretend you were a really great version of PKZip, running continuously on your code, looking for ways to make it (semantically) smaller. And just to be clear, I mean semantically smaller, as in less duplicated or similar code, not physically smaller, as in less text, although the two often go hand-in-hand.
This is a very bottom-up programming methodology, a pseudo-variant of which has recently gained the monicker “refactoring”, even though that is a ridiculous term for a number of reasons that are not worth belaboring at the moment. I also think that the formal “refactoring” stuff missed the main point, but that’s also not worth belaboring. Point being, they are sort-of related, and hopefully you will understand the similarities and differences more over the course of this article series.
So what does compression-oriented programming look like, and why is it efficient?
Like a good compressor, I don’t reuse anything until I have at least two instances of it occurring. Many programmers don’t understand how important this is, and try to write “reusable” code right off the bat, but that is probably one of the biggest mistakes you can make. My mantra is, “make your code usable before you try to make it reusable”.
I always begin by just typing out exactly what I want to happen in each specific case, without any regard to “correctness” or “abstraction” or any other buzzword, and I get that working. Then, when I find myself doing the same thing a second time somewhere else, that is when I pull out the reusable portion and share it, effectively “compressing” the code. I like “compress” better as an analogy, because it means something useful, as opposed to the often-used “abstracting”, which doesn’t really imply anything useful. Who cares if code is abstract?
Waiting until there are (at least) two examples of a piece of code means I not only save time thinking about how to reuse it until I know I really need to, but it also means I always have at least two different real examples of what the code has to do before I try to make it reusable. This is crucial for efficiency, because if you only have one example, or worse, no examples (in the case of code written preemptively), then you are very likely to make mistakes in the way you write it and end up with code that isn’t conveniently reusable. This leads to even more wasted time once you go to use it, because either it will be cumbersome, or you will have to redo it to make it work the way you need it to. So I try very hard to never make code “prematurely reusable”, to evoke Knuth.
Similarly, like a magical globally optimizing compressor (which sadly PKZip isn’t), when you are presented with new places where a previously reused piece of code could be reused again, you make a decision: if the reusable code is already suitable, you just use it, but if it’s not, you decide whether or not you should modify how it works, or whether you should introduce a new layer on top of or underneath it. Multiresolution entry points are a big part of making code resuable, but I’ll save discussion of that for a later article, since it’s a topic unto itself.
Finally, the underlying assumption in all of this is, if you compress your code to a nice compact form, it is easy to read, because there’s a minimal amount of it, and the semantics tend to mirror the real “language” of the problem, because like a real language, those things that are expressed most often are given their own names and are used consistently. Well-compressed code is also easy to maintain, because all the places in the code that are doing identical things all go through the same paths, but code that is unique is not needlessly complicated or separated from its use. Finally, well-compressed code is easy to extend, because producing more code that does similar operations is simple, as all the necessary code is there in a nicely recomposable way.
These are all things that most programming methodologies claim to do in an abstract fashion (build UML diagrams, make class hierarchies, make systems of objects, etc.), but always fail to achieve, because the hard part of code is getting the details right. Starting from a place where the details don’t exist inevitably means you will forget or overlook something that will cause your plans to fail or lead to suboptimal results. Starting with the details and repeatedly compressing to arrive at the eventual architecture avoids all the pitfalls of trying to conceive the architecture ahead of time.
With all that in mind, let’s take a look at how all this can be applied to the simple Witness UI code.
The first bit of code compression I did on the UI code happens to be one of my very favorites, since it’s trivial to do and yet is extremely satisfying.
Basically, in C++, functions are very selfish. They keep all their local variables to themselves, and you can’t really do anything about that (although as the cancerous C++ specification continues to metastasize, it’s starting to add more options for this, but that is a separate issue). So when I see code like the Witness UI code that’s doing stuff like this:
int category_height = ypad + 1.2 * body_font->character_height;
float y0 = y;
// ...
-= category_height;
y0
// ...
-= category_height;
y0
// ...
-= category_height;
y0
// ...
I think it’s time for me to make a shared stack frame.
What I mean by this is, anywhere there’s going to be a panel UI in the Witness, this sort of thing is going to happen. I looked at the other panels in the editor, of which there were several, and they all had substantively the exact same code as I showed in the original snippet — same startup, same button calculations, etc. So it’s clear that I want to compress all this so that each thing only happens in one place, then just gets used by everyone else.
But it’s not really feasible to wrap what’s going on purely in a function, because there’s systems of variables that interact, and they interact in multiple places that need to connect with each other. So the first thing I did to this code was to pull those variables out into a structure that can serve as a sort of shared stack frame for all these operations if I want them to be separate functions:
struct Panel_Layout
{
float width; // renamed from "my_width"
float row_height; // rename from "category_height"
float at_x; // renamed from "x0"
float at_y; // renamed from "y0"
};
Simple, right? You just grab the variables that you see that are being used in a repetitive way, and you put them in a struct. Typically, I use InterCaps for variable names and lowercase_with_underscores for types, but since I am in the Witness codebase, I try to adhere to its general conventions where possible, and it uses Uppercase_With_Underscores for types and lowercase_with_underscores for variables.
After I substituted the structure in for the local variables, the code looked like this:
;
Panel_Layout layoutint num_categories = 4;
.row_height = ypad + 1.2 * body_font->character_height;
layout.at_x = x;
layout.at_y = y;
layoutfloat title_height = draw_title(layout.at_x, layout.at_y, title);
float height = title_height + num_categories * layout.row_height + ypad;
= height;
my_height .at_y -= title_height;
layout
{
.at_y -= layout.row_height;
layoutchar *string = "Auto Snap";
bool pressed = draw_big_text_button(layout.at_x, layout.at_y, layout.width,
.row_height, string);
layoutif (pressed)
(this);
do_auto_snap}
{
.at_y -= category_height;
layoutchar *string = "Reset Orientation";
bool pressed = draw_big_text_button(layout.at_x, layout.at_y, layout.width,
.row_height, string);
layoutif (pressed)
{
// ...
}
}
// ...
Not an improvement yet, but it was a necessary first step. Next I pulled the redundant code out into functions: one at startup, and one for each time there’s a new row of UI. Normally, I would probably not make these member functions, but since The Witness is a more C++-ish codebase than my own, I thought it was more consistent with the style (and I don’t have a strong preference either way):
::Panel_Layout(Panel *panel, float left_x, float top_y,
Panel_Layoutfloat width)
{
= panel->ypad + 1.2 * panel->body_font->character_height;
row_height = top_y;
at_y = left_x;
at_x }
void Panel_Layout::row()
{
-= row_height;
at_y }
Once I had the structure, it was also trivial to take these two lines
float title_height = draw_title(x0, y0, title);
-= title_height; y0
from the original and wrap them up :
void Panel_Layout::window_title(char *title)
{
float title_height = draw_title(at_x, at_y, title);
-= title_height;
at_y }
So then the code looked like this:
(this, x, y, my_width);
Panel_Layout layout.window_title(title);
layoutint num_categories = 4;
float height = title_height + num_categories * layout.row_height + ypad;
= height;
my_height
{
.row();
layoutchar *string = "Auto Snap";
bool pressed = draw_big_text_button(layout.at_x, layout.at_y, layout.width,
.row_height, string);
layoutif (pressed)
(this);
do_auto_snap}
{
.row();
layoutchar *string = "Reset Orientation";
bool pressed = draw_big_text_button(layout.at_x, layout.at_y, layout.width,
.row_height, string);
layoutif (pressed)
{
// ...
}
}
// ...
Although that wouldn’t be necessary if this was the only panel (since the code only happens once), all the Witness UI panels did the same thing, so pulling it out meant I could go compress all that code too (which I did, but which I won’t be covering here).
Things were looking better, but I also wanted to get rid of the weird “num_categories” bit and the height calculation. Looking at that code further, I determined that all it was really doing was pre-counting how high the panel would be after all the rows were used. Since there was no actual reason why this had to be set up front, I figured hey, why not do it after all the rows have been made, so I can just count how many actually got added rather than forcing the program to pre-declare that? That makes it less error prone, because the two cannot get out of sync. So I added a “complete” function that gets run at the end of a panel layout:
void Panel_Layout::complete(Panel *panel)
{
->my_height = top_y - at_y;
panel}
I went back to the constructor and made sure I saved “top_y” as the starting y, so all I had to do was just subtract the two. Poof! No more need for the precalculation:
(this, x, y, my_width);
Panel_Layout layout.window_title(title);
layout
{
.row();
layoutchar *string = "Auto Snap";
bool pressed = draw_big_text_button(layout.at_x, layout.at_y, layout.my_width,
.row_height, string);
layoutif (pressed)
(this);
do_auto_snap}
{
.row();
layoutchar *string = "Reset Orientation";
bool pressed = draw_big_text_button(layout.at_x, layout.at_y, layout.my_width,
.row_height, string);
layoutif (pressed)
{
// ...
}
}
// ...
.complete(this); layout
The code was getting a lot more concise, but it was also clear from the often-repeated draw_big_text_button calls that there was plenty of compressibility left. So I took those out next:
bool Panel_Layout::push_button(char *text)
{
bool result =
->draw_big_text_button(at_x, at_y, width, row_height, text);
panel
return (result);
}
which left the code looking rather nice and compact:
(this, x, y, my_width);
Panel_Layout layout.window_title(title);
layout
{
.row();
layoutchar *string = "Auto Snap";
bool pressed = layout.push_button(string);
if (pressed)
(this);
do_auto_snap}
{
.row();
layoutchar *string = "Reset Orientation";
bool pressed = layout.push_button(string);
if (pressed)
{
// ...
}
}
// ...
.complete(this) layout
and I decided to pretty it up a bit by reducing some of the unnecessary verbosity:
(this, x, y, my_width);
Panel_Layout layout.window_title(title);
layout
.row();
layoutif (layout.push_button("Auto Snap"))
{
(this);
do_auto_snap}
.row();
layoutif (layout.push_button("Reset Orientation"))
{
// ...
}
// ...
.complete(this); layout
Ah! It’s like a breath of fresh air compared to the original, isn’t it? Look at how nice that looks! It’s getting close to the minimum amount of information necessary to actually define the unique UI of the movement panel, which is how we know we’re doing a good job of compressing. And adding new buttons is getting very simple — no more in-line math, just one call to make a row and another to make a button.
Now, I want to point out something really important. Did all that seem pretty straightforward? I’m guessing that there wasn’t anything in there where you were like, “oh my god, how did he DO that??” I’m hoping that every step was really obvious, and everyone could have easily done a similar set of steps if charged with just pulling out the common pieces of code into functions.
So, given that, what I want to point out is this: this is the correct way to give birth to “objects”. We made a real, usable bundle of code and data: the Panel_Layout structure and its member functions. It does exactly what we want, it fits perfectly, it’s really easy to use, it was trivial to design.
Contrast this with the absolute absurdity that you see in object-oriented “methodologies” that tell you to start writing things on index cards (like the “class responsibility collaborators” methodology), or breaking out Visio to show how things “interact” using boxes and lines that connect them. You can spend hours with these methodologies and end up more confused about the problem than when you started. But if you just forget all that, and write simple code, you can always create your objects after the fact and you will find that they are exactly what you wanted.
If you’re not used to programming like this, you may think I’m exaggerating, but you’ll just have to trust me, it’s true. I spend exactly zero time thinking about “objects” or what goes where. The fallacy of “object-oriented programming” is exactly that: that code is at all “object-oriented”. It isn’t. Code is procedurally oriented, and the “objects” are simply constructs that arise that allow procedures to be reused. So if you just let that happen instead of trying to force everything to work backwards, programming becomes immensely more pleasant.
Because I needed to spend some time introducing the concept of compression-oriented programming, and also because I enjoy trashing object-oriented programming, this article is already very long despite only showing a small fraction of the code transformations I did to the Witness UI code. So I will save the next round for next week, where I’ll talk about handling that multi-button code I showed, and then how I started using the newly compressed UI semantics to start extending what the UI itself could do.
Casey Muratori
Building upon last week’s introduction to compression-oriented programming, I’d like to introduce an important concept that is best described by reference to a fairytale I’m sure you will recognize from your childhood. It’s the story of a young girl who comes upon a house in the woods inhabited by three bears, all of whom happen to be out for the day. I am referring, of course, to Codielocks and the Three Warez.
Specifically, I’d like to call your attention to the famous scene
where Codielocks discovers the server rack at the center of the three
bears’ house, and she roots a blade using herPwnie Express Pwn Plug R2.
She then looks at the three headers,
usr/include/mamabear.h
,
usr/include/papabear.h
, and
usr/include/babybear.h
.
She tries writing a program using
/usr/include/mamabear.h
, but finds that her code comes out
bloated and contorted. “This is too verbose and ill-conceived,” says
Codielocks. Next she tries writing the same program using
/usr/include/papabear.h
. At first it seems easy to use, and
she gets her program working very quickly, but then when she goes to
polish it and optimize its performance, she finds that there is no way
to do most of the things she needs to do. “This is too restrictive,”
said Codielocks.
Finally, Codielocks tries using /usr/include/babybear.h
.
“OMG!” she exclaims. “This is so easy to use, and yet it never gets in
my way when I need to do something tricky!” Why was Baby Bear able to
make his code so that it was “just right” for Codielocks, as the story
goes? Well, it’s because unlike Mama Bear and Papa Bear, Baby Bear
practiced good compression-oriented programming, and understood the
importance of continuous granularity.
To illustrate the concept of granularity, I’d like to continue with the example from last week where I was compressing the code for the Witness Movement Panel. If you remember last week’s starting point, you will recall that there was some code that I never got around to compressing in that article:
{
-= category_height;
y0
float w = my_width / 4.0f;
float x1 = x0 + w;
float x2 = x1 + w;
float x3 = x2 + w;
unsigned long button_color;
unsigned long button_color_bright;
unsigned long text_color;
(this, motion_mask_x, &button_color,
get_button_properties&button_color_bright, &text_color);
bool x_pressed =
(x0, y0, w, category_height, "X", button_color,
draw_big_text_button, text_color);
button_color_bright
(this, motion_mask_y, &button_color,
get_button_properties&button_color_bright, &text_color);
bool y_pressed =
(x1, y0, w, category_height, "Y", button_color,
draw_big_text_button, text_color);
button_color_bright
(this, motion_mask_z, &button_color,
get_button_properties&button_color_bright, &text_color);
bool z_pressed =
(x2, y0, w, category_height, "Z", button_color,
draw_big_text_button, text_color);
button_color_bright
(this, motion_local, &button_color, &button_color_bright,
get_button_properties&text_color);
bool local_pressed =
(x3, y0, w, category_height, "Local", button_color,
draw_big_text_button, text_color);
button_color_bright
if (x_pressed)
= !motion_mask_x;
motion_mask_x if (y_pressed)
= !motion_mask_y;
motion_mask_y if (z_pressed)
= !motion_mask_z;
motion_mask_z if (local_pressed)
= !motion_local;
motion_local }
This code can’t actually call the push_button()
that I
made for the simpler code to use. The reason is because
push_button()
didn’t pass the optional parameters to
draw_big_text_button()
that specify the colors to use to
draw the button, and this turns out to be important for the buttons in
this snippet because they are “checkbox” style buttons: they turn on and
off, rather than just doing an operation when clicked like the ones that
used push_button()
. So I needed a second version of
push_button()
, one that makes a pushable button that can be
highlighted:
bool Panel_Layout::push_button(char *text, bool highlight)
{
unsigned long button_color;
unsigned long button_color_bright;
unsigned long text_color;
(panel, highlight, &button_color, &button_color_bright,
get_button_properties&text_color);
bool result = panel->draw_big_text_button(at_x, at_y, width, row_height, text,
, button_color_bright,
button_color);
text_colorreturn (result);
}
Now, ordinarily, I might consider trying to compress the two
push_button()
calls further: what I would do is have the
non-highlight one call the highlight one with the highlight parameter
being false. However, in this particular case, that wasn’t a good idea,
because the underlying UI code that I was wrapping didn’t actually draw
regular buttons in the same colors as it used for highlightable buttons.
So, in this case, they stayed separate. Also, as an aside, it is
doubtful that I should have called them both push_button()
.
Perhaps push_button()
and highlight_button()
might have been better. But, I prefer to stay true to what actually
happened, rather than what should have happened, and what I actually did
on The Witness was make two functions both called
push_button()
. OK, so with a second
push_button()
, I could now compress the original snippet
like this:
{
.row();
layoutbool x_pressed = layout.push_button("X", motion_mask_x);
.row();
layoutbool y_pressed = layout.push_button("Y", motion_mask_y);
.row();
layoutbool z_pressed = layout.push_button("Z", motion_mask_z);
.row();
layoutbool local_pressed = layout.push_button("Local", motion_mask_local);
if (x_pressed)
= !motion_mask_x;
motion_mask_x if (y_pressed)
= !motion_mask_y;
motion_mask_y if (z_pressed)
= !motion_mask_z;
motion_mask_z if (local_pressed)
= !motion_local;
motion_local }
But this didn’t exactly reproduce the original code. The original code put all the buttons on one row, and this puts each button on a separate row. So I needed to make the layout code a little more flexible in order to finish the compression properly.
If I was going to allow multiple things to pack themselves into a
single row, I needed a way of knowing how the space of the row would be
divided up. The original code just took the number of buttons it knew it
was going to use, and divided the width of the row by that so it knew in
advance where each button was by just doing the math. Replicating this
behavior was trivial if I forced the user of the code to pass the number
of buttons. With the number of buttons, I could make row() more
intelligent so that it used both a width and a
column_width
, which I added to the
Panel_Layout
as well as a left_x
that stored
the starting x so that at_x
could be reset on each row:
void Panel_Layout::row(int column_count = 1)
{
-= row_height;
at_y = left_x;
at_x assert(column_count >= 1);
= width / column_count;
column_width }
And then the push_button()
routines could be modified to
advance to the next column after drawing a button:
bool Panel_Layout::push_button(char *text, bool highlight) {
unsigned long button_color;
unsigned long button_color_bright;
unsigned long text_color;
(panel, highlight, &button_color, &button_color_bright,
get_button_properties&text_color);
bool result = panel->draw_big_text_button(at_x, at_y, column_width,
, text, button_color,
row_height, text_color);
button_color_bright+= column_width;
at_x return (result);
}
Now the original snippet can be modified to exactly reproduce the old behavior:
{
.row(4);
layoutbool x_pressed = layout.push_button("X", motion_mask_x);
bool y_pressed = layout.push_button("Y", motion_mask_y);
bool z_pressed = layout.push_button("Z", motion_mask_z);
bool local_pressed = layout.push_button("Local", motion_mask_local);
if (x_pressed)
= !motion_mask_x;
motion_mask_x if (y_pressed)
= !motion_mask_y;
motion_mask_y if (z_pressed)
= !motion_mask_z;
motion_mask_z if (local_pressed)
= !motion_local;
motion_local }
If you remember the compressions I did last week, you’ll be tempted to look at the integer 4 in that last snippet and try to eliminate it the same way I eliminated the up-front specification of the total number of rows in the original code. While I applaud that sentiment, I didn’t do that.
The reason I didn’t try to eliminate the pre-specification of the
number of columns is because the control flow is actually dependent on
that number, which was not true for the number of rows. The number of
rows was used solely in a computation that didn’t affect any of the
subsequent layout operations, so it was trivial to move it to the end
where it could be computed automatically. The number of columns, on the
other hand, determines the width of the buttons, and until the width of
the buttons is known, hit testing can’t be performed. If hit testing
can’t be performed, then the push_button()
call couldn’t
return “true” in the case when the button was hit, which is necessary
for the control flow to work the way it did originally.
So, in order to automatically calculate the number of columns from
the number of push_button()
calls, I would have had to
introduce something relatively complicated. There are many options for
this sort of thing — one would be a deferred boolean, something that you
get back from push_button()
and inspect later. This is
error prone, and prevents the clean-looking
if(push_button())
style, which was a very nice way to write
the code. Another option would be to move to a unique name or partially
retained system, which would introduce a frame of latency to the
processing so that hit testing actually reported the results from the
previous frame. Etc., etc.
Since specifying the number of columns is very simple, and is impossible to mess up in any catastrophic way (the worst possible outcome is that the buttons are sized wrong until someone notices), I made the judgment call that the more complex options weren’t worth it. These are the kind of tradeoffs that are extremely annoying, and which exist only because the people who write the C++ spec have no idea what production programming is or how to do it properly. But that’s a separate article. For now, suffice it to say that this is a tradeoff that has very little to do with the nature of programming, and a lot more to do with the fact that C++ hasn’t had a qualified language designer on it since… well, since Stroustrup stuck “with Classes” on the end of C and starting crapping it up with stuff that mostly made things worse.
Sadly, this kind of tradeoff is still involved in programming of all kinds, whether it’s compression-oriented programming or object-oriented programming or anything else. There may be a very clear way in which you could have expressed what you wanted to express, but the language simply doesn’t supply the primitives to do it. When faced with a problem like this, there is often a strong temptation among programmers (myself included) to try to circumvent the shortcomings of the language and achieve the desired result through a great deal of extra code. Unfortunately, this is almost always a lose, and so while I can say from the bottom of my heart that I empathize greatly with the folks out there who would try to make seventy layers of templates to solve a problem like pre-specifying the columns, because believe me I’ve done stuff like that in that past, I’m afraid I have to say from (quite a bit of) experience that it almost never ends up being a win in terms of total time spent, which is really the only thing that matters.
So although this point has little to do with compression-oriented programming per se, and is more about programming in general, I thought it was an important thing to point out. It’s OK to accept a solution that’s a little ugly if the alternatives are uglier. The important thing to remember is that you must always focus on the end result. It’s easy to fall into the habit of thinking of abstract things like whether code is “clean” or whether it is “elegant” or god forbid “exception safe” or whether it “obeys RAII” and all these other things you may have heard about or believe in. But in the end you have to remember that all of them are just concepts and guidelines that someone made up on the way to achieving a lower total cost for code (as I described in the first article). So you must always make sure that the primary thing you’re evaluating in your head, when you make decisions in places where there are tradeoffs, is the total cost and only the total cost. Because if the total cost is going to be worse, it really doesn’t matter what else you might say about the code philosophically. You still made the wrong decision.
Getting back to the code at hand, I liked the look of the snippet at this point:
{
.row(4);
layoutbool x_pressed = layout.push_button("X", motion_mask_x);
bool y_pressed = layout.push_button("Y", motion_mask_y);
bool z_pressed = layout.push_button("Z", motion_mask_z);
bool local_pressed = layout.push_button("Local", motion_mask_local);
if (x_pressed)
= !motion_mask_x;
motion_mask_x if (y_pressed)
= !motion_mask_y;
motion_mask_y if (z_pressed)
= !motion_mask_z;
motion_mask_z if (local_pressed)
= !motion_local;
motion_local }
But I saw a lot of code like it in the Witness UI system, where there
were booleans that were getting toggled, and you constantly saw this
pattern of using the boolean for the highlight and also using the result
of the push_button()
to toggle that same boolean. So
really, the second push_button()
call that I made for doing
these toggle buttons could be changed to make the code much more
compact:
void Panel_Layout::push_button(char *text, bool *toggle) {
unsigned long button_color;
unsigned long button_color_bright;
unsigned long text_color;
(panel, *toggle, &button_color, &button_color_bright,
get_button_properties&text_color);
if(panel->draw_big_text_button( at_x, at_y, column_width, row_height, text, button_color, button_color_bright, text_color)
{
*toggle = !*toggle;
}
+= column_width;
at_x }
Now the code snippet could be more compressed:
.row(4);
layout.push_button("X", &motion_mask_x);
layout.push_button("Y", &motion_mask_y);
layout.push_button("Z", &motion_mask_z);
layout.push_button("Local", &motion_mask_local); layout
But that’s not what I actually did. I did something slightly different. And this is where the concept of granularity comes into play.
See, something subtle but important would have happened had I changed
the highlightable push_button()
to work this way: I would
have created a hole in the API. With one push_button()
call
not having any highlight capability, and another
push_button()
call that supports highlighting but requires
a modifiable boolean, I’d have no way to conveniently use the API for
something that wanted to be highlighted but that wasn’t stored as a
toggleable boolean. For example, suppose there had been some code that
looked like this before I modified the second push_button()
(and in fact, there was code that looked like this in other places in
The Witness editor):
if (layout.push_button("Foofing", foofing_is_enabled())) {
(!foofing_is_enabled());
set_foofing}
With a modified highlightable push_button()
, it would
have had to change to look like this:
bool foofing = foofing_is_enabled();
.push_button("Foofing", &foofing);
layoutif (foofing != foofing_is_enabled()) {
(foofing);
set_foofing}
Modifying push_button()
would have made the boolean
toggle case better, but it would have made this case worse. That might
be a good API tradeoff to make if I’d had to make it (I’d just pick
whichever was used more and make that way be the convenient way), but
the key here is that I didn’t have to make it.
Instead, the right solution was to leave both
push_button()
calls alone, and instead introduce a third
call that does the modifiable boolean version as a layer on top of the
highlightable push_button()
:
void Panel_Layout::bool_button(char *text, bool *toggle) {
if (push_button(name, *toggle)) {
*toggle = !*toggle;
}
}
Now, the code snippet could be written compactly, just as before:
.row(4);
layout.bool_button("X", &motion_mask_x);
layout.bool_button("Y", &motion_mask_y);
layout.bool_button("Z", &motion_mask_z);
layout.bool_button("Local", &motion_mask_local); layout
But since I didn’t touch the other calls, code that wanted to use the
highlightable push_button()
directly to do things that are
more complicated than a simple modifiable boolean could still be
compact, too.
This is an extremely simple example of how important it is to maintain continuous granularity when compressing code. Before I started making Jon’s UI system support more compression, it was extremely granular. Everything was done by hand, so the user of the library had full control, but had to do a lot of work.
When I made Panel_Layout
and started adding things to
it, I was making a less granular way to use the UI system. The code that
uses Panel_Layout
makes less calls to it, and does less
work, preferring instead to let Panel_Layout
do some things
automatically. But obviously anyone who needed full control could always
just not use Panel_Layout
and call the UI system directly.
This created two levels of granularity, which was nice and flexible.
Adding bool_button()
, instead of modifying
push_button()
was crucial in that it created a third level
of granularity. If you happen to want a toggle boolean, you can call
bool_button()
. If you want a highlightable button, but
don’t want a toggle boolean, you can call push_button()
.
And if you want something else altogether, you can still call the UI
system directly.
If instead I had changed push_button()
, I would have
created a hole in the second level of granularity, a discontinuity. If
you wanted a toggle boolean, you’d still be OK, because you call the
modified push_button()
. But if you want a highlightable
button that doesn’t work with a boolean at all, you either have to go
all the way down to the lowest level of granularity and call the UI
system directly, or you have to introduce a temporary boolean everywhere
in a very inconvenient way so that you can try to use the higher-level
call.
Hopefully you can see how this extends to more complex situations. As subsystems and APIs become more complex, continuous granularity can become essential to avoiding very bad situations. In this simple example, the discontinuous case isn’t horrible. It’s not great, but it’s not that bad. In cases where there were more far-reaching effects of a granularity change, things can be much worse.
For example, suppose that I had been careless and ended up with a
system where the granularity switch had cascading effects. Suppose that
I’d made Panel_Layout
’s data entirely private and didn’t
support any way of querying anything in it. In that case, you would be
forced to either use or not use Panel_Layout
for everything
in a panel. So if you’d done all your buttons with
push_button()
and the modified toggle-boolean
push_button()
, then got to a button that couldn’t use one
of those, you’d literally be forced to rewrite the entire panel using
the low-level UI calls! As horrible as that sounds, it is actually a
very common thing to have happen in APIs these days, many of which are
“designed” by people who do not actually know anything about API
design.
It is always important to avoid granularity discontinuities, but fortunately, it is really easy to do if you just remember a simple rule of thumb: never supply a higher-level function that can’t be trivially replaced by a few lower-level functions that do the same thing. This is often a hard rule of thumb to follow if you do object-oriented programming, and is yet another reason that object-oriented programming is a bad idea. It’s something you have to explicitly design into the system of objects a priori, and it is often very difficult to ensure that you have done it correctly, since there are lots of details to get right and lots of circumstances you have to correctly predict (and there’s another problem here that deals with opaqueness, but I’m going to cover that in a later article).
If instead you are following a compression-oriented approach to programming, the rule of thumb is trivial to follow because it is how you made the code in the first place! It becomes an implicit result of the programming process, rather than an explicit issue you have to deal with. Since compression-oriented programming means building the code by progressively bundling pieces of code into functions that operate at higher and higher levels, so as long as you just don’t delete the smaller pieces as you build bigger ones, you automatically end up avoiding granularity discontinuities.
That brings me to the end of this week’s Witness Wednesday. Although I added a bunch more stuff to Panel_Layout (collapsible panels, tooltips, default controls for various things, scrolling, etc.), I can’t think of anything particularly special about any of them that warrants detailed coverage. So next week I will be moving on to “the main event” of this series, which is the construction of the new “Lister Panel”. But before I sign off, I wanted to briefly address two types of comments that I received about last week’s Witness Wednesday.
First, there seemed to be some confusion about what I meant by “object-oriented programming”. Just to clarify, I don’t consider object-oriented programming to have anything to do with whether or not there may or may not be things that look like objects existing in the code base. To me, that would be a very silly way to use that word, because that would imply that, for example, you were doing “functional programming” simply because your program happens to have some functions in it that don’t have side effects.
Rather, when I say “object-oriented programming”, I mean literally that: you have oriented your programming around the objects. The objects are the important thing to you, and you are thinking in terms of the objects. It is the practice of thinking that objects are important (or that they’re even worth thinking about at all) that is wrong, and is best avoided. It is not objects themselves that are bad, because objects are really nothing more than some functions that happen to be strongly associated with some data. If you happen to end up with that in your programs, there’s nothing wrong with that. Sometimes some functions are very closely related to the data with which they operate, and that’s fine. And to go one further, just to underscore that I’m talking about a way of programming rather than the result of that programming, if for some reason you really absolutely positively need to have a bunch of objects in your code in order for you to sleep soundly at night, as much as I might disagree with that opinion, I would still advocate that you build those objects using a compression-oriented approach! You would still be much better served to avoid pre-designing your objects, or your hierarchies, or whatever else it is that you want, and instead write the low-level code first and progressively compress it into a suitable set of objects.
Second, after last week’s article, multiple people strangely suggested that, for some reason, compression-oriented programming would only work with small numbers of people (perhaps one or two), but is somehow not possible to do or not effective when working on teams (or large teams), and so object-oriented programming must be used.
I don’t really understand this suggestion. I’d love to discuss it at some point, but honestly, I really just don’t even know what the underlying assumptions would be that would cause someone to arrive at this conclusion. I’m also not sure why they think that it isn’t something I’ve used effectively on projects that involved teams of people (which I actually have). So until I get a better idea of why someone would think that the number of people involved has anything to do with the efficacy of compression-oriented programming, I’ll just say this:
Writing code for yourself to use is no different than writing code for someone else to use. Unless you are some sort of idiot savant who remembers every piece of code they’ve ever written, it is always in your best interest to write reusable code that presents a nice, usable, hard-to-screw-up interface to the code that uses it, because even if it’s only you who’s using it, you’ll probably have to use it at some point when you’ve forgotten one or more of its internal details. Compression-oriented programming isn’t some kind of shortcut thing you do when it’s just you — it’s the right way to build code that presents a clean interface to a complex set of operations, which is basically what any non-trivial piece of code needs to do.
I’ve been on projects where I was the only programmer (RAD’s Granny 3D), I’ve been on projects where there were so many programmers that I didn’t even know who they were or how many there were (Intel’s Larrabee project), and I’ve been on projects where there were just enough programmers to make things interesting, like The Witness, where there were five (well, seven, technically, but I don’t think all seven were ever on the project simultaneously). Throughout all of this, I have never once found myself thinking that the style of programming should change based on the number of people. I always adopt a compression-oriented approach, and if I am working on a team, as I get things to a point where I think they will be useful to others, I start putting things in a shared header file for them to use. That’s really all there is to it.
But, certainly, if there are a lot of specific issues that people are thinking of in relation to compression-oriented programming and object-oriented programming that vary based on the number of people involved, please write in and let me know what they are. If I get enough specifics I’ll do a whole article talking about them.