Yet more dangers of macros in C++

March 27, 2009 at 1:34 am (PT) in Programming

I saw some code:

std::string s = foo();
bar(s.c_str());

I tried changing it to:

bar(foo().c_str());

and things broke. It turns out that bar() was a macro that expanded to:

#define bar(s) do { struct st; st.someString = (s); baz(&st); } while (0)

(If you’re wondering about the do ... while (0), consult the comp.lang.c FAQ.)

This is fine for C code, but in the C++ world, this is dangerous. In this case, foo() returns an anonymous std::string object by value. That anonymous object then is destroyed after its internals are assigned to st.someString but before baz() gets to use it, causing baz() to be called with garbage.

Moral #1: Macros that don’t have perfect function-like semantics shouldn’t look like functions. For example, macros should be clearly indicated by naming them in all uppercase.

Moral #2: Use inline functions when possible. (In this case, however, the macro was provided by a C library.)

Tags:

Newer: Why isn’t buying Wii points easier?
Older: The Heisenberg uncertainty principle applied to dating

3 Comments »

  1. wow. haven’t thought about such programming minutae in a long time. esp since the little stuff I do program thee days in perl and java, especially being more sandboxed (had to check if that meant what i thought it meant…been a long time, close enough) mean this stuff isn’t as important. weird to think about c++ again.

    — ben @ April 18, 2009, 8:39 pm (PT)

  2. I think that code would be broken even if bar() were a function. Using foo().c_str() is always a bad idea although some compilers will let you get away with it. I had a similar bug once but it didn’t cause problems until I upgraded to Visual Studio 2008.

    — Billy @ May 22, 2009, 6:17 pm (PT)

  3. No, the code would be legal if bar() were a function, provided that bar() doesn’t try to save its own copy of the address. It’s required that the following operations happen in the following order:

    1. foo() is invoked, which returns an anonymous object.
    2. c_str() is invoked on the anonymous object and returns a pointer to its internals.
    3. bar() is invoked on that pointer.
    4. bar() completes and returns execution to the calling stack frame.
    5. The anonymous object from step 1 is destroyed.

    — James @ May 22, 2009, 8:02 pm (PT)

RSS feed for comments on this post.

Leave a comment

(will never be displayed)


Allowed HTML tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>