20
\$\begingroup\$

There are quite some questions and answers about how to makeenum really type-safe and I didn't find a solution that ensures both type safety and valid values. So I took some ideas and extended it to my own generic solution, somehow resembling toenum classes used in some other languages.

My solution adds the overhead of a pointer dereference for getting the actual enum value, but I think this is a fair deal for situations where you would otherwise have to check for the valid range in a lot of places. Comparing values works naturally with the== operator because there is only one global constant instance of each enum value.

I'm posting it here for review and comments if you like -- please tell me if you spot a bug or any other weakness in my idea. Is there anything I overlooked?

The "implementation" is just a header file:

csafenum.h

#ifndef CSAFENUM_H#define CSAFENUM_H#ifndef __GNUC__#define __attribute__(x)#endif#include <assert.h>/* declare an enum type: CSAFENUM_DECL(TypeName); */#define CSAFENUM_DECL(tname) struct tname##_struct; \    typedef const struct tname##_struct * tname; \    const char *tname##_name(tname e) __attribute__((nonnull(1))); \    int tname##_val(tname e) __attribute__((nonnull(1)))/* declare an enum member: CSE_DECL(TypeName, MemberName); */#define CSE_DECL(tname, name) extern const struct tname##_struct * const name/* define an enum type: CSAFENUM_DEF(TypeName); */#define CSAFENUM_DEF(tname) struct tname##_struct { \    int val; \    const char * const name; \}; \const char *tname##_name(tname tname##_cannot_be_null) \{ \    assert(tname##_cannot_be_null); \    return tname##_cannot_be_null->name; \} \int tname##_val(tname tname##_cannot_be_null) \{ \    assert(tname##_cannot_be_null); \    return tname##_cannot_be_null->val; \} \struct tname##_struct/* define an enum member: CSE_DEF(TypeName, MemberName, IntegerValue); */#define CSE_DEF(tname, name, v) \    static const struct tname##_struct name##_memb = { v, #name }; \    const struct tname##_struct * const name = &name##_memb#endif

Example usage:

fruit.h

#ifndef FRUIT_H#define FRUIT_H#include <csafenum.h>CSAFENUM_DECL(Fruit);CSE_DECL(Fruit, Apple);CSE_DECL(Fruit, Banana);CSE_DECL(Fruit, Pear);CSE_DECL(Fruit, Strawberry);#endif

fruit.c

#include "fruit.h"CSAFENUM_DEF(Fruit);CSE_DEF(Fruit, Apple, 0);CSE_DEF(Fruit, Banana, 1);CSE_DEF(Fruit, Pear, 2);CSE_DEF(Fruit, Strawberry, 3);

main program

#include <stdio.h>#include "fruit.h"int main(){    Fruit a, b, c, d;    a = Apple;    b = Banana;    printf("a = %s (%d)\n", Fruit_name(a), Fruit_val(a));    printf("b = %s (%d)\n", Fruit_name(b), Fruit_val(b));    c = Apple;    if (c == a) puts("c == a");    if (c == b) puts("c == b");    d = 0;    printf("d = %s (%d)\n", Fruit_name(d), Fruit_val(d));    return 0;}

Output

a = Apple (0)
b = Banana (1)
c == a
example: fruit.c:3: Fruit_name: Assertion `Fruit_cannot_be_null' failed.
zsh: abort ./example/example

As pointed out byDarkDust, this does not work inswitch blocks -- pointers aren't allowed to switch on. After thinking about this issue for a while, I get the feeling better not to try solving this, because it would either weaken or overcomplicate (or both) the whole thing.

The whole code is onGitHub.

askedJul 7, 2015 at 9:58
Felix Palmen's user avatar
\$\endgroup\$
1
  • 1
    \$\begingroup\$I found this and your SO answer (stackoverflow.com/a/31165425/20371 ) both via Google and I was about to write something snarky about how this was copied from that, but then saw it's the same guy! Hah.\$\endgroup\$CommentedDec 11, 2016 at 22:36

1 Answer1

3
\$\begingroup\$

Another problem with making the enum values pointers, is that it changes the in-memory representation of an enum. There are many situations where it's necessary for the enum to be an int (such as reading/writing structs to/from files).

If you need type-checked enums, it is better to use C++.

answeredApr 27, 2017 at 15:36
David Jeske's user avatar
\$\endgroup\$

You mustlog in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.