More of a programming nerd than is strictly healthy. See also {nevyn.nu, thirdcog.eu, twitter}

Projects

Overooped

Weekend Hacking

I had a lot to do this weekend and decided to do none of it, and instead ported sfxr to Cocoa, with a native UI and proper save/load :) My version’s called cfxr (as in “Cocoa sfxr”) and is available at http://thirdcog.eu/apps/cfxr . I’m not saying it’s better than sfxr, only different and more native. If you’ve got a 10.5 Mac, check it out and let me know what you think :) It’s basically just an experiment to learn Core Data and Bindings (Thanks, Scott!), and a reason to make save/load work better on the Mac.

The sfxr code (more specifically, the sdl port), when I first saw it, seemed like the worst mess I’ve ever seen. Sure, it’s a quick hack, but not even keeping state in a struct? OMG. But after working with this code for a weekend, it’s surprisingly good for what it is! “Porting” it to Cocoa was as easy as finding out which globals were properties of the sound (that is, attributes), and adding “sound.” before all accesses to those, and #defining objc syntax as C syntax for the four major playback methods. I guess I could’ve #defined the sound attribute accesses as well, making an upgrade as easy as a copy-paste, but I felt I had already done enough code generation for one day :P (Check out the legacyAccessors.m in the source to see what I mean :P Not very good looking code but it got the job done.)

It’s a rough 0.1 and might need some work. I was also thinking about making the playback part an AudioUnit or VST (just for fun) to make the playback more flexible. It works pretty nice as it is though, so do check it out :)

Lazy Man’s Logging

