0

I want to simulate classes in c, and hide the implementation with macros, but i got unexpected expansion behaviour of the macros.

#define decl_class struct class { void *base ## ;
#define end_class } ## ; typedef struct class class ## ;
#define decl_methods struct class ## Methods {
#define method(returnType, methodName, ...) returnType (*methodName)(struct class *self, __VA_ARGS__) ## ;
#define end_methods } ## ;


#define class Integer
decl_class

    int value;

    decl_methods
        method(int, getValue)
    end_methods

end_class

#undef class


#define class Double
decl_class

    double value;

    decl_methods
        method(double, getValue)
    end_methods

end_class

#undef class

The compiler says that i am declaring twice the struct classMethods(class should be the name of the class instead). This means that "class" doesn't get replaced when i want it to. Is it even possible to do so?

Petok Lorand
  • 955
  • 6
  • 15
  • Don't get too fancy with macros, trying to invent a new language with them! This is already close to undebuggable/unmaintainable. If you need full abstration by classes, use a language with OOP support. – too honest for this site May 02 '17 at 12:54
  • 1
    What is this `##` nonsense? – Karoly Horvath May 02 '17 at 12:56
  • The ## concatenates the macro with a string. It is also necessary when you want to put semicolon in the macro – Petok Lorand May 02 '17 at 12:57
  • 2
    No, it isn't necessary (unless, alas, you have some horrible compiler..) – Karoly Horvath May 02 '17 at 13:00
  • 2
    `##` is used required to concatenate two tokens. It only makes sense for one or two arguments and an identifier or integer constant. It has definitively nothing to do with semicolons which are tokens on their own right. Before even thinking about such obfuscation, understand what the features used actually do. Start with something simpler and see the output of the preprocessor. – too honest for this site May 02 '17 at 13:19
  • @Lorand, did you try to check your preprocessor output? If you see what is created, it might help to find what's wrong. – Gerhardh May 02 '17 at 13:29
  • Inventing a new language with macros is indeed a horrible idea. It is actually quite possible to write OO programs in C, by using the concept of opaque types and function pointers. Autonomous classes, true private/protected encapsulation and polymorphism can all be achieved in C, although it is not as easy as in other languages. What you can't achieve in C is handy language features such as automatically called constructors/destructors and RAII, but those features are not _necessary_ for OO design (although very convenient). – Lundin May 02 '17 at 13:52
  • Thank you for the responses. @Lundin do you have any useful links from where i can learn more from? – Petok Lorand May 02 '17 at 14:19
  • @Lorand Not sure how much there is available on the internet. [Here's the basics of "opaque type"](http://stackoverflow.com/questions/13032015/how-to-implement-a-class-in-c/13032531#13032531). – Lundin May 02 '17 at 14:23
  • 1
    You can look up 'object-oriented c book' in a search engine. Google reveals a number of them, including [Object-Oriented Program with ANSI-C](https://www.cs.rit.edu/~ats/books/ooc.pdf). I have reservations about that style; it uses `void *` everywhere instead of separately named structure pointers. It means you lose type safety; you can pass a list to a function that expects a set because they're both a `void *` in the interface. But there's also useful information there. Have fun. Try not to reinvent C++ — they've already done a lot of work there. – Jonathan Leffler May 02 '17 at 14:32

1 Answers1

2

Your first problem is that

#define end_methods } ## ;

is a syntax error (if the macro is expanded), because the result of the token paste is not a single valid token. You should have gotten error messages like

error: pasting "}" and ";" does not give a valid preprocessing token

Your second problem is that token pastes are executed before nested macro expansion. That means your macro

#define decl_methods struct class ## Methods {

is effectively the same as if you had written

#define decl_methods struct classMethods {

To get it to do what you want, class must be a formal parameter to a function-like macro:

#define decl_class(class)   struct class {
#define end_class(class)    }; typedef struct class class;
#define decl_methods(class) struct class ## Methods {
#define end_methods(class)  };
#define method(class, returnType, methodName, ...) \
    returnType (*methodName)(struct class *self, __VA_ARGS__);

and then

decl_class(Double)
    double value;
    decl_methods(Double)
        method(Double, double, get_value);
    end_methods(Double)
end_class(Double)

I suppose you could avoid having to repeat the name of the class in every macro invocation by having an additional set of macros that stick the class pseudo-argument in there, but (for reasons too tedious to get into here; read the "Argument Prescan" section of the GNU CPP manual very carefully) you will need two layers of nested expansion to get the effect you want:

#define decl_class__(class_)   struct class_ {
#define decl_class_(class_)    decl_class__(class_)
#define decl_class             decl_class_(class)

#define decl_methods__(class_) struct class_ ## Methods {
#define decl_methods_(class_)  decl_methods__(class_)
#define decl_methods           decl_methods_(class)

/* etc */

This is technically only required when the innermost macro needs to use ## (or #) but if you're seriously going to use these macros in a real program, you should do it uniformly for all of them otherwise you'll be tearing your hair out six months later.

And after you get past all of that you will discover that your method macro doesn't work right for zero-argument methods, e.g.

#define class Integer
method(int, getValue)

either throws an error because, in standard C, ... in a macro parameter list must receive at least one argument, or it expands to a syntactically invalid declaration,

int (*getValue)(struct Integer *self, );

The only way to work around this one is to use a GNU extension:

#define method__(class_, returnType, methodName, ...) \
    returnType (*methodName)(struct class_ *self, ##__VA_ARGS__);

In GNU extended C, ## in between , and __VA_ARGS__ has the special effect of causing the comma to be deleted when the ... received no arguments. (This extension was proposed for standardization about 15 years ago, but the committee wasn't interested.)

At this point I invite you to reconsider the possibility of just using C++ instead.

zwol
  • 135,547
  • 38
  • 252
  • 361
  • I have tried #define decl_class_(class_) struct class_ { #define decl_class decl_class_(class) but it won't work. I just wanted to avoid using function like macros to don't writ the classname everytime. Also thank you for pointing out the semicolon relating problem. – Petok Lorand May 02 '17 at 14:22
  • @Lorand This is more fun than what I'm supposed to be doing right now, so I figured out the remaining problems, see edits. In the future, though, please please never say "it doesn't/won't work" without giving any detail. We don't know what you are seeing on your screen unless you tell us. – zwol May 02 '17 at 14:43
  • Wow, that is what i was looking for. Thank you very much for your help, i really appreciate it – Petok Lorand May 02 '17 at 15:01