Wednesday, April 15, 2009

Unique handles

I was surprised today to find that there's no built-in UNIQUE function for arrays of handle derived objects.
>> X = [myClass(); myClass(); myClass()]; %Three unique instances of myClass
>> X = [X X]; %Double up -- two copies of each unique instance
>> unique(X)

??? Error using ==> double
Conversion to double from class1 is not possible.

Error in ==> unique at 92
a = double(a);

When you're working with arrays of handle graphics handles, handles are actually doubles, so any standard array-based function (e.g. permute, kron, ismember) will work on an array of handle graphics handles. This works because you're actually working with each graphics object's globally unique identifier (GUID).

The more recently released handle objects are represented by doubles in the same way; it's just that the underlying GUID is abstracted away so that its actual numeric value is never seen. This is a nice abstraction in some ways: myObj.Prop = val is much easier to read and write than set(myObj, 'Prop', val).

The fact that handle objects are still represented as GUIDs in the Matlab interpreter is evident when you check the built-in handle methods:
>> methods handle
Methods for class handle:

addlistener findobj gt lt
delete findprop isvalid ne
eq ge le notify
The relational operators gt, lt, ne, eq, ge, and le operate by calling the appropriate comparators on the GUIDs themselves. For some reason there are more handle methods listed in the documentation that aren't returned by the methods function: transpose, permute, reshape, and sort. Again, since arrays of handle objects are essentially arrays of GUIDs, these functions work exactly how we'd expect.

Of course, once we've got reshaping and relational operators, we can build most of the other functions we'd need. Here's a quick and dirty unique function:

function B = uniquehandles (A)
%UNIQUEHANDLES Find unique handle object instances

A = A(:); %Column vector
A = sort(A);
i = [true; A(1:end-1)~=A(2:end)];
B = A(i);
The problem here is obvious if you check out the code for the built in Matlab unique function: it's dominated by error-checking and code for maintaining dimensional consistency, and I really don't want to duplicate it all.

The weird thing is that despite the documentation, it seems like almost all array functions work with arrays of handles like repmat, sortrows, num2cell, and mat2cell. Even some of the set functions work: ismember and intersect work, but unique, setdiff, and setxor don't.

This happens because ismember and intersect are polymorphic. There isn't a separate handle.ismember function; it "just works" because the GUID basis allows arrays of handles to be treated like numeric arrays. The other set functions attempt to explicitly cast the input array to double, which results in the oh-so-aggravating "cannot convert to double" error.

No comments:

Post a Comment