How not to write a preferences system

In conspire, we have gotten to a point, where we need to rewrite the preferences system. I’ll go over how the current one works, though, in this blog post, as advice on how NOT to do it. Well, actually, there are many things about xchat’s preferences engine which are good, it’s just that there’s also many things which are not. ;)

First of all, all of the preferences are stored in a single struct. This is actually IMPORTANT for how the current XChat prefs system works (everything is an offset from the struct’s base. more on that later.):

 struct xchatprefs
{
        char nick1[NICKLEN];
        char nick2[NICKLEN];
        char nick3[NICKLEN];
        ...
};

Like that, above.

Now, about the offset stuff. This is just the most amazing pile of craq I have seen in a while (and I know craq, I forked XMMS/BMP classic after all). Here’s the code:

#define STRUCT_OFFSET_STR(type,field) \
( (unsigned int) (((char *) (&(((type *) NULL)->field)))- ((char *) NULL)) )
#define STRUCT_OFFSET_INT(type,field) \
( (unsigned int) (((int *) (&(((type *) NULL)->field)))- ((int *) NULL)) )

#define P_OFFSET(field) STRUCT_OFFSET_STR(struct xchatprefs, field),sizeof(prefs.field)
#define P_OFFSETNL(field) STRUCT_OFFSET_STR(struct xchatprefs, field)
#define P_OFFINT(field) STRUCT_OFFSET_INT(struct xchatprefs, field),0
#define P_OFFINTNL(field) STRUCT_OFFSET_INT(struct xchatprefs, field)

Ok, now, here’s what it does.

The macro STRUCT_OFFSET_STR(type, field) calculates the offset of a struct member from the struct’s base by defining a pointer to a structure of the same type at NULL, creating a reference to the struct member, and subtracting the base address from it.

The macro STRUCT_OFFSET_INT(type, field) calculates the offset of a struct member from the struct’s base by defining a pointer to a structure of the same type at NULL, creating a reference to the struct member, and subtracting the base address from it.

The macro P_OFFSET(field) calculates the offset of a member from the preferences struct’s base by using STRUCT_OFFSET_STR(type, field). It additionally defines the size of the field’s type.

The macro P_OFFSETNL(field) calculates the offset of a member from the preferences struct’s base by using STRUCT_OFFSET_STR(type, field).

The macro P_OFFINT(field) calculates the offset of a member from the preferences struct’s base by using STRUCT_OFFSET_INT(type, field). It additionally defines the size of the field’s type.

The macro P_OFFINTNL(field) calculates the offset of a member from the preferences struct’s base by using STRUCT_OFFSET_INT(type, field).

In reality, there are no differences between P_OFFSET and P_OFFINT. These values are used to populate an array of this struct type:

struct prefs
{
        char *name;
        unsigned short offset;
        unsigned short len;
        unsigned short type;
};

These structs form a table which contains all of the preferences which are stored in the configuration file and settable via the /set command. There are three types:

#define TYPE_STR 0
#define TYPE_INT 1
#define TYPE_BOOL 2

These types indicate how they should be saved, etcetera.

Now how does the GUI frontend manipulate these settings? Great question! By using the P_OFFSETNL and P_OFFINTNL macros.

The rewrite of this will be discussed in a later blog entry.