4
votes

I am trying to write a macro to assist with object oriented programming in C. As I store the class information in a constant struct, I need to create a macro that does the following:

  • Take the type of the object (typeof the derefenced pointer)
  • Append _info to get the name of the desired classInfo struct
  • Take the address of that symbol so it can be passed to the function
  • Call the destroyObject function with a pointer to the class struct and the object itself

An example:

queue_t* p = NULL;
delete(p);

delete should expand to:

destroyObject(&(queue_t_info), p);

I tried using this macro, but I can't get to to work:

#define delete(X) (destroyObject(&(typeof(*X)##_info), X))

I'm having trouble with the typeof part to work correctly.

2
You seem to think types have names. - melpomene
So it's not at all possible to get the preprossor to spit out int when passed a reference to an integer? - charliehorse55
@melpomene Why do you think that they do not? - Lightness Races in Orbit
Actually, thinking about this more I realize the preprocessor hasn't parsed my code yet so it can't associate variables with types. I came up with a decent solution to get around this. I'll post it as an answer to the question. - charliehorse55
The worst thing you can do when programming is trying to make the language (C here) into (sort of) something it isn't (OO, here). It will just royally confuse your reader (probably yourself in a few weeks) or even trick the compiler into being silly/generate wrong code. Want C-like OOP? Go for C++. - vonbrand

2 Answers

7
votes

typeof isn't macro, it is language construction and it is expanded by compiler, not preprocessor. Since preprocessing goes before compilation, macro can't access typeof result.

Your delete(p) is expanded to: (destroyObject(&(typeof(*p)_info), p)). (You can see it by -E gcc flag)

2
votes

I realized that what I was attempting to do was impossible - the C preprocessor doesn't parse and symbolize the code so it doesn't know which type a variable is.

To solve this, I require the type to be passed into the delete function as well. This isn't ideal as it introduces a frequent source of bugs due to mismatched types. If a programmer passes a pointer to object A but specifies object B in the delete function, the wrong destructor would be called. To solve this, I added a typecheck to the macro so that a compiler warning would be generated for any mismatched types.

#define typecheck(type,x) \
({  type __dummy; \
typeof(x) __dummy2; \
(void)(&__dummy == &__dummy2); \
})

#define delete(P, X) (destroyObject(&(X##_info), P), typecheck(X, *P))
#define new(X, ...) (createObject(&(X##_info), ##__VA_ARGS__))

Normal usage of the macro:

queue_t* p = new(queue_t);
delete(p, queue_t);

However using the wrong type:

queue_t* p = new(queue_t);
delete(p, int);

causes a compiler warning:

Comparison of distinct pointer types ('int *' and 'typeof (*p) *' (aka 'queue_t *'))