views:

324

answers:

7

I'm using a macro and I think it works fine -

#define CStrNullLastNL(str) {char* nl=strrchr(str,'\n'); if(nl){*nl=0;}}

So it works to zero out the last newline in a string, really its used to chop off the linebreak when it gets left on by fgets.

So, I'm wondering if I can "return" a value from the macro, so it can be called like

func( CStrNullLastNL( cstr ) ) ;

Or will I have to write a function

+4  A: 

Macro's don't return values. Macros tell the preprocessor to replace whatever is after the #define with whatever is after the thing after the #define. The result has to be valid C++.

What you're asking for is how to make the following valid:

func( {char* nl=strrchr(str,'\n'); if(nl){*nl=0;}} );

I can't think of a good way to turn that into something valid, other than just making it a real function call. In this case, I'm not sure why a macro would be better than an inline function. That's seems to be what you're really asking for.

jeffamaphone
A good way to turn that into something valid is to use the `({ })` GCC extension. But that, obviously, works only on GCC. Of course, I agree an inline function would be way better.
slacker
+2  A: 

Returning a value is what inline functions are for. And quite often, said inline functions are better suited to tasks than macros, which are very dangerous and have no type safetly.

Michael Dorgan
A: 
#define CStrNullLastNL(str) ({ \
    char* nl=strrchr(str,'\n');\
    if(nl){*nl=0;} \
    nl; \
})

should work.

Edit: ... in GCC.

Danvil
When I pasted your proposed macro into VS2008 it didn't compile when used from within a function call argument list.
jeffamaphone
@jeffamaphone:Sure it didn't. The `({ })` construct is a GCC extension.
slacker
A damn useful extension, I might add.
slacker
But not portable.
jeffamaphone
@jeffamaphone: Sure it's not portable. After all, that's the definition of "extension". :)
slacker
The ({ }) construct does what exactly? evaluate to the last statement?
bobobobo
+11  A: 

For a macro to "return a value", the macro itself has to be an expression. Your macro is a statement block, which cannot evaluate to an expression.

You really ought to write an inline function. It will be just as fast and far more maintainable.

Mike DeSimone
Ah, a good, succinct way of stating it.
jeffamaphone
With compilers nowadays, isn't the inline modifier implicit?
geofftnz
Mike DeSimone
it's also necessary if you're writing the body of the function in a header file (which may be included by more than one implementation file) - otherwise you get linker errors
Phil Nash
... The linker errors being caused by the generation of non-inlined versions of the function. And you have to put the body of the function in the header in order for the compiler to generate inline code in every compile unit that pulls in the header, and the distinction between .h*/.c* files is usually lost by the time the code makes it to the compiler. Thanks for the clarification.
Mike DeSimone
... A downvote, sans explanation, a week after I last said anything. Does this happen a lot to other folks? O_o
Mike DeSimone
+1  A: 

I gave +1 to Mike because he's 100% right, but if you want to implement this as a macro,

char *CStrNullLastNL_nl; // "private" global variable
#define nl ::CStrNullLastNL_nl // "locally" redeclare it
#define CStrNullLastNL( str ) ( \
    ( nl = strrchr( str, '\n') ), /* find newline if any */ \
    nl && ( *nl = 0 ), /* if found, null out */ \
    (char*) nl /* cast to rvalue and "return" */ \
OR  nl? str : NULL /* return input or NULL or whatever you like */
)
#undef nl // done with local usage
Potatoswatter
+1, but macro should return str
oraz
Some questions: 1) What is `OR`? Is it like `||`? 2) I guess this is not reentrant? 3) Do you want to declare `CStrNullLastNL_nl` `static` so it doesn't have external linkage? BTW, this brings back memories of reading `cfront` output.
Mike DeSimone
@Mike: 1. I put "OR" between two alternative lines of code because Oraz suggested different behavior. Note missing comma. 2. Not reentrant. Macros are a bad idea to begin with. 3. I suppose. Put two threads in different source files and make it reentrant, too! All a stupid exercise.
Potatoswatter
Thanks. Make sense now. ^_^
Mike DeSimone
+1  A: 

If you really want to do this, get a compiler that supports C++0x style lambdas:

#define CStrNullLastNL(str) [](char *blah) {char* nl=strrchr(blah,'\n'); if(nl){*nl=0;} return blah;}(str)

Although since CStrNullLastNL is basically a function you should probably rewrite it as a function.

MSN
A: 

Can you use the comma operator? Simplified example:

#define SomeMacro(A) ( DoWork(A), Permute(A) )

Here B=SomeMacro(A) "returns" the result of Permute(A) and assigns it to "B".

Adisak
Sure you can, but you can't put control-flow statements in an expression. That's where it gets tricky.
Mike DeSimone
The ?: operator works fine as control flow in an expression. I use it all the time.
Adisak