Saturday, September 13, 2014

A trivial way to work around boolean traps and bad APIs

Long time no blog...

In Ariya's blog from 2011, there were interesting ideas about good/bad APIs pertaining to booleans. To repeat, the main point, an API may be not so great if you can't understand the meaning of its parameters from the calling site.

For example (to recap), looking at an Xlib call site:

XClearArea(myDisplay, 0, 0, 100, 100, true);

What does "true" mean here? Since I'm not an X11 expert I have to go to the documentation to know that this means "whether or not to send expose events".

Today I don't have to use X11 much, but this is a problem in APIs I do have to use more often, like OpenGL:

glUniformMatrix4fv(matrixLocation, 1, GL_FALSE, matrix);

GL_FALSE here means that the matrix should not be transposed, but this is not clear from the call site.

Now there are many ways to tackle this in the design of the API itself, for example:

enum ShouldTransposeMatrix { DontTransposeMatrix, TransposeMatrix };

But... when using existing APIs we don't have the privilege to redesign the API.
Also, despite of it solving the problem in the call site, something about it feels too verbose... 3 new words just to represent a boolean parameter for a single function.

So...

In a project I've been working on we developed a coding style principle that makes this problem almost entirely go away, and in a trivial fashion.
We put this line somewhere on the top:
typedef GLboolean ShouldTransposeMatrix;  


... and then the calling site looks like:

glUniformMatrix4fv(matrixLocation, 1, ShouldTransposeMatrix(GL_FALSE), matrix);


Only for booleans?

Of course not! By using this "optional strong-typing" style any non-obvious parameter can look much clearer in the call site, without changing the API itself.

typedef GLboolean ShouldNormalize;
typedef GLsizei AttribStride;
typedef GLint AttribSize;
typedef GLenum AttribType;

glVertexAttribPointer(index, AttribSize(4), AttribType(GL_FLOAT), ShouldNormalize(GL_FALSE), AttribStride(4), nullptr);

This example is maybe going too far but it's a matter of taste... I like using this style mainly for places where the call site makes the API obviously non-obvious, and most often than not with booleans.

But isn't this trivial? Why should I care? Give me my two minutes of reading this blog back!
It is. You shouldn't. I can't.


No comments:

Post a Comment