You could use XMacro to manage constants. This approach lets defining all constants in one place. Moreover arbitrary constant expression can be used. The solution works in C99 and above without any compiler specific extensions.
The main disadvantage is that a value of all constants must fit to int.
#define PRIO_TASK__XMACRO(X) \
X(PRIO_TASK_A, 1) \
X(PRIO_TASK_B, 2) \
X(PRIO_TASK_C, 3) \
X(PRIO_TASK_D, 1+1)
Instantiate the constant as a anonymous enum:
#define DECLARE_ENUM(NAME,VALUE) NAME = VALUE,
enum {
PRIO_TASK__XMACRO(DECLARE_ENUM)
};
As suggested in the comment by Jonathan Leffler, use switch to detect if a value was ever repeated.
static inline void check_unique_enums(int c) {
switch (c) {
#define CASE_ENUM(NAME, VALUE) case NAME: break;
PRIO_TASK__XMACRO(CASE_ENUM)
}
}
Compiling with gcc produced errors:
so.c: In function ‘check_unique_enums’:
so.c:15:32: error: duplicate case value
15 | #define CASE_ENUM(NAME, VALUE) case NAME: break;
| ^~~~
so.c:5:3: note: in expansion of macro ‘CASE_ENUM’
5 | X(PRIO_TASK_D, 1+1)
| ^
so.c:16:1: note: in expansion of macro ‘PRIO_TASK__XMACRO’
16 | PRIO_TASK__XMACRO(CASE_ENUM)
| ^~~~~~~~~~~~~~~~~
so.c:15:32: note: previously used here
15 | #define CASE_ENUM(NAME, VALUE) case NAME: break;
| ^~~~
so.c:3:3: note: in expansion of macro ‘CASE_ENUM’
3 | X(PRIO_TASK_B, 2) \
| ^
so.c:16:1: note: in expansion of macro ‘PRIO_TASK__XMACRO’
16 | PRIO_TASK__XMACRO(CASE_ENUM)
| ^~~~~~~~~~~~~~~~~