Tuesday, November 28, 2006

Returning an Array in C - the Least of 3 Evils

In C++ you can write things like this:
void get_some_strings(vector& out_strings)
{
out_strings.clear();
for (some loop)
out_strings.push_back(whatever);
}
Easy! How do we do this if our API has to be pure C? Well, there are three options.

First you could always allow the function to allocate memory that's deallocated by the caller. I don't like this option at all. To me, it's a walking memory-leak waiting to happen. You'll have to check for memory leaks every time you use the function and there are no compile-time mechanisms to encourage good programming technique.

A second option is to pass in some preallocated buffers. Hrm - I think I tried that once. Unfortunately as you can read here, the technique is very error prone and it took Sandy (the better half of the SDK) to clean up the mess I made.

The third option, and least evil IMO is to use a callback. Now we have something like this:
void get_some_strings(void (* cb)(const char * str, void * ref), void * ref)
{
for (some stuff)
cb(some string, ref);
}
If you can tollerate the C function callbacks (have a few Martinis - they'll look less strange) this actually provides a very simple implementation, which means less risk of memory-management bugs.

On the client side if you're using this library from C++ you can even turn this back into the vector code you wanted originally like this:
void vector_cb(const char * str, void * ref)
{
vector * v = (vector*)ref;
v->push_back(str);
}
Now I can use this one callback everywhere like this:
vector my_results;
get_some_stuff(vector_cb,&my_results);
What I like about the callback solution is that both the client code and implementation code can be written in a simple and consise manner, which translates into solid code.

No comments:

Post a Comment