C++, Page 2

Optimizing exceptions

You might often hear about ex­cep­tions being slow. For this rea­son they are usu­ally shunned in the em­bed­ded space, and some­times even for reg­u­lar desk­top/server pro­gram­ming. What makes them slow? When one is thrown it needs to search through the call stack for ex­cep­tion han­dlers.

I guess I don’t un­der­stand this line of think­ing. For one, ex­cep­tions are meant for ex­cep­tional sit­u­a­tions: things you don’t ex­pect to hap­pen under nor­mal op­er­a­tion. Code that uses ex­cep­tions will run just as fast (or maybe even faster) as code with­out, until you throw one. These ex­cep­tional sit­u­a­tions are tru­ely rare, so I usu­ally don’t care if they do hap­pen to run slower.

A com­piler can ac­tu­ally use ex­cep­tions to op­ti­mize your code. Con­sider this in­ef­fi­cient (but typ­i­cal) pseudo-C:

int dosomething(void) {
   /* do something A */
   if(err) return -1;

   /* do something B */
   if(err) {
      /* cleanup previous work A */
      return -1;
   }

   /* do something C */
   if(err) {
      /* cleanup previous work B */
      /* cleanup previous work A */
      return -1;
   }

   return 0;
}

Or even this more ef­fi­cient (yes boys and girls, goto ac­tu­ally has a good use case in C, get over it) pseudo-C:

int dosomething(void) {
   /* do something A */
   if(err) return -1;

   /* do something B */
   if(err) goto err1;

   /* do something C */
   if(err) goto err2;

   return 0;

   err2:
   /* cleanup previous work B */

   err1:
   /* cleanup previous work A */

   return -1;
}

Why are these bad? Cache lo­cal­ity. In the first ex­am­ple, you have error han­dling code in­line with your reg­u­lar code. In the sec­ond you have it slightly bet­ter and off to the end of the func­tion. Ide­ally the code you run will all be com­pacted in as few cache lines as pos­si­ble, and er­ror­ing han­dling this way will waste sig­nif­i­cant space on cleanup code that in the large ma­jor­ity of cases won’t be run.

But with ex­cep­tions, the com­piler is free to take all the cleanup code in your en­tire app, and shove it into a sin­gle sep­a­rate area of code. All your nor­mal code that you ex­pect to run can be com­pact and closer to­gether. Of course, this will make ex­cep­tions run slower. If your code is heavy on throw­ing ex­cep­tions (which would prob­a­bly be an abuse) it will prob­a­bly cause a sig­nif­i­cant over­all slow­down. But if they are used cor­rectly–for ex­cep­tional sit­u­a­tions–then the com­mon case will be im­proved cache usage and there­for faster code.

Visual C++ 2008 Feature Pack is now available

The Vi­sual C++ 2008 Fea­ture Pack I talked about be­fore is fin­ished and ready for down­load. This in­cludes a bulk of the TR1 up­dates (sadly, still no cst­dint) and some major MFC up­dates.

GCC 4.3, C++0x preview

GCC 4.3 came out a cou­ple weeks ago, and I fi­nally got time to give its ex­per­i­men­tal C++0x sup­port a go. Specif­i­cally, I was in­ter­ested in two fea­tures of it: vari­adic tem­plates and rvalue ref­er­ences.

There is one prime ex­am­ple of what these two fea­tures are awe­some for: per­fect for­ward­ing. Take a mem­ory pool. You might have some­thing like this:

class pool {
   void* alloc();

   template<typename T>
   T* construct() { return new(alloc()) T; }
};

But that is hardly sat­is­fac­tory. What if you want to con­struct T with an ar­gu­ment?

class pool {
   void* alloc();

   template<typename T>
   T* construct() { return new(alloc()) T; }

   template<typename T, typename ArgT>
   T* construct(const ArgT &arg) { return new(alloc()) T(arg); }
};

So we add a new func­tion to han­dle pass­ing an arg. Bet­ter, but still not very great. What if you want to pass it mul­ti­ple ar­gu­ments?

C++ has very few prob­lem that can’t be worked around in a rel­a­tively strait­for­ward way. Un­for­tu­nately, this is one of those prob­lems. The cur­rent so­lu­tion most li­brary de­vel­op­ers em­ploy will in­volve some re­ally nasty pre­proces­sor hacks to gen­er­ate sep­a­rate func­tions for 1, 2, 3, up to maybe 10 or 15 ar­gu­ments. So, how do we solve this?

Enter vari­adic tem­plates, the new C++ fea­ture built specif­i­cally to solve this. Here is an up­dated pool class that takes any num­ber of ar­gu­ments:

class pool {
   void* alloc();

   template<typename T, typename Args...>
   T* construct(const Args&... args) { return new(alloc()) T(args...); }
};

Pretty sim­ple! Those el­lipses will ex­pand into zero or more args. Great – we’re al­most there. But we still have a prob­lem here: what hap­pens if the con­struc­tor for T takes some ar­gu­ments as non-const ref­er­ences? This con­struct func­tion will try to pass them as const ref­er­ences, re­sult­ing in a com­pile error. We can’t have it pass args as non-const ref­er­ences, be­cause then if you pass it an rvalue—such as a tem­po­rary—it will gen­er­ate an­other com­pile error as rval­ues can only be bound to const ref­er­ences.

This is where the sec­ond part of our pool up­grades come in: rvalue ref­er­ences.

class pool {
   void* alloc();

   template<typename T, typename Args...>
   T* construct(Args&&... args) {
      return new(alloc()) T(std::forward(args)...);
   }
};