file_put_contents(…, FILE_APPEND) to log is a bad idea and you know it, but it’s sometimes good enough, or you just don’t get paid enough to make something serious. I just let you make it a tiny bit more serious, a whole lot more dependable, and still just change a single line of code.

  /// Creates a table called $table as (id, when, message) if none such exists, and inserts a row with $message in it.
  /// If no connection details are given, it uses the current database connection. Same goes for $database and $when.
  ///
  /// @returns TRUE on success or FALSE on failure.
  ///
  /// @example mysql_put_contents("orders", "I CAN HAZ CHEEZBURGER?", "mysite", NULL, "127.0.0.1:3306", "mysite_user", "secret") or die(mysql_error());
  /// @example mysql_put_contents("guestbook", "Longcat says: I'm loooooooooooong") or die("Errorz!");
  function mysql_put_contents($table, $message, $database = NULL, $when = NULL, $host = NULL, $user = NULL, $pass = NULL) {
    if($host)
	    mysql_connect($host, $user, $pass);
	  if($database)
	    mysql_select_db($database);
	
	  $qry = "CREATE TABLE IF NOT EXISTS `$table` (
             `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
             `when` TIMESTAMP DEFAULT NOW(),
             `message` TEXT NOT NULL
           );";
    $result = mysql_query($qry);
    if($result === FALSE)
      return FALSE;
      
    $qry = "INSERT INTO `$table` VALUES(NULL, ".($when ? $when : 'NULL').", '".mysql_real_escape_string($message)."');";

    $result = mysql_query($qry);
    if($result === FALSE)
      return FALSE;
      
    return TRUE;
  }
	

Re: Firefox 3 vs. Safari 3

I agree with everything that Gruber writes about in his latest article. A friend of mine went majorly goddamned-mac-zealot on me and his article (had to explain not once but *twice* that Gruber wasn’t saying “FF should be exactly as Safari” but “Safari beats the Mac port of FF on a number of points” :P) All is well and good though, as he found a fix for the most annoying UI element of FF3 for me: single-click-selects-entire-URL. I almost screamed from frustration from just trying to edit the URL a few days ago. Here’s how to fix it, though:

  1. Go to about:config (type it in the location bar and type enter)
  2. Filter on “clickSelects”
  3. Double-click on the row that says “browser.urlbar.clickSelectsAll” to set it to false.

Now when you single-click in a Firefox location bar, it’ll place the caret in it; double click will select word; and triple-click will select the whole line, just like in a Mac app.

Addendum: Okay, so Voxar tried to convince me that Safari’s location bar was idiotic and illogical (of course only from reading Gruber’s article and not trying it out, even though he has a Mac on his desk :P), and challenged me: if Safari always highlights the first autocomplete entry when you type an url, wouldn’t it be VERY cumbersome to enter e g “http://daringfireball.net/2008/” when the only URL in your history is “http://daringfireball.net/2008/04/firefox_3_safari_3”? The answer is of course, no :) In Safari, you type “da[right-arrow]2008[backspace][enter]” and you’re done with it. The same scenario in Firefox would require you to type the entire URL in by hand, since the autocomplete would be completely useless (even the AwesomeBar would be stumped!). Now I remember why I love Safari :) (Watch It In Full Motion.)

Nevyn’s First Rule of Singleton Evilness

I finally figured out a litmus test for whether being a singleton is okay or evil for a particular class:

If a class is thread-safe and has no state that can be changed, it may be a singleton.

NSFileManager: OK. RMS::Sound::Gateway: Not so much.

I suppose there are ways to circumvent this rule; e g most of Apple’s singletons may be created either through the+[defaultManager] method, or instantiated on-the-spot, e g if you want several separate NSNotificationCenter inside the same application.

This discussion came up during the creation of the RMS game engine, among many other times. Now that I’m ripping out parts of the engine for re-use, I realize that it was a very bad design decision to use singletons the way we did. For example, my 2D modeler prototype for my candidate thesis is document-based, thus it needs an RMS::Sound::Gateway for each window. This is no-can-do until I fix the code, because for example the Voice class has the delegated play method, and the way it works is that it gets the global gateway and adds itself to the gateway’s list of playing voices.

“State that can be changed”? Oh, right. +[NSColor redColor] has state, but it can still be a singleton since that state can’t be changed by my code. Same for NSEvent’s singleton methods, and so on.

Not sure why Core Audio isn’t an Objective C API

It’s for performance, right? It’s the only good reason I can think of. And, you know, it sounds sensible. I mean, ultra-low latency and all that, you probably don’t want that objc dispatch overhead.

I just did an experiment, however. I dislike working with C APIs, so I’m writing Cocoa wrappers for Core Audio, just exposing those pesky Component properties that take five lines to set or get, with simple methods. Suddenly I thought, “Wait, what if I try to use an objc method as a render callback? Those require very low latency and are called often. So I should be seeing some of that objc overhead.”.

Very unscientific comparison, comparing a simple sine renderer in c and objc, on an MBP 1.83x2 (source available on request):

This isn’t, by far, any compelling evidence that Core Audio should be Objective C; I’m just saying it seems more feasible than I originally thought it’d be. Also, actually thinking about the problem, I realize that the callback’s only called 44100/512 ≈ 86 times a second and has about 10 ms to complete (astronomically long in computer terms).

But NeXT did it that way, didn’t they? I want to remember that NeXT had basically /everything/ in objc, including drivers and audio and such things. So, why not Mac OS X? NeXT was hardly known for being a slow OS. Tell me what I’m missing in the comments.

These are my children. They join me on all my journeys.

So I use a lot of small utility classes that just jump between projects. Not enough to warrant a library, but still code I can’t live without. For example, ary(id first, …) instead of [NSArray arrayWithObjects:…], dict(id key, id value, …) for a dictionary, sf(NSString *format, …) instead of [NSString stringWithFormat:…], a zooming and delta-scrollable CALayer, vector class with most linear algebra, line class, simple macros to turn of CoreAnimation animations… You get the point.

Anyways, from my last blog entry you might have gathered that NSOperationQueue, CoreAnimation and garbage collection don’t really work well together. It seems to be a problem with ensuring that only a single is committing changes/transactions at a time (hence a crash in CALayerEnsureTransaction). NSOperationQueue sets up a full thread with a run loop for every single operation. I have no idea why they chose this wasteful approach; personally, I’d reuse a set of threads. And since NSOperationQueue isn’t working, and my entire acoustic modeling simulation is built around NSOperations, I decided to do just that. Thus, FakeOperationQueue joins my army of utility classes. It has the exact same interface as NSOperationQueue (almost, I only covered what I use), and works on NSOperations. Note that it doesn’t consider dependencies! It’s a very simple class, only a hundred lines of code.

Rentzsch seemed somewhat interested in my fake queue. Thus, I decided I might as well put my stuff online.

I have newprojectitis

Jan: [exams not going good]
nevyn: ah... boring education?
Jan: [secret project name] :)
nevyn: hehe
nevyn: goddamn coding is so much fun
nevyn: I started a new game today :P
Jan: hehe, you have serious newprojectitis

Three Points On Error Handling

I’ve been ranting on Twitter lately about how to do error checking the wrong way. Coincidentally, the latest topic on Mac Developer Roundtable was about just that, error handling.

I don’t think this episode of MDR was very interesting. It was essentially an overview of the three common error handling strategies (exceptions, enum returns, pass-by-ref error object), and some general C++/Java likeage from Uli (which is not a compliment :P). However, it got me writing a real blog entry, which is a good thing :)

So: Three things kept repeating in my head while I listened to MDR#3, hoping that someone’d mention it so that we can rid the world of more bad code.


First off, stay far far away from nested ifs. I don’t remember which Mac dev blog I read it on, maybe Wil Shipley’s or ridiculous_fish, but whoever it was recommended to always try to keep the code as far to the left as possible. Use the outermost block for the common, correct case, and use inner blocks for error cases. What I mean by this is, check for the error condition and treat it in-place, don’t check for the not-error case and treat the error in an else far far away. An example is in order…


// Don't:
void collectPhazon() {
  PhazonDetector *detector = findNearestPhazonDetector();
  if(detector) {
    Phazon *nearestPhazon = detector->scanForPhazon();
    if(nearestPhazon) {
      sendPhazonCollectorDrone(nearestPhazon->position());
    } else {
      beep(kNoPhazonFoundBeep);
    }
  } else {
    beep(kMajorlyCatastrophicErrorBeep);
  }
}

// Do:
void collectPhazon() {
  PhazonDetector *detector = findNearestPhazonDetector();
  if(!detector) { beep(kMajorlyCatastrophicErrorBeep); return; }
  
  Phazon *nearestPhazon = detector->scanForPhazon();
  if(!nearestPhazon) { beep(kNoPhazonFoundBeep); return; }
  
  sendPhazonCollectorDrone(nearestPhazon->position());
}

This also applies to smaller scopes, such as for and while loops. Instead of having a big if block inside the loop, say if(error_condition) continue;. Feel free to use gotos as well, eg:


  Baz *a() {
    ...
    if(error) goto a_cleanup;

    return myBaz;
  a_cleanup:
    free(myBaz);
    fclose(bazHandle);
    return NULL;
  }

The same effect can be achieved with try-catch-finally, so use whichever makes your code the easiest to understand.


Secondly, never use just a boolean NO or a nil return value as an error return for a method or function that can fail in more than one way. If you do, the user of your library (or yourself, if it’s in your app!) can’t know what actually went wrong.

This leads to error dialogs such as “Couldn’t connect to iPod” — why not? Because one isn’t connected? Because it’s the wrong model? Because I hit it with a hammer? The sentence is lacking a ‘because’ because the reason is hidden behind bad abstractions.

This is where NSError is your friend, and I think that this is the answer to Scotty’s burning question: ‘When should I use NSError’? The answer is simple: whenver there’s more than one way to fail; whenver a NO/nil can mean more than one kind of failure.


Third, and this was actually mentioned, if you’re checking for errors, make sure you understand /why/ you’re checking for that error, and what the logical response is.

This snippet from Apple’s sample “ComplexPlayThru” is a perfect example of how not to do it (ComplexPlayThru.cpp, line 353-354):


  comp = FindNextComponent(NULL, &desc);
  if (comp == NULL) exit (-1);

Nice! My app suddenly disappeared with no explanation whatsoever! And I’m sure no other part of the app uses the error code ‘-1’! Awesome. Also, the component that they’re looking for is an Apple default, must-be-there-or-any-sound-app-will-crash component. It’s safe to assume that it will always be there, and just skip the error check.

This also goes against Gus’ advice: please don’t just copy any sample code you find, even if it’s from Apple.

The diNovo Mini has a dedicated ctrl-alt-del button. Need I say more?

The diNovo Mini has a dedicated ctrl-alt-del button. Need I say more?

Mr. Random Is My Friend

This is Mr. Random.

21:35:26 nevyn@xephon:~$ cat bin/rnd
  #!/usr/bin/env ruby
  class Array
    def random
      self[Kernel.rand(self.length)]
    end
  end


  if $0 == __FILE__
    puts $*.random
  end

This is how you use Mr. Random.

21:38:38 nevyn@xephon:~$ rnd pizza chinese leftovers
leftovers 

The best thing is how it makes you realize what you don’t want.

21:38:50 nevyn@xephon:~$ rnd pizza
pizza