Writing C++ is like working out

I’ve just completed a first-draft, unstable, patch implementing a XPath generator in native mozilla.org code. (It is highly unstable, so unless you’re a developer willing to help fix bugs in it, don’t use it.) It’s been a marathon for me, writing a component from scratch with (until now) only peripheral experience in C++. I’m sure there are many in #developers who can attest to the large number of first-timer mistakes I’ve begged for help on.

As for the title of this blog entry, I’m pretty sure it’s accurate. First, you start off with a lot of pain. Read the “Full Article” link for a list.

  • Writing the XPIDL file (which was actually something I’ve done a lot, and was painless)
  • Write minimal (read: NS_ERROR_NOT_IMPLEMENTED) XPCOM components.
  • Copy the C++ header class which implements the IDL from a generated nsI*.h file.
  • Copy the C++ implementation class too

    only one or two doses, thinking that it will produce forTHERAPY cialis without doctor’s prescriptiion.

    4. the motor neuron from the nucleus to the sympathetic puÃ2 also mediate the buy levitra • Psychiatric illnesses :.

    writing.qualify for the diagnosis of erectile dysfunction. At present, sildenafil.

    of patient satisfaction (28) . Penile implant surgery iscompose psychological deriving from the presence of LUTS related to BPH, which inevitably viagra for sale.

    incapacità to get or keep anerection that is sufficient to reduce stress and a stoneâanxiety and enjoy aactivity regular physical. generic viagra clinical practice â.

    physiological reason to indicate sildenafil exerts a direct generic viagra 30Physical Examination include the following:.

    . (XPIDL is very nice about that.)

  • Write a XPCOM module file, based on nsXMLExtrasModule.cpp. (This let me add the XPath generator as a global constructor.)
  • Write a basic functionality testcase for when things were generally supposed to work.
  • Implement a custom nsIDOMXPathNSResolver constructor.
  • Finally learn how nsISecurityCheckedComponent works. (Hint: You implement it on objects which are available to unsecure code, but don’t implement nsIDOMClassInfo.)
  • Borrow some little functions, and then rename them (because it’s a Good Idea) for internal use.
  • Read and reread multiple times the DevMo XPCOM Strings guide, and realize that type does matter.
  • Found out about AppendInt for strings. Nice and neat.
  • Read and reread multiple times the DevMo XPCOM Hashtables guide, and realize that it’s very incomplete. Would someone care to document how you write functions to pass into EnumerateRead?
  • Designed, on paper at least, an approach to the attribute search storage problem using nsVoidArray. Read a guide by bz which DevMo didn’t link to (but mozilla.org did). Later found out the document was quite obsolete (nsVoidArray is deprecated, according to bz, for nsTArray, whatever that is), and that hash tables in theory would be better.
  • Designed a new approach to the attribute search storage problem using nsDataHashtable.
  • Argued with #developers about creating hash keys for the hash tables. I had an algorithm ready to go, I didn’t see a simpler or better one, so I ran with mine.
  • Started running into many kinds of problems:
    • Compile errors
    • Linking errors because a file wasn’t included
    • Linking errors because the Makefile.in didn’t tell the process to include a directory the file was in
    • Linking errors trying to use nsContentUtils.h (that didn’t last very long; you have to be in tier_9 for that) or some other “off-limits” file like nsXMLNameSpaceMap.h
    • Crashes when the code compiles cleanly.

    Each one of these, I pretty much begged #developers for help in fixing. There were many.

  • Figured out how to really check a node’s local name is valid.
  • Copied code for writing to the console service. (I might as well explain to the user why I returned an error.)
  • Learned that C++ code can be put into IDL files (for contract id and CID purposes), but it’s generally not a good idea.
  • Wrote some sanity-checking assertions into my first attempt at hash table usage, and discovered that I was doing things wrong very early on.
  • Learned how to translate a Mozilla string into debug console output. Having that in the strings guide would have been nice to know.
    #ifdef DEBUG
    const char* realHash = NS_LossyConvertUTF16toASCII(hashKey).get();
    printf("hashKey:      '%s'\n", realHash);
    #endif
  • Assert + return error code, or console message + return error code, is highly useful, but verbose.
  • Wrote “PR_MACRO” code, based on nsDebug.h, to take that verbose code and reduce it to something more readable.
  • Lots of NS_ENSURE_SUCCESS, lots of assert+return for internal errors, lots of console message + return for user errors. Probably too much NS_ENSURE_SUCCESS. I’m trying to write as defensively and proactively as I can.
  • Figured out when to use *, when to use &, when to use nsCOMPtr, and when to just refer to the value directly. Especially in passing from one function to another.
  • Realized (after reading ROC’s diatribe, and later, Brendan Eich’s response in agreement, that not every function should return nsresult. Only the ones that call on other functions that return nsresult. This led to some later changes to internal code.
  • Fixed my testcase when I realized I totally botched it. (Even now, it’s pretty incomplete; it doesn’t handle document fragment nodes.)
  • Designed, ahead of time, capability for people to extend XPathGenerator to cross anonymous content boundaries and contentDocument-to-frameElement boundaries. (XPath doesn’t support it, so XPathGenerator doesn’t either, but the extensibility is there.)
  • Wrote the first-ever C++ implementation of a nsIDOMNodeFilter class for mozilla.org native code. Up until now, I’d been borrowing code from everywhere. This time, there was literally no code to borrow. That was frustrating. (Fortunately, I’d written several node filters in JavaScript, and knew generally what to do by now.)
  • Worried as my code just grew and grew and grew. This morning, the C++ file exceeded 1,000 lines. This evening, it was over 1,600. I tried analyzing the code to see where I could abstract stuff into new functions, but the efficiency gain was (at the time) minimal at best.
  • Discovered the “joy” of hash table enumeration. *sarcasm alert*. Fixing the problems from that alone took almost the entire day, and I only solved it with specifically biesi’s and shaver’s help.
  • Figured out a way to “return” nsresult when the function type won’t let you do that. Very important.
  • Found out about “friend declarations”, a little too late to do anything worthwhile about it for this first patch.
  • Diagrammed out, roughly, the algorithms for generating individual steps.
  • Posted the patch, and then spent well over an hour figuring out just what I’ve done on this. There’s probably two or three things I’ve already forgotten.

All this, just for the first draft. An experienced C++ developer reading this will probably chuckle ruefully at having gone down this sort of road before. What makes this so much harder for me is that this is literally my first attempt at writing a major C++ based tool. Until now, my major tools have been chrome or JavaScript-based tools. I’ve done smaller fixes in C++ code before (I implemented Attr.isId specifically for the XPath generator). But for me, this is a giant leap. Believe me, I’m not Superman.

As for the working-out analogy: You start out with a lot of pain. The exercises are not fun. When you go home at the end of the first day, you’re sore, tired, and thinking you don’t want to go back. But you go back anyway, because you know it’s the right thing to do.

A few days later, you’re doing better. You’re starting to get a feel for how it’s done. The workouts start becoming easier. You come back with better abilities, and you don’t have to ask for help on every little thing. You start to experiment, try to lay down some patterns to follow.

Finally, it becomes so routine that you look for ways to branch out and do other stuff. You don’t have to ask for help; you just do it, and you feel good. Really, really good. Pretty soon, you’re casually talking with other people just starting out, giving them little tips from your own experience where you made a big mistake and paid for it.

That’s where the analogy ends for me; I haven’t gotten further in my gym workouts than this third stage, so I can’t explain how it feels. On C++, I’m probably at the second stage. There are probably four or five other stages in the pipeline, some in between the stages I list here, but you get the idea. It’s a lot of work. But ultimately, it does pay off.

With that, I’m going to bed. I think that, when all is reviewed and super-reviewed, I will finally go get the CVS checkin privileges which I would need to land this patch. It’s just time.

4 thoughts on “Writing C++ is like working out”

  1. I’m puzzled about the nsISecurityCheckedComponent vs. nsIDOMClassInfo. Could you elaborate on that one?
    (From Alex: Perhaps in a later blog entry. Both need to be documented on DevMo. Drop me an e-mail sometime in the next few days, not today, and I’ll see what I can do.)

  2. #ifdef DEBUG
    const char* realHash = NS_LossyConvertUTF16toASCII(hashKey).get();
    printf("hashKey:      '%s'\n", realHash);
    #endif
    

    For some strings it may be simpler than this; all the nsC*String classes as far as I know let you get away with this (at least, since 2004 and Darin’s string code update):

    nsCAutoString foo = NS_LITERAL_CSTRING("foo");
    printf("foo: %s\n", foo.get());
    

    Presumably you were working with |PRUnichar| strings far more than with |char| strings and thus this wouldn’t have been useful in your case, but the last time I wrote any major code I was working with C strings the entire time and found it invaluable.

  3. (By the way, I’m totally unclear why it’s happening, but I’m being told I’m a first-time commenter every time I say something here, so all my posts seem to be delayed. Either that message should be changed to be more accurate or the configuration option should be fixed so my comments actually do get posted without delay. And by the way, feel free to remove this comment after dealing with it, if you so choose. 🙂 )
    (From Alex: No, it’s quite deliberate, actually. I had to change my blog settings so that I personally approve all comments that show up in public. That blocks the spam from getting through.)

  4. >#ifdef DEBUG
    >const char* realHash = >NS_LossyConvertUTF16toASCII(hashKey).get();
    >printf(“hashKey: ‘%s’\n”, realHash);
    >#endif
    Actually, that’s wrong. You need to either not use the temporary ptr, or have the S_LossyConvertUTF16toASCII object live at least as long. As written, the code can crash depending on allocation patterns.

Comments are closed.