No, it's not modern art. It's not a photonegative scan of a bunch of text. It's a *free* DOS app! Put your credit card back in your fanny pack and just hit download...
Introduction
If programming languages were musical artists or bands, what would the music be in the software world? First off, the pervasive Java would be U2. It's everywhere and everyone kind of likes it. C# would be the Rolling Stones, for similar reasons. JavaScript/AJAX would be the new school hip hop -- overplayed. LISP, the language that has a trippy level of self awareness, would be the Grateful Dead or Phish. The entire VB language has been visually altered numerous times, and the end result is much less attractive than the original BASIC. There's a platoon of musicians that map to VB. Fortran is Elvis. Befunge is RadioHead, post-2000. COBOL is the past his prime musician that keeps releasing albums. Let's not name any names!
Although XML is not really a programming language (unless you like to hand-code ANT), I am being convinced by the reptilian parts of my brain to mention it. How did it ever become popular, and why is it still in use? It's like the boy band phenomenon. It didn't start out with a lot of potential (it's SLOW to parse), and it ain't gonna get any better!
What about C and C++? C can be verbose. It's followers tend to want it to stay complex. It can be fun (like when we wrap in C++), but we do things to make it less fun (like keep things in structures and procedures). C is Rush, the masters of "math rock". I'm an old fan and an old C programmer, so I can say this with impunity. In fact, I'm just old, period. The C culture of complexity carries over into C++, and we have additional new code smells to suck all the joy and efficiency out of coding, like template madness and multiple inheritance. How can we make C++ usable to a wider audience? We can start with how we code! We can create C++ code outside of the math rock genre and push it into mass appeal. There are things we can do with code, even under the current C++ standard, to make C++ the Beatles of the software world.
In this article, I will zero in on one programming language concept that makes code more intuitive, and I'll show how to implement it in C++, where it's not directly supported.
Background
I switch between a lot of languages in my work. They all have their pluses and minuses. I always come to the same conclusion when evaluating the merits of a language: the bottom line is intuitiveness. The completeness of the available APIs are important, but APIs can be built around any programming language. So for the purpose of this discussion, I'll stick to intuitiveness or readability as the primary objective. Can you show a block of code to your boss and feel confident that he/she can tell what it does? This depends a lot on the language grammar. Show the boss a block of SQL, and you will have to explain it. Show a block of Java, and it'll probably make some sense to a non-technical audience.
Before I start gushing about a feature of C#, let me first express my concern about the direction the language is taking under the guidance of its one member steering committee. I understand the need for features like lambdas
, but in any language, my tiny little brain struggles to follow the logic in the code. I'll deal with lambdas
since they seem worthwhile, but it could be what opens the lid to Pandora's box. I'm concerned that Microsoft is going to throw the kitchen sink into the language and promote an ever growing suite of keywords and flow logic that mere humans can't visualize under the 4 dimensional limit. Using my criterion that languages should be intuitive, I just don't see things like Linq
inline queries and the new yield
keyword in C# as progress. The underlying Linq
technology is cutting edge, but it can be used in full without the muddy syntax. Why make C# code look like SQL? It's kind of like a mash-up of the Rolling Stones and thrash metal!
Enough rant. I carry a joyful message I want you to save away in the leftmost node of your brain. There is a language feature in C# that I consider the single largest improvement in that language family since C became C++ or Java became, well, Java. Accessors are something we take for granted now in C#. They're like the bass guitarist -- in the background and underappreciated. What would the music be like without the bassist? It would be less pleasing to the ear. Likewise, classes in languages without real accessors are less pleasing to the eye. In my opinion, accessor-less classes are actually harder to use. Why? Accessors distinguish between property and method, and autocomplete features give you different icons. What is the difference between a property and a method in a class? A property is a way to set and get an attribute on a instance. It's more noun than verb. A method is more of an action, and calling an action implies that more work will happen. If the class is Cup
, then LiquidVolume
is a property and fill
is a method. LiquidVolume
is atomic, so calling it involves a very small cost. Calling fill
requires more work by the Cup
instance or the CoffeePot
filling it up.
Since we're talking about math rock, let's stick with math-heavy code. Here's a snippet in C# and Java of our imaginary Cup
class:
class Cup
{
double _diameter,
_liquidHeight;
public double LiquidVolume {
get {
double radius = _diameter / 2.0;
return 3.14159 * (radius * radius) * _liquidHeight;
}
}
public double LiquidHeight
{
get {
return _liquidHeight;
}
set {
_liquidHeight = value;
}
}
public void fill(CoffeePot pot, double pouringTime, double outputVelocity)
{
}
}
Cup cup = new Cup();
cup.LiquidHeight = 2;
cup.Diameter = 2;
cup.fill(pot, 100, 50);
Trace.WriteLine( cup.LiquidVolume.ToString() );
class Cup
{
double _diameter,
_liquidHeight;
public double getLiquidVolume()
{
double radius = _diameter / 2.0;
return 3.14159 * (radius * radius) * _liquidHeight;
}
public double getLiquidHeight()
{
return _liquidHeight;
}
public void setLiquidHeight(double value)
{
_liquidHeight = value;
}
public void fill(CoffeePot pot, double pouringTime, double outputVelocity)
{
}
}
Cup cup = new Cup();
cup.setLiquidHeight(2);
cup.setDiameter(2);
cup.fill(pot, 100, 50);
System.out.println( cup.getLiquidVolume().ToString() );
The classes in the snippet illustrate the difference between Java and C# with regard to properties. I don't play Sun versus Microsoft, so take these comments in the proper context. The C# property accessors are an improvement to the language family. It clearly makes code more readable when you have visual cues. This is something every programmer knows already; readability is why we indent and create coding standards.
Note that I follow a non-Microsoft naming convention for classes, methods, and properties. Upper case words in English and similar languages usually denote a noun (like Chris). In German, all nouns begin capitalized. Therefore, it makes sense for classes and properties to be in upper case. Putting methods in lower case, in direct contrast with coding standards used by Microsoft, is a way to distinguish nouns from verbs or actions from attributes. I don't worry about this with local variables, and I've never seen any need when I keep functions short.
The snippet also illustrates that accessors allow you to drop the () when calling a property. This lets the code reviewer to sort out nearly "atomic" operations from those that require more CPU cycles. This may not sound important, but in a loop, it's a critical distinction. You can call a property get
accessor from inside a loop, but you should save the result of a heavier method call to a local variable.
Enough About Java and C#! What about C++?
If you program out of a textbook, it's not at all obvious that you can create classes in C++ with accessor syntax exactly like that of C#. I guess few people have ever seen the need, because I've never seen any APIs designed this way. It's actually really straightforward in C++ to support accessor syntax. It's just a bit more verbose than what you do in C#! That's OK for us math rockers. The basic ingredient is using instances of inner classes with overloaded operators. The inner classes are the "accessor" code, and the overloaded operators make the desired syntax possible.
The sample application is a DOS harness around a Trigonometry
class. It illustrates how to build accessor classes and how to call into them. Here's the guts of the source code:
#include "stdafx.h"
#include <math.h>
#include <stdlib.h>
#include <assert.h>
#include <iostream>
using namespace std;
const double PI = 3.141592653589793238462643,
TWOPI = 2 * PI,
DEGREE = PI / 180.0;
class Trigonometry
{
public:
Trigonometry()
: Sine(_radians), AngleInDegrees(_radians),
Radians(_radians), _radians(0.0)
{
}
protected:
double _radians;
static bool approximatelyEqual(double d1, double d2)
{
return fabs( d1 - d2 ) <= 0.0001;
}
static void setRadians(double &target, const double newVal)
{
target = fmod(newVal, TWOPI);
while( target < 0 )
target += TWOPI;
}
class RadianAccessor
{
double &_target; public:
RadianAccessor(double& target)
: _target(target)
{
}
operator double () const
{
return _target;
}
double operator = (double newVal)
{
setValue(newVal);
return value();
}
double operator ++(){
setValue(_target + 1.0);
return _target;
}
bool operator == (double d) const
{
return approximatelyEqual( d, value() );
}
protected:
double value() const
{
return _target;
}
void setValue(double newVal)
{
setRadians(_target, newVal);
}
};
class AngleAccessor
{
double &_target; public:
AngleAccessor(double& target)
: _target(target)
{
}
operator double () const
{
return value();
}
double operator = (double newVal)
{
setValue(newVal);
return value();
}
double operator ++(){
setValue(value() + 1);
return value();
}
double operator +=(double addend){
setValue(value() + addend);
return value();
}
bool operator == (double d) const
{
return approximatelyEqual( d, value() );
}
protected:
double value() const
{
double res = _target / DEGREE;
return res < 0 ? res + 360 : res;
}
void setValue(double newVal)
{
setRadians(_target, newVal * DEGREE);
}
};
class SineAccessor
{
double &_target; public:
SineAccessor(double& target)
: _target(target)
{
}
operator double () const
{
return value();
}
double operator = (double newVal)
{
setValue(newVal);
return value();
}
double operator ++(){
setValue(0.1 + sin(_target));
return value();
}
bool operator == (double d) const
{
return approximatelyEqual( d, value() );
}
protected:
double value() const
{
return sin(_target);
}
void setValue(double newVal)
{
if( newVal > 1.0 )
newVal = 1.0;
else if( newVal < -1.0 )
newVal = -1.0;
setRadians(_target, asin(newVal));
}
};
public:
SineAccessor Sine;
AngleAccessor AngleInDegrees;
RadianAccessor Radians;
};
int _tmain(int argc, _TCHAR* argv[])
{
Trigonometry trig;
trig.Sine = -1.0;
assert( trig.AngleInDegrees == 270.0 );
while( trig.Sine < 1.0 ){
cout << "sin(" << trig.Radians << ") = " << trig.Sine << endl;
++trig.Sine;
}
cout << "Press ENTER to continue or just wait for your computer to crash:
" << endl;
getchar();
trig.AngleInDegrees = 630.0; assert( trig.AngleInDegrees == 270.0 ); for( int n=0; n < 72; n++ ){
cout << trig.AngleInDegrees << "Degrees => " << trig.Radians
<< " Radians" << endl;
trig.AngleInDegrees += 10;
}
cout << "Press ENTER to continue. Don't worry; it won't burn your fingers: "
<< endl;
getchar();
return 0;
}
Points of Interest
Admittedly, the accessor class code is a bit long, especially when you start overloading binary operators. You cannot support the unary and binary operations without overloading those operators. I only implemented a couple of operators here for brevity. So why even bother writing accessor classes?
It's a good thing to contemplate whether writing accessors is worthwhile in production code. There is one clear case in which I use this technique in class design -- APIs. If you're re-using code, it needs to be really intuitive how you use a class. Let me give an example. Today, I was working with an API for manipulating file and directory paths in C++. Simply looking at the autocompletion lists in the IDE was not good enough to tell me what function to call on an instance. The only way I could tell a class property from a method was to check for a corresponding 'set' function (almost all methods were const). I had to keep referring to the documentation, which basically expanded the function name into a sentence. Unless these classes are fresh in my memory, their use will slow my productivity. The corresponding classes in the .NET APIs are actually easier to use, not because they're better, but because of clear separation between noun and verb. I rarely need to reference the documentation when using the file and directory classes in C#.
There's a potentially interesting solution for generating C++ accessor classes. You can "metacode" your accessors in a language like Python, run the script as a pre-build step, and #include
the meta-generated C++ code. You can also embed scripting code in a #pragma
and write a separate preprocessor script to read, evaluate, and expand the #pragma
script inline. This allows you to mass produce the operators, filtering the list appropriately. It also lets you solve the syntax enigma thing once and for all with prefix and postfix operators -- postfix has a very interesting syntax.
Conclusion
Elegant, readable code makes us a better investment for the people who pay us. We become more productive when we re-use code that speaks to us. We don't have to stop and rewind a song when the lyrics are clear, and we don't have to kill time searching Google when the APIs we use are intuitive. We should seek ways to make the C++ language easier to use, especially when it doesn't require a new standard to do so. The language itself is just the bottom of the "code stack"; what you do on top of the language's grammar has a dramatic impact with readability. Hey, that's why we all indent, right? If you can do things to maximize the usefulness of autocompletion lists, you will save time from browsing documentation. Separate the nouns and verbs in your classes, and you will re-use those classes over and over. Readable code is like The Beatles' Greatest Hits -- it's going to be around for a very long time.
Rock on!
History