If anyone ever wants to compare structs using the method I described above, here's some code that actually compiles and tests as far as the testcases here allow:
Code:
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>

typedef enum { VOID = 0, INT, CHARARRAY, CHAR, CHARPTR, FLOAT, DOUBLE, STRUCT, STRUCTPTR } EType; 

typedef ptrdiff_t offset_t;

typedef struct SElement
{
	char *name;   // Not actually needed - just because the macro CAN DO THAT. 
	offset_t offs;
	enum EType type;
} Element;



typedef struct SExample
{
	int   a;
	char *str1;
	char str2[40];
	char b;
} Example;

#define ELEM(structname, name, type)   { #name, offsetof(structname, name), type }

Element exampleDescr [] = 
{
	ELEM(Example, a, INT),
	ELEM(Example, str1, CHARPTR),
	ELEM(Example, str2, CHARARRAY),
	ELEM(Example, b, CHAR)
};

typedef struct SExample2
{
	int   a;
	float f;
	double d;
	char b;
} Example2;


Element example2Descr[] =
{
	ELEM(Example2, a, INT),
	ELEM(Example2, f, FLOAT),
	ELEM(Example2, d, DOUBLE),
	ELEM(Example2, b, CHAR)
};

int CompareStructsGeneric(void *struct1, void *struct2, Element *descr, size_t nElements)
{
	size_t i;
	for(i = 0; i < nElements; i++)
	{
		void *elem1, *elem2;
		int cmp;
		char *s1, *s2;
		double d1, d2;

		elem1 = (char *)struct1 + descr[i].offs;
		elem2 = (char *)struct2 + descr[i].offs;

		switch(descr[i].type)
		{
		case INT:
			if (*(int *)elem1 > *(int *)elem2)
				return 1;
			else if (*(int *)elem1 < *(int *)elem2)
				return -1;
			break;   /* Equal, continue to next element ... */
		case CHAR:
			if (*(char*)elem1 > *(char*)elem2)
				return 1;
			else if (*(char*)elem1 < *(char*)elem2)
				return -1;
			break;   /* Equal, continue to next element ... */
		case FLOAT:
			d1 = *(float *)elem1;
			d2 = *(float *)elem2;
			// Avoid code duplication at expense of using goto!
			goto fcompare;
		case DOUBLE:
			d1 = *(double *)elem1;
			d2 = *(double *)elem2;
fcompare:
			if (d1 > d2)
				return 1;
			else if (d1 < d2)
				return -1;
			break;
		case CHARPTR:
			elem1 = *(char **)elem1;
			elem2 = *(char **)elem2;
			// Clever-tricks: fall-throug!
		case CHARARRAY:
			s1 = elem1;
			s2 = elem2;
			cmp = strcmp(s1, s2);
			if (cmp) return cmp;
			break;
		default: 
			printf("Incorrect or unimplemented case: %d\n", descr[i].type);
			break;
		}
	}
	return 0;
}

Example a = { 1, "Hello", "World!", 'a' };
Example b = { 2, "Hello", "World!", 'b' };
Example c = { 1, "Hello", "World!", 'a' };
Example d = { 1, "Hello", "World!", 'd' };

Example2 a2 = { 1, 5.0f, 6.0, 'a' };
Example2 b2 = { 1, 5.1f, 6.0, 'a' };
Example2 c2 = { 1, 5.0f, 6.0-0.001, 'a' };

struct TestCase
{
	char *name;
	void *s1, *s2;
	Element *descr;
	size_t nelem;
	int res;
};

#define ARRAYSIZE(x) (sizeof(x)/sizeof((x)[0]))

#define TC(name, s1, s2, ds, res) { #name, &s1, &s2, ds, ARRAYSIZE(ds), res }
struct TestCase testCases[] = 
{
	TC(equal1,   a,  a,  exampleDescr, 0),
	TC(equal2,   a,  c,  exampleDescr, 0),
	TC(greater1, b,  a,  exampleDescr, 1),
	TC(lesser1,  a,  b,  exampleDescr, -1),
	TC(equal3,   a2, a2, example2Descr, 0),
	TC(equal3,   a2, a2, example2Descr, 0),
	TC(greater2, b2, a2, example2Descr, 1),
	TC(lesser2,  c2, a2, example2Descr, -1),
	TC(lesser3,  a,  d,  exampleDescr, -1)
};

void expect(const char *name, int expected, int res)
{
	if (res != expected)
	{
		printf("%s: Expected %d, got %d\n", name, expected, res);
	}
}

int main()
{
	int i;
	for(i = 0; i < ARRAYSIZE(testCases); i++)
	{
		int res;
		printf("Performing test-case %s\n", testCases[i].name);
		res = CompareStructsGeneric(testCases[i].s1, testCases[i].s2, testCases[i].descr, testCases[i].nelem);
		expect(testCases[i].name, testCases[i].res, res);
	}
	return 0;
}
Note, I know goto and fall-through switches is bad. Doing lots of duplication of code is ALSO bad - it's a balance, and I'm not feeling like typing a lot more than necessary, so went for the first nasty variant rather than the second one. [All of the non-string comares could also be turned into a multiline macro, but I do not like multiline macros much, so since it's actually OK to write it out, I did].

--
Mats