17

In C++, I need to defined a macro. That macro would take as parameter a "block" of code.

Can we safely use several lines of code as parameter of a macro function?

I was asking myself if :

  1. is the following code valid, defined as valid by the standard, as in "cross-platform"?
  2. is there a better way to do the same (I can't use template function there because I need the context).

#define MY_MACRO( expr ) DOSOMETHING( (expr) ); DOANOTHERTHING( (expr) ); // etc...

int my_function() {
    int o = RandomNumber();
    MY_MACRO( 
        int k = AFunction();
        k++;
        AnotherFunction( k + o ); // here I need to keep the context of the call
    ); 
}

We can't use functors because we need to have access to the context of the call. We can't use lambda (snif) because we use an old compiler that don't provide it (and we can't change it).

Klaim
  • 67,274
  • 36
  • 133
  • 188
  • what is the use of this macro? – CharlesB Apr 06 '11 at 12:39
  • Did you try it ? What happened ? My guess is that each expression "parameter" that you pass will need to be enclosed in `()`. – Paul R Apr 06 '11 at 12:40
  • Thanks for the edit Paul, couldn't find how to get the display right. @CharlesB : It generate code that insert the expressions parameters in several part of the generated code. The problem is more related to: can we safely use several lines of code as parameter of a macro function? – Klaim Apr 06 '11 at 12:42
  • @Paul R: We are trying right now, but the question is more if it's defined by the standard (using several lines for a macro) and if it's cross-platforum. – Klaim Apr 06 '11 at 12:42
  • @Klaim, perhaps it may help with answers if you explain what do you mean by *context*. Do you for example need all the state in the caller within `DOSOMETHING` and `DOANOTHERTHING`? If not, can you not encapsulate what you need within a functor which you can pass to these? – Nim Apr 06 '11 at 12:53
  • @Nim: As I added, we just can't use functors or lambda. See the updated code. The DOSOMETHING and DOANOTHERTHING macro here are not revelant to the question because it's the fact that we reuse more than once the `expr` that is important. As far as I can use several lines of code, I'm fine with the macro. – Klaim Apr 06 '11 at 12:57

6 Answers6

12

16.3/9:

Within the sequence of preprocessing tokens making up an invocation of a function-like macro, new-line is considered a normal white-space character.

So the multi-line macro invocation in general is fine. Of course if DOSOMETHING and DOANOTHERTHING don't introduce braces for scope, then your particular example will redefine k.

Edit:

We can't use functors because we need to have access to the context of the call. We can't use lambda (snif) because we use an old compiler

The usual way is to capture whichever variables you need in the functor, just like a lambda does. The only thing a lambda can do that a functor can't is "capture everything" without having to type it out, but whoever writes the lambda can see what variables they use, so that's just convenience, they could type them all out if they had to. In your example:

struct MyFunctor {
    int o;
    MyFunctor(int o) : o(o) {}
    void operator()() const {  // probably not void in practice
        int k = AFunction();
        k++;
        AnotherFunction( k + o );
    }
};

template<typename F>
void DoThings(const F &f) {
    DOSOMETHING(f());
    DOANOTHERTHING(f());
}

int my_function() {
    int o = RandomNumber();
    DoBothThings(MyFunctor(o));
}

You can also capture variables by reference (usually using a pointer as the data member rather than a reference, so that the functor can be copy-assigned).

