1

In my code, I have:

#define EV( event ) SendEvent( new event );
EV( evFormat );

But I want to pass a created object in the EV macro, like:

CEvent *ev = new CEvent();
EV( ev );

Is this possible? Because there is no way I will modify the EV macro.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
domlao
  • 15,663
  • 34
  • 95
  • 134

5 Answers5

5
#define EV( event ) SendEvent( new event );    // Can't be changed.

The macro enforces that each call to SendEvent should create a new dynamic object. Your problem is not just that using a macro for that is stupid and e.g. reduces the readability of your source code. It is also that the macro does not allow you to create the object earlier than the call, and that you can't change the macro; in your words "there is no way I will modify the EV macro".


The solution is therefore simple:

Don't use the macro, use SendEvent directly, and remember to not delete.

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • Each call to SendEvent will generate a new dynamic object, yes. But must it be a new dynamic object of the same type as what is ultimately passed to SendEvent...? *Not necessarily.* The textual nature of preprocessor macros and more complex expressions adds some tricks in to the mix, as per my answer. – HostileFork says dont trust SE Sep 23 '11 at 01:09
  • @HostileFork: true, one can exploit text substitution here to make the macro do not-intended things. Your answer makes it generate `SendEvent( new dummy && dummy::dofree() ? ev : NULL )` where, since `dofree` always returns `true`, the actual arg passed to `SendEvent` is always `ev`. Which is probably unintended. Instead, if you want to be able to cancel the `new` in the macro, I would just use a macro argument that makes it perform a placement new. – Cheers and hth. - Alf Sep 23 '11 at 01:55
  • @AlfPSteinbach Placement new can be closer to a no-op w/o leaking, hadn't thought of it that way. The "always ev" is intentional, as that is the intent of the function, I just had to put something in that slot. Passing NULL in unexpected scenarios is better in my mind because it "breaks harder". Comma operator unusable here, other ideas? – HostileFork says dont trust SE Sep 23 '11 at 02:36
4

The way EV is currently written, it will generate a new object on each call. But that creation doesn't have to be of an object of the same type as what is ultimately passed to SendEvent. That's because the textual nature of preprocessor macros and more complex expressions adds some tricks. Consider this:

class dummy {
private:
    static dummy* freeme;
public:
    dummy() { freeme = this; }
    static bool dofree() { delete freeme; return true; }
};
dummy* dummy::freeme;

CEvent *ev = new CEvent(this);
EV( dummy && dummy::dofree() ? ev : NULL );

That will expand out so that the new you're running is not of a CEvent, but of a dummy class...which you then free, and then the whole expression evaluates to your event:

SendEvent( new dummy && dummy::dofree() ? ev : NULL );

(Note: Using ?: is not as nice as the comma operator, so there's a wasted NULL branch here that never actually happens. The comma operator would be nice, but preprocessor macros treat commas specially and this is one of the cases where it can't be used. Making it thread-safe is left as an exercise for the reader.)

To make it "cleaner" but still express it in terms of EV...without explicitly mentioning SendEvent at all, you could make your own macro:

#define EV2(event) EV( dummy && dummy::dofree() ? event : NULL )

...or you could just use SendEvent since it seems to do exactly what you want. But where's the fun in that? ;-P


UPDATE:

As @AlfPSteinbach points out, a more esoteric but lightweight way of turning your new into a no-op is with placement new:

int dummy;

CEvent *ev = new CEvent(this);
EV( (&dummy) int ? ev : NULL );

So now you're expanding into:

SendEvent( new (&dummy) int ? ev : NULL );

You're executing a new, but this time without needing to worry about freeing the result! Because I wasn't entirely sure if this was kosher esp. in threading cases, I made its own question:

Is it well-defined/legal to placement-new multiple times at the same address?

Community
  • 1
  • 1
  • One lesson here is just how maddening preprocessor macros can be, and how "outside the language" pasting text together is. It's because `event` as passed to `EV` is just a bunch of letters that this kind of trick is possible. They should be used sparingly because they are so chaotic. But if you really were up against a wall and had a *good reason* why you *had to* express `EV2` purely in terms of `EV`, this sort of nonsense can do it until you can get your hands untied to do it right. (I'm skeptical very many people in the world have a "good enough" reason to do this kind of thing.) – HostileFork says dont trust SE Sep 23 '11 at 00:53
2

Totally doable without altering the macro. It's just risky. Note that you'll have to delete []

#include <memory>

struct CEvent {};
void SendEvent(CEvent*) {}

#define EV( event ) SendEvent( new event );

int main() {
    char *cev = new char[sizeof(CEvent)];
    CEvent* ev = (CEvent*)cev;

    EV( (ev)CEvent );

    ev->~CEvent();
    delete [] cev;
}

http://ideone.com/

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • This doesn't pass an existing object, the way the question asks. But it does leave the user with a pointer to the object. Of course, there must be a `delete` somewhere matching the `new` inside the macro, now you have deletion using the wrong type, as well as double-deletion, which is not going to be pretty. – Ben Voigt Sep 23 '11 at 00:42
  • You are correct that it does not pass a _created_ object, and that if the macro/function does the delete, it will be completely wrong. – Mooing Duck Sep 23 '11 at 01:16
1

If there is no way you will modify the EV macro, then it isn't possible. Can't you create your own macro that does SendEvent( event ); instead?

Update: Would it be a possibility to #undef the original macro, and provide your own definition of it? Assuming you only want the new behavior, you could do

#undef EV
#define EV( event ) SendEvent( event );

but now you will need to replace the original kind of call with EV( new event ).

K-ballo
  • 80,396
  • 20
  • 159
  • 169
1

You might be leaking memory, unless SendEvent manages it from inside, something like

void SendEvent(const Event *ev) {
     doSendEvent(ev);
     delete ev;
}

That aside, have you tried this, it should work

EV(CEvent)

If not, you could redefine operator new for the class CEvent so the above call would work.

But your question is really unusual. Are you sure you there you are going down the right path ?

ixe013
  • 9,559
  • 3
  • 46
  • 77