Reputation: 663
I want to write a simple Macro function. Because this macro is used in many places by different normal c++ functions, I encountered a variable scope issue. I would like to know if there is a quick way to solve it? Thank you very much.
As you can see in the attached code, depending on whether the macro is called in the function for the first time or not, I want to either declare or reuse the variable ptrCandidate
. Note the variable scope is in the function, not in the file or translation unit. In other words, every time the macro is invoked in a new function for the 1st time, I want the top macro. And within the same function, if the macro is invoked again, I want the bottom macro.
#define EXPECT_MY_CLASS_EQ(expectedStr, candidateStr) \
auto ptrCandidate = parseAndGetPtr(candidateStr); \
doWork(ptrCandidate); \
EXPECT_EQ(expectedStr, convertToString(ptrCandidate));
#define EXPECT_MY_CLASS_EQ(expectedStr, candidateStr) \
ptrCandidate = parseAndGetPtr(candidateStr); \
doWork(ptrCandidate); \
EXPECT_EQ(expectedStr, convertToString(ptrCandidate));
void foo(){
EXPECT_MY_CLASS_EQ("123","abcd")
}
void bar(){
EXPECT_MY_CLASS_EQ("111","aabb")
EXPECT_MY_CLASS_EQ("222","ccdd")
}
void foo(){
auto ptrCandidate = parseAndGetPtr("abcd");
doWork(ptrCandidate);
EXPECT_EQ("123", convertToString(ptrCandidate));
}
void bar(){
auto ptrCandidate = parseAndGetPtr("aabb");
doWork(ptrCandidate);
EXPECT_EQ("111", convertToString(ptrCandidate));
/* auto */ ptrCandidate = parseAndGetPtr("ccdd");
doWork(ptrCandidate);
EXPECT_EQ("222", convertToString(ptrCandidate));
}
Upvotes: 0
Views: 825
Reputation: 1
A possible way might be to use __LINE__
or __COUNTER__
with preprocessor symbol concatenation.
In your case, you probably don't need any macro: prefer some static inline
function.
Here is a real-life example (using concatenation and __LINE__
) from my Bismon's project file cmacros.h line 285 (it is in C, but the same trick could be done in C++)
#define LOCAL_FAILURE_HANDLE_ATBIS_BM(Fil,Lin,Lockset,Flabel,FcodVar,ReasonVar,PlaceVar) \
struct failurehandler_stBM fh_##Lin \
= { \
.pA = {.htyp = typayl_FailureHandler_BM}, \
.failh_magic = FAILUREHANDLEMAGIC_BM, \
.failh_lockset = Lockset, \
.failh_reason = NULL, \
.failh_jmpbuf = {}}; \
curfailurehandle_BM = &fh_##Lin; \
volatile int failcod_##Lin = setjmp(fh_##Lin.failh_jmpbuf); \
FcodVar = failcod_##Lin; \
if (failcod_##Lin) { \
ReasonVar = fh_##Lin.failh_reason; \
PlaceVar = fh_##Lin.failh_place; \
goto Flabel; \
}; \
(void)0
#define LOCAL_FAILURE_HANDLE_AT_BM(Fil,Lin,Lockset,Flabel,FcodVar,ReasonVar,PlaceVar) \
LOCAL_FAILURE_HANDLE_ATBIS_BM(Fil,Lin,Lockset,Flabel,FcodVar,ReasonVar,PlaceVar)
/// code using LOCAL_FAILURE_HANDLE_BM should probably backup and
/// restore the curfailurehandle_BM
#define LOCAL_FAILURE_HANDLE_BM(Lockset,Flabel,FcodVar,ReasonVar,PlaceVar) \
LOCAL_FAILURE_HANDLE_AT_BM(__FILE__,__LINE__,Lockset,Flabel,FcodVar,ReasonVar,PlaceVar)
Back to your question, if you still want a macro: just create a block, e.g.
#define EXPECT_MY_CLASS_EQ(expectedStr, candidateStr) do{ \
auto ptrCandidate = parseAndGetPtr(candidateStr); \
doWork(ptrCandidate); \
EXPECT_EQ(expectedStr, convertToString(ptrCandidate));} while(0)
Upvotes: 0
Reputation: 16156
As shown in another answer, you don't need a macro in this case.
Generally speaking though, you can avoid re-definitions of variable names by the following means:
Use of __LINE__
preprocessor symbol (or __COUNTER__
, though IIRC that's not standard). Note that creating a variable name with the preprocessor requires two levels of indirection (replace VARIABLE
in the link with __LINE__
).
A do { /* code */ } while(0)
... which is AFAIK the most common way to write macros that are more than just a simple expression.
A lambda which is immediately executed:
([](auto var) { /* code using var */ })(initExpressionForVar())
Note that each of these approaches actually creates a new variable each time, so is semantically different from your approach with two separate macros! This is especially important if the type of the (assigned) variable has a non-default assignment operator!
If, for some reason, you rely on the reuse of a single variable and the assignment to it, then IMO the easiest approach is to define two macros. One macro which declares the variable (and initializes it, if necessary), and another macro with the code which uses the variable.
Upvotes: 5
Reputation: 217663
It seems regular function works:
void EXPECT_MY_CLASS_EQ(const char* expectedStr, const char* candidateStr)
{
auto ptrCandidate = parseAndGetPtr(candidateStr);
doWork(ptrCandidate);
EXPECT_EQ(expectedStr, convertToString(ptrCandidate));
}
Upvotes: 2