I am coding in a framework written in C and not compatible with C++. When implementing a data structure for sets as doubly-linked lists, I used macros like these to achieve generics.
/* Interface protocol:
* 1. Do not modify contents of nodes in a list. Results should be regarded
* as returned instead of modified in the arguments. The old list shall
* not be used.
* 2. Use RA_DECL_DL (T) to declare the doubly linked list and related oper
* for type T. If RA_DECL_DL (T) is used in an `.h' file, put it in
* #define GUARD_RA_FL_<T> #ifdef GUARD_RA_FL_<T> ... #endif
* to prevent re-declarations.
* 3. A function int _<T>_eq (T x, T y) must be declared in that file, before
* RA_DECL_DL and RA_DEFN_DL.
* 4. An RA_DL (T) can be used both as a set or a doubly linked list, but not
* simultaneously for a particular instance. */
#define RA_DECL_DL(T)
typedef struct _##T##_DL_ *T##_DL_t;
struct _##T##_DL_
{
T val;
T##_DL_t next;
T##_DL_t prev;
};
T##_DL_t _##T##_DL_empty ();
T##_DL_t _##T##_DL_insert (T x, T##_DL_t lst);
T##_DL_t _##T##_DL_remove (T x, T##_DL_t lst);
int _##T##_DL_isIn (T x, T##_DL_t lst);
int _##T##_DL_isEmpty (T##_DL_t lst);
T##_DL_t _##T##_DL_union (T##_DL_t l1, T##_DL_t l2);
T##_DL_t _##T##_DL_intersect (T##_DL_t l1, T##_DL_t l2);
T##_DL_t _##T##_DL_diff (T##_DL_t l1, T##_DL_t l2);
#define RA_DEFN_DL(T)
T##_DL_t _##T##_DL_empty () { return NULL; }
T##_DL_t _##T##_DL_insert (T x, T##_DL_t lst)
{
T##_DL_t ret = checked_malloc (sizeof (struct _##T##_DL_));
if (lst == NULL)
{
ret->val = x;
ret->next = NULL;
ret->prev = NULL;
return ret;
}
else
{
for (T##_DL_t iter = lst; iter != NULL; iter = iter->next)
{
if (_##T##_eq (iter->val, x))
return lst;
}
ret->val = x;
ret->prev = NULL;
ret->next = lst;
lst->prev = ret;
return ret;
}
}
T##_DL_t _##T##_DL_remove (T x, T##_DL_t lst)
{
T##_DL_t iter;
for (iter = lst; iter != NULL; iter = iter->next)
{
if (_##T##_eq (iter->val, x))
break;
}
if (iter == NULL)
return lst;
if (iter->prev != NULL)
iter->prev->next = iter->next;
if (iter->next != NULL)
iter->next->prev = iter->prev;
return iter->prev == NULL ? lst->next : lst;
}
int _##T##_DL_isIn (T x, T##_DL_t lst)
{
for (T##_DL_t iter = lst; iter != NULL; iter = iter->next)
{
if (_##T##_eq (iter->val, x))
return 1;
}
return 0;
}
T##_DL_t _##T##_DL_union (T##_DL_t l1, T##_DL_t l2)
{
T##_DL_t ret = _##T##_DL_empty ();
for (T##_DL_t iter = l1; iter != NULL; iter = iter->next)
ret = _##T##_DL_insert (iter->val, ret);
for (T##_DL_t iter = l2; iter != NULL; iter = iter->next)
ret = _##T##_DL_insert (iter->val, ret);
return ret;
}
T##_DL_t _##T##_DL_intersect (T##_DL_t l1, T##_DL_t l2)
{
T##_DL_t ret = _##T##_DL_empty ();
for (T##_DL_t iter = l1; iter != NULL; iter = iter->next)
{
if (_##T##_DL_isIn (iter->val, l2))
ret = _##T##_DL_insert (iter->val, ret);
}
return ret;
}
T##_DL_t _##T##_DL_diff (T##_DL_t l1, T##_DL_t l2)
{
T##_DL_t ret = _##T##_DL_empty ();
for (T##_DL_t iter = l1; iter != NULL; iter = iter->next)
{
if (!_##T##_DL_isIn (iter->val, l2))
ret = _##T##_DL_insert (iter->val, ret);
}
return ret;
}
#define RA_DL(T) T##_DL_t
#define RA_DL_empty(T) _##T##_DL_empty ()
#define RA_DL_insert(T, x, lst) _##T##_DL_insert (x, lst)
#define RA_DL_remove(T, x, lst) _##T##_DL_remove (x, lst)
#define RA_DL_isIn(T, x, lst) _##T##_DL_isIn (x, lst)
#define RA_DL_union(T, l1, l2) _##T##_DL_union (l1, l2)
#define RA_DL_intersect(T, l1, l2) _##T##_DL_intersect (l1, l2)
#define RA_DL_diff(T, l1, l2) _##T##_DL_diff (l1, l2)
The problem is: if for two files, say f1.c
and f2.c
, I write RA_DECL_DL(int)
and RA_DEFN_DL(int)
in both files, then, for example, the function _int_DL_insert
is defined twice globally. This causes a conflict between identifiers, though they are actually identical.
Using #ifdef
guard macros does not solve this problem because macros are not seen across .c
files. Using static
prevents me using the functions in multiple files, which is sometimes needed.