We’ve fi­nally got our so­lu­tion. That dou­ble-ref­er­ence look­ing thing is the new syn­tax for rvalue ref­er­ences. This con­struct im­ple­ments per­fect for­ward­ing: call­ing construct<foo>(a, b, c, d) will be­have ex­actly as if we had called the con­struc­tor di­rectly via new(alloc()) T(a, b, c, d).

This works be­cause Args is a tem­plated type that will re­solve to ref­er­ences and const ref­er­ences if it needs to. One prob­lem I have yet to fig­ure out how to solve is a con­struc­tor where you know the type you want, and want to ac­cept any ref­er­ence type:

struct foo {
   foo(const bar &b) : m_b(b) {}
   foo(bar &&b) : m_b(std::move(b)) {}

   bar m_b;
};

I don’t care if b is a lvalue or rvalue ref­er­ence: I just want the con­struc­tion of m_b to be as ef­fi­cient as pos­si­ble so that it can use move se­man­tics when you pass it an rvalue. So far the only way I can find to do it is with two sep­a­rate con­struc­tors, which could mean a lot of code du­pli­ca­tion on some­thing less triv­ial than this ex­am­ple.

Digging into TR1

Chan­nel 9 has an in­ter­view with Stephan T. Lavavej of the Vi­sual C++ team show­ing off some of the new fea­tures in TR1, the new draft stan­dard li­brary ad­di­tions for C++0x. While the in­ter­view will prob­a­bly have noth­ing new for com­pe­tent C++ de­vel­op­ers, Stephan does go into good de­tail ex­plain­ing the unique strengths of C++ that new­bies may not im­me­di­ately see.

If you’ve thought of C++ but have been scared by its com­plex­ity, or have been tempted by newer garbage col­lected lan­guages like C#, this will prob­a­bly be a good eye opener.

C++ sucks less than you think it does

C++ seems to be the quick­est lan­guage to get a knee-jerk “it sucks!” re­ac­tion out of peo­ple. The com­mon rea­sons they list:

C++ is a very pow­er­ful, very com­plex lan­guage. Being a multi-par­a­digm (pro­ce­dural-, func­tional-, ob­ject-ori­ented–, and meta-pro­gram­ming) lan­guage that im­plores the coder to al­ways use the best tool for the job, C++ forces you to think dif­fer­ently than other pop­u­lar lan­guages and will take the av­er­age coder years of work­ing with it be­fore they start to get truly good at it.

C++ does have its flaws. Some are fix­able, some aren’t. Most of what is fix­able is being ad­dressed in a new stan­dard due some time next year (2009).

Its biggest prob­lem is that new­com­ers tend to only see its com­plex­ity and syn­tax, not its power. The pri­mary rea­son for this is ed­u­ca­tion. In in­tro­duc­tory and ad­vanced courses, stu­dents are taught an un­der­whelm­ing amount of tem­plates with­out any of the use­ful prac­tices that can make them so epi­cally pow­er­ful. Use of the in­ter­est­ing parts of the stan­dard li­brary, such as it­er­a­tors and func­tional pro­gram­ming, are put aside in favor of ob­ject-ori­ented de­sign and basic data struc­tures which, while use­ful, is only a small part of what C++ is about. RAII, an easy zero-cost de­sign pat­tern that makes re­source leaks al­most im­pos­si­ble, is vir­tu­ally un­taught.

C++ does tend to pro­duce larger ex­e­cuta­bles. This is a trade off – do you want smaller ex­e­cuta­bles or faster code? Whereas in C you might use call­backs for some­thing (as shown by the qsort and bsearch func­tions in the stan­dard li­brary) and pro­duce less code, in C++ every­thing is spe­cial­ized with a tem­plate that gives im­proved per­for­mance. This fol­lows C++’s “don’t pay for what you don’t use” phi­los­o­phy.

Don’t get me wrong, C++ is not the right tool for all jobs. But among all the lan­guages I have used, C++ stands out from the crowd as one that is al­most never a bad choice, and a lot of times is the best choice. It might take longer to get good at than most other lan­guages, but once you’ve got it down its power is hard to match.

Visual C++ 2008 Feature Pack beta

It’s here! A beta of the promised TR1 li­brary up­date has been put up for down­load.

In­cluded in the pack is an up­date to MFC that adds a num­ber of Of­fice-style con­trols. Wish they’d put these out as plain Win32 con­trols, be­cause I’ve got no in­ten­tion of using MFC!

C++0x work progressing

A bit de­layed, but I just found the re­sults of the Oc­to­ber 2007 C++ meet­ing. In it, they voted in sev­eral re­ally nice things for the C++0x draft:

Visual Studio 2008 released, TR1 support coming

Any­one fol­low­ing Vi­sual Stu­dio 2008 will know that al­though it of­fers a plethora of new fea­tures for the man­aged world, there was lit­tle focus on the un­man­aged side of things. Now that it is fi­nally out the door, I guess it’s a good time to look at what few new fea­tures are there for us un­man­aged C++ coders.

Not much, huh? That’s be­cause Mi­crosoft was run­ning under the as­sump­tion that peo­ple would flock to C# and only use un­man­aged C++ to main­tain "legacy" code. Per­haps the best news so far, they’ve fi­nally re­al­ized their mis­take. Al­though they didn’t have time to put things into VC++ 2008, they have re‐com­mit­ted to un­man­aged code for the next ver­sion and in the mean­time made a small sep­a­rate an­nounce­ment that they will be bring­ing VC++ 2008 users a mostly com­plete TR1 im­ple­men­ta­tion up­date in early Jan­u­ary.