If by "context", you mean for example that the macro argument and/or the macro body might contain a break or goto, and hence needs to be inside the lexical scope of the caller then you can't use a functor or a lambda. For shame ;-)

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • The example might be misleading : the macros I need can be used in a lot of functions and the "context" would be the objects availabl in the function's scope. Replace "context" by "scope" to understans what I meant. So here, the functor isn't useful as I would have to move all my functions and even different parts of the same function in functors. In practice, that would be hell. That's why I wish we could use lambda. But we can't :( – Klaim Apr 06 '11 at 16:34
  • @Klaim: hmm, if you have to access so many variables that it's hell to list them, then I would be looking to refactor anyway, maybe express the logical connections between those objects differently than just, "they're all in scope at this point". Simplest way, make them all members of some stupid "state" object and pass that. Then try to work out how to make the "state" less stupid and more meaningful. But I accept you may be in a hole that's too deep to climb out of, or at least too hard to climb out of *today*. – Steve Jessop Apr 06 '11 at 16:43
  • What I meant is that I would have to do this for around 40 functions, not that there are a lot of variables in the functions. – Klaim Apr 06 '11 at 17:30
3

the way to make it work (at least for gcc version 4.8.1 (Ubuntu/Linaro 4.8.1-10ubuntu9)), is to use braces { } enclosing your actuals for the macro.

A useful example:

#ifdef DEBUG
#define MYDEBUG(X) (X)
#else
#define MYDEBUG(X)
#endif

MYDEBUG({
  if (shit_happens) {
     cerr << "help!" << endl;
     ....
  }
});
1

A macro with multiple lines for parameter is fine also in case of multiple arguments, it even allows "commas" inside, but I strongly descourage using "commas" beause if it is not ambiguos to a machine it is certainly ambiguos to humans:

#include <iostream>
using namespace std;

#define MACRO2FUN( X, Y) x; y;
void function(int a, int b, int c){
    std::cout<<a<<" "<<b<<" "<<c<<std::endl;
}

int main() {
    MACRO2FUN(
      function(3,4,5), 
      function(6,7,8)
      )
      return 0;
}
CoffeDeveloper
  • 7,961
  • 3
  • 35
  • 69
1

I think you will need to use extra parentheses to make your expressions look like a single argument which won't get broken up by the preprocessor, i.e. do it more like this:

#define MY_MACRO( (expr) ) DOSOMETHING( (expr) ); DOANOTHERTHING( (expr) ); // etc...

int my_function() {
    MY_MACRO( 
        (int k = AFunction();
        k++;
        AnotherFunction( k );)
    ); 
}

although I haven't actually tried it.

Paul R
  • 208,748
  • 37
  • 389
  • 560
  • I haven't tried it either, but I think comma is the only thing that will break up macro arguments. Strange as it looks, you can put semi-colons in there without doing anything special, it's still a single macro argument that happens to contain semi-colons. – Steve Jessop Apr 06 '11 at 12:53
  • @Steve: ah, OK - that makes sense - I have had to add extra parentheses for printf-like macros in the past, but of course these would have contained commas. – Paul R Apr 06 '11 at 13:01
  • 2
    This answer don't work : the ; finish the whole expression. To make it work, you need to put all the expresssions between () too. (we checked on GCC4.4 ) – Klaim Apr 06 '11 at 16:31
  • Yes commas between expression that would be between (). – Klaim Apr 06 '11 at 16:35
1

In C++ you should use a functor! ;)

struct ninja
{
  void operator()() const
  {
    int k = AFunction();
    k++;
    AnotherFunction( k );    
  }
};

template <typename Functor>
void do_something(Functor const& f)
{
  f();
}

template <typename Functor>
void do_otherthing(Functor const& f)
{
  f();
}

int my_function()
{
  ninja foo;
  do_something(foo);
  do_otherthing(foo);
}
xaxxon
  • 19,189
  • 5
  • 50
  • 80
Nim
  • 33,299
  • 2
  • 62
  • 101
  • Or in C++0x, without a name: `[](){ int k = AFunction(); ++k, AnotherFunction( k ); }` – MSalters Apr 06 '11 at 12:46
  • Yes we thought about that, but the problem is that we need to keep the context of the call. However lambda would have solve this but we currently can't use a compiler providing the lambda feature TT__TT I'm updating the answer, thanks anyway that confirm what I was thinking. – Klaim Apr 06 '11 at 12:48
  • Don't know much about this thing called C++0x... :( – Nim Apr 06 '11 at 12:49
  • @Klaim, what do you mean by *context*? – Nim Apr 06 '11 at 12:49
  • @Nim: Look at the example, the code need to have access to variables in the calling function. C++0x is the nickname of the new standard of C++ (C++2011) that define the lambda feature. – Klaim Apr 06 '11 at 12:51
  • @Klaim, I *know* that! :) I was only kidding, I tend to stick to answers in the current standard until the standard is finalised and ready for prime time... Again, re my comment, can you not encapsulate what you need within the functor (even if it's just references) – Nim Apr 06 '11 at 12:55
  • Functor would require to add references the variables of the caller function. If that's not automatic (like with lambda), we just can't use it. – Klaim Apr 06 '11 at 12:58
0

The main problem that I see is that expr isn't an expression at all. It even contains a declaration. Obviously, you're going to have a problem with two variables k defined in my_function.

If you can use C++0x (e.g. VS2010, GCC4.6) then you can use lambda's to capture context. Not that you'd need to capture context for such a simple case, nor do you need templates, your macro just needs a std::function<void(void)>.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Too bad, we're stuck with GCC4.4 for the moment :( – Klaim Apr 06 '11 at 12:55
  • @Klaim it's tacky to complain about answers because of constraints that weren't present in the question. This comment should also be removed. – xaxxon Mar 05 '18 at 04:46