[Cs22800] enlightenment style question

Jon Trowbridge trow at ximian.com
Sat Nov 16 18:31:39 CST 2002


Here are my 2 cents, along with some random observations about C API
aesthetics.

First and most important: if there is a convention used by the rest of
the Enlightenment APIs, follow it.  Sometimes being consistent is a
greater virtue than being perfectly engineered.

OK, now that I've gotten that disclaimer out of the way, here are a
few thoughts on how to be perfectly engineered.

In my experience, the first thing to think about when returning
complex data objects[1] are life-cycle issues.  Since C is one step
above flint knives and bearskins, the poor user has to correctly
manage any chunks of memory that you hand back to him.  You want to
make this task as easy as possible for the user --- hence the benefit
of consistency.

> hypothetical taskbar writer consists of a list of names, each with a 
> number and a pointer.

You almost certainly want to assemble some kind of struct that
contains the window information, and then hand the user a sequence of
them.  Don't hand back N lists (a name list, a number list, a pointer
list, etc.), since that is a giant pain in the ass to iterate over.

So you'd have something like:

/* Typing 'struct' all of the time is very passe./
typedef struct _TaskbarInfo {
  char *name;
  int  *a_number;
  void *a_pointer; /* or whatever... */
} TaskbarInfo;

Hmm... but there is a string inside of the struct -- so whoever frees
the structure has to make sure to also free the name.  Please don't
expect the user to do something like

  free (my_taskbar_info->name)
  free (my_taskbar); 

because that would lead to pain and memory leaks when someone adds
another allocated field to the struct six months from now.  So be
polite and provide some sort of explicit destructor.  IMO a destructor
should always be provided in this kind of situation, even if it just
ends up being a wrapper around a call to free: it is best to be
defensive.  This makes things pretty simple for the user; they just
have to remember to call taskbar_info_free on any TaskbarInfo items
they need to dispose of.

Of course, there is another layer to the problem that I've ignored so
far: you aren't returning just one of these guys, but you have a bunch
of structs to deal with.

I'd be surprised if E didn't have some library full of typical data
structures like lists and hashes already coded up in C.  I'd advise
you not to use them, except maybe if:
  * They are smart and let you attach destructors for the data
    objects, so that freeing the list doesn't leak all of the objects
    in the list.
  * The Enlightenment convention is to return these kinds of objects.
  * There is absolutely no other way to do what you want to do in a
    clean way.

In the GNOME, there is a library full of data structures called glib,
but some people[2] considered it bad form to return them from any
user-visible API --- the argument generally boil down to life-cycle
questions or the lack of type safety, but in the end it is primarily a
aesthetic/religious question[3].

A simple alternative is to return a NULL-pointer-terminated array of
pointers.  These are easy to iterate over, though it is still good
manners to provide a whole-array destructor.

Though it is probably beyond the scope of what you'd want to do here,
sometimes it is nice to abstract away all of the memory management
issues, particularly if the semantics are complicated[4] or you think
they may change.  I really like using a functional programming-type
idiom --- which works just fine in C.  Your API would look something
like this:

typedef void (*TaskbarInfoFn) (const char *name,
                               int         a_number,
                               void       *a_pointer,
                               void       *closure);

void get_taskbar_info (TaskbarInfoFn fn, void *closure);

All memory-management questions are now gone: the get_taskbar_info
function can manage all of the memory and clean up after itself as
necessary.

In case you are confused, here is an example of using this kind of
API to write the window info to a file:

static void 
write_info_callback (const char *name,
                     int         a_number,
                     void       *a_pointer,
                     void       *closure)
{
  FILE *out = closure;
  fprintf (out, "%s (id=%d)\n", name, a_number);
}

void 
write_taskbar_info_to_stream (FILE *out)
{
  get_taskbar_info (write_info_callback, out);
}

Warning: C programmers tend to freak out when you do this sort of
thing.  They are usually the same misguided people who don't like
Lisp.  But if you are stuck working in C, this idiom can be a
life-saver.

-JT


[1] i.e., something other than an int, double, etc.

[2] Here when I say "some people", I really mean "I", but I'm not
    the only person who feels this way.  Honest.

[3] However, that doesn't mean it isn't important.  If your code is
    aesthetically pleasing, it is more likely to be well-thought-out
    and correct.  And it will also be more readable, which vastly 
    simplifies future maintenance.

[4] Classic example: when you are caching data and you don't want to
    let the user decide when to free it.  And you also don't feel like
    adding full-blown reference counting to your objects, which would
    be overkill in many cases.






More information about the CS22800 mailing list