Archive: July 27, 2003

<<< July 24, 2003

Home

July 28, 2003 >>>


Sunday,  07/27/03  10:51 AM

Happy Anniversary - to me!  And to my wonderful wife Shirley.  We celebrated our 11th anniversary yesterday.  Wow, eleven years.  A lot has happened in that time, to us, and to the world!  Eleven years ago nobody had heard of the Internet, it was not a thing.  And now it is an indispensable part of many lives.  Amazing to contemplate, and to think what the next eleven years might bring...


Lots of catching up to do, it's all happening...

Well, Lance has done it again.  Five straight Tour de France victories.  Absolutely unbelieveable!  This was his toughest one, and in many ways his best.  The time trial yesterday was great; too bad Jan Ullrich fell in the wet conditions, it made for a bit of anticlimax.  But that's bike racing... sometimes you have to survive first, then win.  I think the key point of this Tour was stage 15 in the Pyrenees, when Lance fell hard, and then incredibly got up and powered to a stage win.  He just seems to have an extra gear when he needs it :)

Remember we were talking about Mitochondrial Eve, and how humans were first found about 160,000 years ago in Africa?  Well, turns out humans came to North America much later, only about 18,000 years ago...  There is evidence from Y chromosome studies that the first Americans came over a land bridge from Asia.

pretty womanHere's an interesting post on what makes faces attractive.  [ via GNXP ]  This is an especially interesting discussion considering the natural selection considerations; somehow there must have been selective pressure for "attractive" faces, or we wouldn't find them important in choosing a mate.

Also in GNXP: Genes, Medicine, and the New Race Debate.  [ Originally from Technology Review, but behind a paywall ]  Researchers are using the data from the human genome project to construct a "haploid map", which defines the boundaries of each haplotype block, a kind of grouping of genes along a chromosome.  "The most immediate impact of the HapMap is likely to be the prediction of how a patient will respond to a drug."  One of the holiest grails in biotech right now.

More on RFID; Salon says everything is watching you.  Not yet, but soon...

Recent reports from AOL and SBC seems to suggest DSL is gaining on Cable, in the neck-and-neck race to bring broadband to the U.S.  Interesting.  I've had both, and I can honestly say cable is better.  Faster, anyway, and at least as reliable.  With cable you get 300K+ downloads all the time, whereas with DSL you only get about 100K.  Uploads are slower - and you can't host a server on cable - so that's the downside, but for the average couch surfer that doesn't matter.

CNet report Microsoft considering Music Store.  Excellent, welcome to the party.  Yet another case where MS would follow Apple into a new market / technology, and perhaps yet another case where they'd be more successful over the long haul.

Anil Dash writes about Microsoft: many years of sucking.  He picks on the font installation dialog, which has sucked for ten years.  There are these little backwaters of Windows which never seem to get any attention.  That funky dialog box you get when you first use a help file (which Joel likes so much) comes to mind.  It is probably unfair to judge the entire company, or Windows, by one anachronism, but it is fun...

Check out these amazing optical illusions.  The one at left is my favorite; no matter how hard my brain tries to tell my eyes the lines are straight, they want to say they're curved.  Cool.

 

Third-Party C++ Children

Sunday,  07/27/03  06:09 PM

The problem

Suppose you have an application which provides "core" functionality for other developers.  Suppose you want to expose functionality as C++ classes, rather than via APIs.  Suppose you want to enable extension of those classes by third-parties.  What are the pitfalls?  How do you avoid them?

Discussion

Let's say you expose functionality via a class cBase.  This class can be used "as is", but is really intended to be used as a base class for other classes (created by third-parties):

class cNew : public cBase {...}

The third-party code is going to create cNew objects (your code doesn't know about them), and when your code interacts with these objects you can only treat them as cBase objects, not cNew objects (because, again, you don't know about cNew).

Therefore you have to make any methods you intend to make overrideable as virtual methods:

class cBase {
public:
    virtual ~cBase();         // make destructor virtual
    virtual void myMethod();  // a virtual method

Now in your code you deal with cBase pointers, which are really pointing to cNew objects:

cBase *pObj;                  // pointer to cBase / cNew

pObj->myMethod();             // could call cNew's method

Virtual methods are called indirectly through a vector table in the object; if the pObj pointer was pointing to a cNew object, then the myMethod() pointer would have been initialized to point to cNew's myMethod by the cNew constructor.

This is the intent of virtual functions, and it works great.  So...

Back to the problem

Now suppose you've released cBase into the field, and a bunch of third-party developers have created various cNew classes based upon it.  You cannot change the interface to cBase in any way!  Nope, not even if you're careful to keep all the old methods backward-compatible.  The problem is that if you add a new virtual method to cBase, its vector table is going to get bigger.  The vector table in objects made by existing third-party code will be incorrect (because it won't have the correct number of vectors for cBase).

It is really unlikely that you'll never want to enhance cBase.  Furthermore, it is even more unlikely that you'll be able to distribute an updated cBase header to each third-party developer, and it is astronomically unlikely that they'll update their code and successfully distribute it to each of their users.  In other word, backward-compatibility with "old" third-party code is essential.  What do you do?

The solution

The solution is simple - you extend cBase by deriving from it.  You create a new class for your code's use:

class cBase2 : public cBase {...}

In your code, anywhere you can you should assume you have a cBase object, as in the example above.  If you have code which must "know" whether you have a cBase2 object, then you have to ask the object what it is.  For this you need a getVersion() method.  And you have to build this into cBase:

class cBase {
public:
    virtual int getVersion(return 1);  // returns cBase version

Note that getVersion() is virtual.  Later, when you create a cBase2, you can do it like this:

class cBase2 : public cBase {
public:
    virtual int getVersion(return 2);  // returns cBase version
    virtual void newMethod();          // method only in cBase2

Later there may be a cBase3, and a cBase4, and they will all return different versions.

Now that we have getVersion(), we can write intelligent code to behave appropriately with both "old" and "new" objects:

cBase *pObj;                           // object pointer

if (pObj->getVersion() == 1) {         // if object based on cBase
     // do something intelligent with "old" object
     }
else {
     cBase2 pObj2 = (cBase2*) pObj;    // cBase2 pointer

     pObj2->newMethod();               // now can call new method
     }

This way you will always provide support for those "old" third-party objects that may be out there.  And you can enhance the support for the "new" objects.  Having created cBase2, what do you do with your third-party developers?

Simple: you distribute the cBase2 header (which includes the cBase header) to all third-party developers.  Any developer who wants can take advantage of the new capabilities by deriving from cBase2:

class cNew : public cBase2 {...}

And the old school developers who don't want or need the new functionality can continue to derive from cBase, with no loss of support.

This technique benefits your developers, too; they might well have modules which need to detect "old" modules at customer sites, and can use the getVersion()method to do it.

That's it - please let me know if you have questions or suggestions.

 

 
 

Return to the archive.