Strange C Preprocessor/Macro Problem

Started by
9 comments, last by callmewhitey 16 years, 6 months ago
Hello, I've got a program written in C, and I'm trying to modify it so that people who aren't experienced with programming can modify the program. The idea was to create a sort of scripting language using macros, so that something like

BEGIN_FUNCTION( func1 )
  set xpos to 0
  add 5 to ypos
END_FUNCTION

would turn into something like

void func1(Object* o)
{
  o->xpos = 0;
  o->ypos += 5;
}

So then I started trying to create the macros... it was a little hackey, but I came up with the following:

#define to ,
#define BEGIN_FUNCTION(name) void name(Object* o) { do{}while(0
#define set ); set2(
#define add ); add2(
#define END_FUNCTION ); }

Which after being run through the preprocessor creates something like

void func1(Object* o)
{
  do{} while(0);
  set2(xpos, 0);
  add2(5, ypos);
}

Ok, looks good. All that's left to do is create the set2 and add2 macros, right?

#define set2(var, val) o->var = val
#define add2(val, var) o->var += val

Wrong! The compiler gives "unterminated argument list invoking macro "set2"". If I just paste the code in from the preprocessor output before defining set2 and add2, and then define them, it works just fine. Or by making set2 and add2 functions. Or by changing the script syntax so the user just types something like set(xpos, 0). I'm just curious to know, why the hell doesn't this work?
Advertisement
#define set2(var, val) o->var = val
#define add2(val, var) o->var += val

I think what you want with the above is

#define set2(var, val) o->##var = val
#define add2(val, var) o->##var += val

*EDIT* Should explain why

In macro's the ## is for concatenation, otherwise it can't evaluate that the var should be referencing the parameter.
Why are you doing this? This is Not A Good Idea.

If you want to give programmatic control over your program to a non-technical person, use a scripting language. What you're trying to do is silly and dangerous -- and ultimately, will likely make it harder for a non-programmer to modify the behavior of the program. Even ignoring the fact that the non-programmer will still need to understand a fair bit of the concepts behind turning C into an executable.

Natural language is useless ambiguous; what you seem to be trying to do is make C more like a natural language. To a non-technical person unfamiliar with the rigidity of most programming languages, and the requirements of syntax and language grammar, this just a fire waiting to happen. The user will tend to be lulled by the natural flow of the "language" and type something that they think should work, but will have no fallback path in your brittle macro language.

Then they'll be faced with REEMS of compile errors referring to code they didn't even write, and can't even see. Your macrolanguage is thus more usable only to those who know how to use it perfectly, which is to say, likely only programmers who can understand how it's constructed. Which mean you've already lost.
You might want to take a look at the articles "Implementing a State Machine Language" and "Enhancing a State Machine Language through Messaging" in AI Game Programming Wisdom. Steve Rabin has implemented something similar to this (a quasi-scripting language implemented with macros) which is discussed in the afore mentioned articles.

That said, I agree with jpetrie; this isn't the best idea. I don't know if Rabin's SML has been used in production code, the only use for it that I am aware of is in an academic setting.
Agreed ^

Although, it's worth noting that paramatized macros are notorious for being *tricky*. As a general rule, each parameter should be enclosed with parenthesis as to prevent any ambiguous cases... And... I can really see no good reason for going down this path ( although you should by all means continue if you think it's neat ). Maybe just using an actual scripting language would be easier for all involved.
Using the C preprocessor is a bad idea. While it might work if the code is perfect, a beginner is unlikely write perfect even with your simplified syntax and the result will be a mess of unintelligible error messages.

A better solution is to write a proper compiler (an interpreter would be even better). Look up info on flex and bison (or lex and yacc). I haven't used those tools in a long time so they could be obsolete by now. Also, you can write the compiler to output C code if that is what you need.
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
Quote:
A better solution is to write a proper compiler (an interpreter would be even better). Look up info on flex and bison (or lex and yacc). I haven't used those tools in a long time so they could be obsolete by now. Also, you can write the compiler to output C code if that is what you need.

I like ANTLR better, although it's nearly as tough to learn if you've never done anything with parser generation before.
This is a limitation of C macros. Each macro is fully expanded on usage, so this leads to problems like this:

This compiles:
#define set ); set2(

This doesn't compile:
#define set ); set2(
#define set2(x, y)

set2 is defined to be a macro taking 2 parameters and you are passing in zero parameters and not closing the braces.

You can probably make this work with some sort of multi-pass preprocessing scheme, but you are still limited by the fact that C preprocessor can't do this:

#define #define foo

I suggest using a scripting language for this, C preprocessor is way too limited.
deathkrushPS3/Xbox360 Graphics Programmer, Mass Media.Completed Projects: Stuntman Ignition (PS3), Saints Row 2 (PS3), Darksiders(PS3, 360)
Wow, not quite the type of replies I expected, thanks for all the input though! I guess what I failed to mention is that this a small side project at a job I work at part-time, so I don't exactly have the time to write my own compiler, create an entire scripting language, or anything of that nature. If anyone's got something better that can be implemented in an hour or two, I'm all ears...

All I was looking to do is allow people to modify a few objects without having to mess around with pointers, structs, etc. You were right about natural language being a bad idea, so I'll be modifying it to be more C-like, while still hiding some of the more complicated aspects of the language. Yes, the fact that the compiler errors don't resemble the actual written code is a problem, but the compiler still points to the line causing the error, so it's usually pretty easy to figure out what's wrong. Like I said, I needed a quick solution.

Thanks again for all the suggestions!
You should give Lua a shot. It's an easy language to embed, easy to learn and quite fast. It'll do everything you're asking for in a far more "safe" way.

-Piro

This topic is closed to new replies.

Advertisement