ID:135982
 
When I write a multi-line macro like this:
#define MyMacro(x) \
    if (x) \
        usr << "Foo!" \
    else \
        usr << "Bar!" \
        usr << "Baz!"
it fails to compile. Even if I add semicolons after the three expression statements:
#define MyMacro(x) \
    if (x) \
        usr << "Foo!"; \
    else \
        usr << "Bar!"; \
        usr << "Baz!";
it doesn't behave as the indentation suggests it should. The observed behaviour is that the last expression statement is not a part of the else clause of the if statement. In order to achieve the desired behaviour I have to resort to "brace syntax":
#define MyMacro(x) \
    if (x) \
    { \
        usr << "Foo!"; \
    } \
    else \
    { \
        usr << "Bar!"; \
        usr << "Baz!"; \
    }
It appears that a \ followed by a newline in a macro definition does not become a real newline in the macro replacement text; a behaviour inherited from the preprocessors of C and C++, but not ideal for a language where newlines and indentation can be part of the syntax.

It would be useful if indent syntax could be used in multi-line macros, but there would have to be a rule for mapping indentation levels in the macro definition to indentation levels in the replacement text inserted. For example, the indentation of the first line after the #define could be mapped to the indentation level of the line where the macro is invoked. Given the macro definition:
#define MyMacro(x) \
    if (x) \
        usr << "Foo!" \
    else \
        usr << "Bar!" \
        usr << "Baz!"
the following code:
/mob
    verb/TestMyMacro(x)
        if (isnum(x))
            MyMacro(x)
would preprocess to:
/mob
    verb/TestMyMacro(x)
        if (isnum(x))
            if (x)
                usr << "Foo!"
            else
                usr << "Bar!"
                usr << "Baz!"
For now, I suggest you just use procs. =) They're easier to write and, in some cases, easier to debug.
In response to Crispy
Crispy wrote:
For now, I suggest you just use procs. =) They're easier to write and, in some cases, easier to debug.

I prefer to use procs, but a proc can't replace a macro that defines procs itself. For example, I use the following macro to work around the hard limit on the number of objects of type /list (the token pasting operator ## is something else that can't be used in procs):
#define DECLARE_LIST(cpath1, cpath2, cname1, cname2) \
/cpath1 \
{ \
        var/cpath2/first##cname2##In##cname1 = null; \
        var/cpath2/last##cname2##In##cname1 = null; \
        proc/RemoveAll##cname2##s() \
        { \
                while (first##cname2##In##cname1) \
                { \
                        first##cname2##In##cname1.RemoveFrom##cname1(); \
                } \
        } \
        proc/DeleteAll##cname2##s() \
        { \
                while (first##cname2##In##cname1) \
                { \
                        del first##cname2##In##cname1; \
                } \
        } \
} \
/cpath2 \
{ \
        var/cpath1/owning##cname1 = null; \
        var/cpath2/prev##cname2##In##cname1 = null; \
        var/cpath2/next##cname2##In##cname1 = null; \
        proc/RemoveFrom##cname1() \
        { \
                if (owning##cname1) \
                { \
                        if (prev##cname2##In##cname1) \
                        { \
                                prev##cname2##In##cname1.next##cname2##In##cname1 = next##cname2##In##cname1; \
                        } \
                        else \
                        { \
                                owning##cname1.first##cname2##In##cname1 = next##cname2##In##cname1; \
                        } \
                        if (next##cname2##In##cname1) \
                        { \
                                next##cname2##In##cname1.prev##cname2##In##cname1 = prev##cname2##In##cname1; \
                        } \
                        else \
                        { \
                                owning##cname1.last##cname2##In##cname1 = prev##cname2##In##cname1; \
                        } \
                        owning##cname1 = null; \
                        prev##cname2##In##cname1 = null; \
                        next##cname2##In##cname1 = null; \
                } \
        } \
        proc/AddTo##cname1(cpath1/dst) \
        { \
                if (dst) \
                { \
                        owning##cname1 = dst; \
                        prev##cname2##In##cname1 = dst.last##cname2##In##cname1; \
                        next##cname2##In##cname1 = null; \
                        if (dst.last##cname2##In##cname1) \
                        { \
                                dst.last##cname2##In##cname1.next##cname2##In##cname1 = src; \
                        } \
                        else \
                        { \
                                dst.first##cname2##In##cname1 = src; \
                        } \
                } \
        } \
        proc/MoveTo##cname1(cpath1/dst) \
        { \
                RemoveFrom##cname1(); \
                AddTo##cname1(dst); \
        } \
}
In response to Crispy
For now, I suggest you just use procs. =) They're easier to write and, in some cases, easier to debug.

Some cases? Macros are never easy to debug :). But they have a definate speed advantage for small functions where the functionality takes less time than the overhead from the procedure call.
In response to Theodis
Theodis wrote:
For now, I suggest you just use procs. =) They're easier to write and, in some cases, easier to debug.

Some cases? Macros are never easy to debug :).

They are if they're bug-free! ;-D

But they have a definate speed advantage for small functions where the functionality takes less time than the overhead from the procedure call.

Sure, no argument there.
In response to Crispy
They are if they're bug-free! ;-D

If you're debuging something that works you have serious issues :).
In response to Theodis
Theodis wrote:
If you're debuging something that works you have serious issues :).

Stop revealing the fact that I have no valid argument, damn you. =P