A few weeks ago I blogged about how to be a cool C# programmer. if you are a C++ programmer, you can be cool, too. The next C++ standard, dubbed C++0x promises many enhancements and cool abilities, but in this post I’ll stick with the current C++ standard. Here are some ways of being a cool C++ programmer (in no particular order):
1. Zeroing out a structure and setting its first field in one swoop
One of the common needs in a native Windows applications is setting up structures in preparation for calling some API function. Most of those structures need to be zeroed out, except the first field that needs to be set to the size of the structure. This is a typical Windows programming idiom that helps deal with versioning issues (if the structure grows in a subsequent release or service pack, the implementation can read only as many fields as are relevant).
We can do that with a call to memset followed by setting the relevant field name (usually called cb, cbSize or nLength) to the size of the structure. But there is a quicker, more elegant way, which, in fact, is part of the C standard (not even C++).
For example, instead of writing (in preparation for calling CreateProcess):
STARTUPINFO si;
::memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
You can write:
STARTUPINFO si = { sizeof(si) };
How cool is that?
2. Use const whenever possible
Many C++ developers know about const member functions – functions that declare they don’t change any member variables – but too few are actually using it. So, to be cool use it whenever possible, not only for the coolness, of course, but because it promotes a kind of “local immutability”, and more practically, allows const objects to execute these functions (and no others).
Using const (not just for member functions) is cool, too: For example, instead of:
char* greeting = "hello";
should be:
const char* greeting = "hello";
Which is not just cool, but more correct.
3. Use the mutable keyword where appropriate
Having said all that about const, there is a little known keyword called mutable that allows const member functions to change those member variables marked with mutable. This seems like a backdoor to circumventing const, and technically it is. It’s useful for distinguishing "physical const-ness” from “logical const-ness”. Sometimes they are not the same. Consider this simple code:
class Dictionary {
public:
string find(const string& key) const;
private:
//...
mutable string cached_key, cached_value;
};
The find member function does not logically change the dictionary’s contents, but it wants to keep a cache of the last searched item (for instance), so it needs to update the cached_key and cached_value members. This can only be done when mutable is used.
4. Use the explicit keyword
Yet another infrequently and less known used keyword. This one can only be used in a converting constructor, that is, a constructor that accepts a single argument. This kind of constructor is invoked automatically if an object of that argument type is supplied where the actual type is expected. An example will illustrate:
class Database {
public:
Database(const char* path);
// other members
Database();
};
void foo() {
Database db;
db = Database("hello.dat");
// same deal
db = "hello.dat";
}
The last line works because of the converting constructor, but it seems illogical to construct an entire database just to call the assignment operator with that. It also results in creation and destruction of temporaries that may be expensive. The explicit keyword makes that forbidden:
class Database {
public:
explicit Database(const char* path);
// other members
Database();
};
void foo() {
Database db;
db = Database("hello.dat");
// does not compile!
db = "hello.dat";
}
5. Template Meta programming
This is a hot one. It can make you look cool, but too much of it and it will give you a C++ freak reputation that makes you less than cool – you may be expelled from your C++ peer group. Here’s an example of one basic idea (don’t freak out):
template<int N>
struct Factorial {
enum { Result = Factorial<N-1>::Result * N };
};
template<>
struct Factorial<1> {
enum { Result = 1 };
};
int _tmain(int argc, _TCHAR* argv[]) {
cout << Factorial<5>::Result << endl;
}
If you’re getting a feeling like “recursion” – you’re right; but it’s a different kind of recursion – it’s compile time recursion. There are no loops possible during compilation, so recursion is the only way to go. This is done by template specialization.
This is just the tip of the iceberg in template meta programming. Check out more in the boost libraries, and other sources. Be careful, though, this may be a dangerous road, and a lonely one at that.
6. functors
Functors, or function objects are just that: objects pretending to be functions. Technically, using them in C++ is pretty easy: just overload the function call operator(). Here’s a simple example:
int quad(int x) {
return x * x + 4 * x - 10;
}
void foo() {
vector<int> v;
//...
// transforms the vector into x*x+4*x-10 for each item
transform(v.begin(), v.end(), v.begin(), quad);
}
A typical example from STL is the ordering of items in a set or map. These entities are not hash tables, but binary trees. By default, they are ordered by using the less<T> class (which is a functor). To change the order of objects, a new functor can be plugged in:
class BookCompare {
public:
bool operator()(const Book& b1, const Book& b2) const {
return b1.yearPublished > b2.yearPublished;
}
};
set<Book, BookCompare> books;
map<Book, Shelf, BookCompare> library;
Use functors: they are cool.
7. The Comma operator
The comma operator is not usually regarded as an operator at all. For example, what would be the value of the variable x, assuming the array a is initialized with the values 0 through 9:
int a[10];
//...
int x = a[2, 3];
If you can’t guess, read on (yes, it does compile just fine).
The comma operator just says that thing will be done from left to right, and (and this is important) that value of the expression is the rightmost one. Now do you know what will x equal?
If not, read on still.
One of the things I had to do in the COM days was to set some value and return an HRESULT. One way to do this was this:
HRESULT CMyClass::SomeMethod(int x, int* result) {
if(x > 5) {
*result = 0;
return S_OK;
}
return E_FAIL;
}
I had to open curly braces and do the two things I needed to do. But the small comma can help:
HRESULT CMyClass::SomeMethod(int x, int* result) {
if(x > 5) return *result = 0, S_OK;
return E_FAIL;
}
Remember, the S_OK is the expression value, but the *result=0 is executed as well. Cool!
There are other ways to be cool in C++, but this should get you started!