2

I'm looking for a way to convert the following function structure to a macro. I know, it's a silly and pointless example, but it illustrates the point since I cannot give out my actual source code.

int foo(int x, int y)
{
  do
  {
    --x;
    ++y;
  }while(x > y);

  return x * y; //note that x and y have changed values here.
}

So that I can call the function in main or some other function like so:

int next_x = foo(x,y);

I cannot seem to get the syntax 100% correct here. This is my poor attempt:

#define FOO(x,y)      \
(                     \
  do                  \
  {                   \
    --x;              \
    ++y;              \
  }while(x < y),      \
  x                   \
)                     

The reasoning for the x at the end is so that I could, in theory, be able to do this

int next_x = FOO(x,y);

but instead, I get a syntax error and I'm not sure why. Any help would be appreciated.

===============================================

Additional Info

I should also note that I have other macros which are structured accordingly:

#define INIT(x,y)   
(   
  x = //something,
  y = //something
)

#define NEXT_INT(x,y)    \
(                        \
  INIT(x,y),             \
  get_next_num(x,y)      \            //Note, this is an inline function call , not a macro.
 )

#define NEXT_FLOAT(x,y,temp)         \
(                                    \
  temp = NEXT_INT(x,y),              \
  temp ? temp * 1.23456 : FLT_MIN    \
)                                    

And so, I can and have done the following:

float my_flt = NEXT_FLOAT(x,y,temp);
a3f
  • 8,517
  • 1
  • 41
  • 46
audiFanatic
  • 2,296
  • 8
  • 40
  • 56
  • My dim memory of macros tells me that they need to be all on one line. – Ross Presser Jul 25 '13 at 19:07
  • There's no really great answer here, and that's probably good. Otherwise people would think this was a great idea and do it more often. Does your C compiler not support 'inline' (I think it's in the C99 standard)? The only reason I can see for doing this is creating a macro version of C++'s templates. Please don't. Some poor sustaining engineer will take out a contract on your life. – Speed8ump Jul 25 '13 at 19:23
  • What is the objective to avoid an inline function? – harper Jul 25 '13 at 19:39
  • Yea, it does support inlining. But the problem is that in my actual function, I need to save the new values of `x` and `y` for subsequent calls to this function `foo`.. The obvious way to do this would be to pass pointers to `x` and `y` and then just make a regular, inlined function. However I do not want to do that because I'm making a time critical application and I need `x`' and `y` to remain in registers. Since registers don't have addresses, it means that they'll have to be stored in memory not local to the CPU and then retrieved, thus eating up cycles. – audiFanatic Jul 25 '13 at 19:41
  • 1
    @audiFanatic: You are assuming that an inlined function will perform poorly, but I it is not clear if you have actually seen an issue on your system. Unless you have a requirement that you are not allowed to enable optimizations on the compiler (or the compiler produces very poor quality optimizations), there is no need to prefer a macro to an inline function for the reason you state. – jxh Jul 25 '13 at 20:13
  • 1
    @RossPresser Nope, you can use \ to tell computer "the next line is still part of marco" – Lidong Guo Jul 25 '13 at 21:47

3 Answers3

3

The C syntax only allows expressions to be separated by the comma operator (,). do ... while() is not an expression, but a statement, so it is an error to use it as a value to a comma operator.

Generally speaking, an inline function should be preferred over a macro to perform some inline computation. They are easier to implement, less error prone, and easier to maintain. There are very few situations where an inline function would fail where a macro would succeed.

There really isn't a safe macro to achieve your objective, but a workaround would be to pass the variable you want updated in as a macro parameter.

#define FOO(x, y, result) \
    do { \
        do { \
            --x; \
            ++y; \
        } while(x > y); \
        result = x * y; \
    } while(0)

If you are using GCC, you can use their statement-expression syntax, which is an extension to C, and not a standard C feature. The syntax is like:

({ statement; statement; expression; })

And the result of the above would be the last expression.


In a comment, you express:

I need to save the new values of x and y for subsequent calls to this function foo.. The obvious way to do this would be to pass pointers to x and y and then just make a regular, inlined function. However I do not want to do that because I'm making a time critical application and I need x and y to remain in registers.

You are assuming that x and y would not be left in registers, which is bad assumption to make. It depends on the quality of your compiler's ability to optimize code. Consider the following:

static inline int foo (int *x, int *y) {
    do {
        --*x;
        ++*y;
    } while (*x > *y);
    return *x**y;
}

int main (int argc, char *argv[]) {
    int x = argc+1;
    int y = argc;
    foo(&x, &y);
    return 0;
}

When compiled with gcc -O1, the result is:

main:
.LFB14:
        movl    %edi, %edx
.L2:
        movl    %edi, %eax
        addl    $1, %edx
        subl    $1, %edi
        cmpl    %edx, %eax
        jg      .L2
        movl    $0, %eax
        ret
.LFE14:

You will observe there is no pointer value dereference, which is exactly what you wanted to have happen.

jxh
  • 69,070
  • 8
  • 110
  • 193
  • hmm... that's interesting. I guess I assumed that it would since I was told that, if I had a function which took an array pointer as a parameter, I should first copy the data I needed into registers and then save the modified data back at the end of the function. But looking at my .asm file, it seems like you're right, though it's much harder to tell since it's intertwined with other code. – audiFanatic Jul 25 '13 at 20:42
  • @audiFanatic: It depends on whether the function gets inlined or not. If the function is not an inline function, it may benefit from copying values out from pointers and then copying it back later. But for an inline function, most compilers will do the right thing. – jxh Jul 25 '13 at 20:46
2

You cannot use do/while loops as an expression, so you'll need to use a block within parentheses, like this:

EDIT: this is a GCC extension, not standard C

#define FOO(x,y)    \
({                  \
  do                \
  {                 \
    --x;            \
    ++y;            \
  }while(x > y);    \
  x;                \
})
Drew McGowen
  • 11,471
  • 1
  • 31
  • 57
  • Other mistakes in my answer was poor indentation, and + for GCC I was unaware of this. Nice answer Drew Mc. Thanks! – Grijesh Chauhan Jul 25 '13 at 19:22
  • else we can leave a space after `\\`. most frequent mistake I think – Grijesh Chauhan Jul 25 '13 at 19:24
  • This may be an issue then, since I'm not using GCC, I'm using a specialized compiler for my application. I'm curious though, why place the bracket after the `x;` and not around the loop only? – audiFanatic Jul 25 '13 at 19:33
  • That's just how the syntax works. More than likely it's to show that it's a block of code to be executed sequentially, and the parentheses tells the compiler to use the last statement as the result of the expression. – Drew McGowen Jul 25 '13 at 19:34
0

One issue here is that multiline macros require a \ at the end of each line.

jh314
  • 27,144
  • 16
  • 62
  • 82
  • Sorry for the confusion, I did this up in notepad++ real quick and I forgot to add them originally, my fault. But no, that's not the issue unfortunately. – audiFanatic Jul 25 '13 at 19:12