4

I have a macro used around a code statement, to introduce nested exception handling:

#define TRAP_EXCEPTION(statement) \
    try \
    { \
        try{statement} \
        catch(Engine::Exception& e) \
        { \
            throw MyException(e.message()); \
        } \
    }

This has been working well until one case raised compiler errors. I managed to construct a minimal example:

TRAP_EXCEPTION
(
 std::map<MyType, bool> Map;
)
catch(MyException& e)
{
}

This gives the following errors... how do I fix it (ideally in the macro)?

> error C2143: syntax error : missing '>' before '}'
> error C2976: 'std::map' : too few template arguments
> error C2143: syntax error : missing ';' before '}'
Mr. Boy
  • 60,845
  • 93
  • 320
  • 589
  • 1
    Look at the preprocessed code to see what the macro expands to (there's an option in VS to generate it). – molbdnilo Apr 25 '14 at 12:20
  • 1
    Don't use a macro like that. There are [far better ways to implement exception boundaries](http://blogs.msdn.com/b/vcblog/archive/2014/01/16/exception-boundaries.aspx). – James McNellis Apr 25 '14 at 16:00
  • I'm not sure which part of that is applicable here? Can you write a full answer? – Mr. Boy Apr 25 '14 at 16:31
  • The macro in your question is used to wrap arbitrary blocks of code in a try block. That article discusses several alternatives to your macro that are much, much cleaner (more maintainable, actually possible to debug through correctly, etc.). – James McNellis Apr 25 '14 at 18:52

3 Answers3

5

Macros don't understand the template parameters (the angle brackets to be precise), they just see the , and think you provided two different parameters to the macro. You need to add round brackets:

TRAP_EXCEPTION
(
    (std::map<MyType, bool> Map;)
)

and the macro needs to be changed:

#define UNWRAP(...) __VA_ARGS__

#define TRAP_EXCEPTION(statement) \
    try \
    { \
        try{UNWRAP statement} \
        catch(Engine::Exception& e) \
        { \
            throw MyException(e.message()); \
        } \
    }

Note that this would require you to always provide an additional pair of round brackets on the call side.

In your case (since the macro is supposed to take only one statement), you could also use a variadic macro:

#define TRAP_EXCEPTION(...) \
    try \
    { \
        try{ __VA_ARGS__ } \
        catch(Engine::Exception& e) \
        { \
            throw MyException(e.message()); \
        } \
    }

which is probably better since the call side doesn't change and

TRAP_EXCEPTION
(
    std::map<MyType, bool> Map;
)

will now work correctly.

Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
3

The preprocessor doesn't recognise < and > as brackets, so interprets the comma as a macro argument separator. You'd have the same problem if the statement contained an unbracketed comma for any other reason (e.g. a comma operator, or commas to separate declarators).

If you really want to abuse the preprocessor like this, you could bodge it to accept any number of macro arguments:

#define TRAP_EXCEPTION(...) \
try \
{ \
    try{__VA_ARGS__} \
    catch(Engine::Exception& e) \
    { \
        throw MyException(e.message()); \
    } \
}

but I would advise you not to try to do anything clever with macros.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • It's not really abuse... this is very similar to something GoogleTest does https://code.google.com/p/googletest/wiki/AdvancedGuide#Exception_Assertions I haven't established how their code handles this yet... – Mr. Boy Apr 25 '14 at 12:36
  • Hmm, GTest also fails to compile when `map` is used :( – Mr. Boy Apr 25 '14 at 12:56
3

Instead of the __VA_ARGS__ trick, you can also use a typedef before the macro. The __VA_ARGS__ trick has some drawbacks.

typedef std::map<MyType, bool> MyMap;

TRAP_EXCEPTION(
  MyMap Map;
)
Billy Donahue
  • 564
  • 2
  • 10