soodsandeep
asked on
C++ Inline fucntion vs. macro
hello.
Please, what is difference between an inline function and a macro in C++ since both are expanded in-line.
waiting..
thanks.
Please, what is difference between an inline function and a macro in C++ since both are expanded in-line.
waiting..
thanks.
There are other differences too. For example, macro's do not obey scope properly, so if you declare a MAX() in a namespace or in a class, the macro will replace invokations of this MAX. This either results in a compiler error incorrect behavrior.
There is nearly no justifiable reason to use macros in C++. Any use of macros is risky, as PMazur's example and mine both illistrate. C++ provides alternatives, like inline functions and templates. These can replace nearly every use of macros, but will be safer and more powerful.
Do yourself a favor, forget macros exist.
There is nearly no justifiable reason to use macros in C++. Any use of macros is risky, as PMazur's example and mine both illistrate. C++ provides alternatives, like inline functions and templates. These can replace nearly every use of macros, but will be safer and more powerful.
Do yourself a favor, forget macros exist.
There is another, possibly more significant difference. The (pre) compiler will replace every occurrence of the macri name with the defined tokenstring. IOW suppose you defined:
#define max(a,b) ((a)>(b)?(a):(b))
every occurence of max() will be replaced with (or attempted to be replaced with) the macro definition.
#define max(a,b) ((a)>(b)?(a):(b))
every occurence of max() will be replaced with (or attempted to be replaced with) the macro definition.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Unlike a function, macros don't have to be compileable units.
For example:
#define START_LOOP (x,y) while ((x) < (y)) {
#define END_LOOP }
Then I can do
int a=5, b=10;
START_LOOP(a,b)
// do fun stuff here
myfunc(a);
END_LOOP
This is a trivial example, but START_LOOP could be a very complicated expression.
A more sophisticated example:
#if !defined(_DEBUG) && !defined(DEBUG)
#define HANDLE_ERROR_BEGIN int checkpoint = 0; try {
#define HANDLE_ERROR_END(x) } catch (Error* e) { \
e->Push(checkpoint, x); \
throw(e); } \
catch (...) { Error* e = new Error; \
e->Push(checkpoint, x); \
throw(e); }
#else
#define HANDLE_ERROR_BEGIN int checkpoint = 0;
#define HANDLE_ERROR_END(x)
#endif
You would use this like this:
int myfunc()
{
HANDLE_ERROR_BEGIN
int a;
myotherfunc(a);
// etc
HANDLE_ERROR_END("int myfunc")
}
This allows me to trap errors in release mode, but it debug it doesn't trap them so I can debug it.
Final example -if you do any MFC programming, take a look at the definitions of BEGIN_MESSAGE_MAP/END_MESS AGE_MAP.
For example:
#define START_LOOP (x,y) while ((x) < (y)) {
#define END_LOOP }
Then I can do
int a=5, b=10;
START_LOOP(a,b)
// do fun stuff here
myfunc(a);
END_LOOP
This is a trivial example, but START_LOOP could be a very complicated expression.
A more sophisticated example:
#if !defined(_DEBUG) && !defined(DEBUG)
#define HANDLE_ERROR_BEGIN int checkpoint = 0; try {
#define HANDLE_ERROR_END(x) } catch (Error* e) { \
e->Push(checkpoint, x); \
throw(e); } \
catch (...) { Error* e = new Error; \
e->Push(checkpoint, x); \
throw(e); }
#else
#define HANDLE_ERROR_BEGIN int checkpoint = 0;
#define HANDLE_ERROR_END(x)
#endif
You would use this like this:
int myfunc()
{
HANDLE_ERROR_BEGIN
int a;
myotherfunc(a);
// etc
HANDLE_ERROR_END("int myfunc")
}
This allows me to trap errors in release mode, but it debug it doesn't trap them so I can debug it.
Final example -if you do any MFC programming, take a look at the definitions of BEGIN_MESSAGE_MAP/END_MESS
One big difference: If you have a bunch of code in a macro and you are debugging your program and you come into a function like:
CS_RETCODE inv_rpc(VOIDPTR UNUSED(ptr), DBPROCESS *dbproc,
char *rpc_name, DB_RPC_PARAM *rpc_params, ...)
{
LOCAL_VARIABLE_DECLARATION S
SET_RETRY_CONFIG_VARIABLES
for (counter = 0; counter < rpc_retry_count; counter++) {
SET_UP_QUERY
while (dbresults(dbproc) != NO_MORE_RESULTS)
dbcanquery(dbproc);
PROCESS_RETURN_INFO
}
CHECK_RETURN_STATUS
return CS_SUCCEED;
}
The debugger can't see inside the macros because it isn't actual code in the executable so it jumps over it and then how do you know that the code is doing the right thing?
Not being able to debug inside macros is the very worst thing to have happen when you have a service level agreement guaranteeing less than 14 minutes of downtime a month (honestly, that's what my SLA says!)
That code above is right out of actual production code that I have to support. Here is what one of those macros actually looks like. I just have one question - would you not want the ability to debug into this code?
#define PROCESS_RETURN_INFO \
if ((ret_param_num = dbnumrets(dbproc)) > 0) { \
va_start(param_list, rpc_params); \
for (param_ptr = rpc_params; \
param_ptr->paramname != END_PARAM_TABLE; param_ptr++) { \
param_val = va_arg(param_list, CS_BYTE *); \
if (param_ptr->status == (CS_BYTE) DBRPCRETURN) { \
for (i=1; i <= ret_param_num; i++) { \
if (strcmp(dbretname(dbproc, (int) i), \
param_ptr->paramname) == 0) { \
return_val = dbretdata(dbproc, (int) i); \
len = dbretlen(dbproc, (int) i); \
for (j = 0; j < len; j++) \
param_val[j] = return_val[j]; \
if (param_ptr->maxlen != -1) \
param_val[j] = '\0'; \
if (param_ptr->type == SYBCHAR) { \
while (param_val[--j] == ' ' && j >= 0) \
param_val[j] = '\0'; \
} \
break; \
} \
} \
} \
} \
va_end(param_list); \
} \
\
if (dbhasretstat(dbproc)) { \
ret_stat = dbretstatus(dbproc); \
switch (ret_stat) { \
case PROC_SUCCESS_STATUS: \
return CS_SUCCEED; \
\
case PROC_DEADLOCK_VICTIM: \
ASC_event("SYS_WARN", __LINE__, __FILE__, \
"Warning! RPC DeadLock Victim, Ret Stat %d, Retry %d", \
ret_stat, counter); \
ASC_sleep(rpc_retry_sleep) ; \
continue; \
\
case PROC_RESOURCE_ERR: \
ASC_event("RPCSPACE", __LINE__, __FILE__, \
"Critical Resource Error; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep) ; \
continue; \
\
case PROC_MISSING_OBJECT: \
ASC_event("RPC_ERR", __LINE__, __FILE__, \
"Error: Missing RPC Object; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep) ; \
continue; \
\
case PROC_DATATYPE_ERR: \
ASC_event("RPC_ERR", __LINE__, __FILE__, \
"RPC Datatype Error; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep) ; \
continue; \
\
case PROC_PERMISSION_ERR: \
ASC_event("RPC_ERR", __LINE__, __FILE__, \
"RPC Permission Error; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep) ; \
continue; \
\
case PROC_SYNTAX_ERR: \
ASC_event("RPC_ERR", __LINE__, __FILE__, \
"RPC Syntax Error; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep) ; \
continue; \
\
case PROC_MISC_ERR: \
ASC_event("RPC_ERR", __LINE__, __FILE__, \
"Misc User RPC Error; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep) ; \
continue; \
\
case PROC_NON_FATAL_INT_ERR: \
ASC_event("RPC_ERR", __LINE__, __FILE__, \
"Non Fatal Internal Error; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep) ; \
continue; \
\
case PROC_SYS_LIMIT_ERR: \
ASC_event("RPCSPACE", __LINE__, __FILE__, \
"System Limit Reached; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep) ; \
continue; \
\
case PROC_FATAL_INCON1: \
case PROC_FATAL_INCON2: \
ASC_event("RPCSPACE", __LINE__, __FILE__, \
"Error: Fatal Inconsistency; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep) ; \
continue; \
\
case PROC_CORRUPT_TBL_IDX: \
ASC_event("RPCSPACE", __LINE__, __FILE__, \
"RPC - Corrupt Table/Index; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep) ; \
continue; \
\
case PROC_CORRUPT_DATABASE: \
ASC_event("RPCSPACE", __LINE__, __FILE__, \
"RPC - Corrupt DataBase; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep) ; \
continue; \
\
case PROC_HARDWARE_ERR: \
ASC_event("RPCSPACE", __LINE__, __FILE__, \
"RPC - Hardware Error; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep) ; \
continue; \
\
default: \
ASC_event("RPC_ERR", __LINE__, __FILE__, \
"Error: Unknown RPC Error [%d] - Failing RPC",ret_stat); \
return CS_FAIL; \
} \
} \
else \
return CS_SUCCEED;
#define SET_UP_QUERY \
\
ASC_diag(NULL, KERNEL_LEVEL, "Invoke RPC", __LINE__, __FILE__, \
"Invoking RPC %s, using DBPROC @ %08X", rpc_name, dbproc); \
\
start = ASC_cur_tm(); \
dbrpcinit(dbproc, rpc_name, (CS_SMALLINT)0); \
if (rpc_params != NULL) { \
va_start(param_list, rpc_params); \
for (param_ptr = rpc_params; \
param_ptr->paramname!= END_PARAM_TABLE; param_ptr++) { \
param_val = va_arg(param_list, CS_BYTE *); \
if (param_ptr->datalen == 1) \
len = 1; \
else \
len = (param_ptr->type == SYBCHAR)? \
strlen((char *) param_val): param_ptr->datalen; \
if (dbrpcparam(dbproc, param_ptr->paramname, \
param_ptr->status, param_ptr->type, param_ptr->maxlen, \
len, param_val) == CS_FAIL) { \
ASC_event("RPC_ERR", __LINE__, __FILE__, \
"Error: %s dbrpcparam(%s) Failed", \
rpc_name, param_ptr->paramname); \
return CS_FAIL; \
} \
} \
va_end(param_list); \
} \
if (dbrpcsend(dbproc) == CS_FAIL) { \
ASC_event("RPC_ERR", __LINE__, __FILE__, \
"Error: dbrpcsend(%s) Failed", rpc_name); \
return CS_FAIL; \
} \
if (dbsqlok(dbproc) == CS_FAIL) { \
ASC_event("RPC_ERR", __LINE__, __FILE__, \
"Error: dbsqlok(%s) Failed", rpc_name); \
return CS_FAIL; \
} \
end = ASC_cur_tm(); \
if (strcmp(rpc_name, "kick_start") != 0) { \
ASC_diag(NULL, LOW_LEVEL, "Invoke RPC", __LINE__, __FILE__, \
"Success: dbsqlok (%s) succeeded in %.0fms", \
rpc_name, MS_DIFF(start, end)); \
}
Worse yet, these aren't the only ones - there are a whole bunch of these things! And don't tell me to change the code. I support the app - I do not enhance it...
Jerry
CS_RETCODE inv_rpc(VOIDPTR UNUSED(ptr), DBPROCESS *dbproc,
char *rpc_name, DB_RPC_PARAM *rpc_params, ...)
{
LOCAL_VARIABLE_DECLARATION
SET_RETRY_CONFIG_VARIABLES
for (counter = 0; counter < rpc_retry_count; counter++) {
SET_UP_QUERY
while (dbresults(dbproc) != NO_MORE_RESULTS)
dbcanquery(dbproc);
PROCESS_RETURN_INFO
}
CHECK_RETURN_STATUS
return CS_SUCCEED;
}
The debugger can't see inside the macros because it isn't actual code in the executable so it jumps over it and then how do you know that the code is doing the right thing?
Not being able to debug inside macros is the very worst thing to have happen when you have a service level agreement guaranteeing less than 14 minutes of downtime a month (honestly, that's what my SLA says!)
That code above is right out of actual production code that I have to support. Here is what one of those macros actually looks like. I just have one question - would you not want the ability to debug into this code?
#define PROCESS_RETURN_INFO \
if ((ret_param_num = dbnumrets(dbproc)) > 0) { \
va_start(param_list, rpc_params); \
for (param_ptr = rpc_params; \
param_ptr->paramname != END_PARAM_TABLE; param_ptr++) { \
param_val = va_arg(param_list, CS_BYTE *); \
if (param_ptr->status == (CS_BYTE) DBRPCRETURN) { \
for (i=1; i <= ret_param_num; i++) { \
if (strcmp(dbretname(dbproc, (int) i), \
param_ptr->paramname) == 0) { \
return_val = dbretdata(dbproc, (int) i); \
len = dbretlen(dbproc, (int) i); \
for (j = 0; j < len; j++) \
param_val[j] = return_val[j]; \
if (param_ptr->maxlen != -1) \
param_val[j] = '\0'; \
if (param_ptr->type == SYBCHAR) { \
while (param_val[--j] == ' ' && j >= 0) \
param_val[j] = '\0'; \
} \
break; \
} \
} \
} \
} \
va_end(param_list); \
} \
\
if (dbhasretstat(dbproc)) { \
ret_stat = dbretstatus(dbproc); \
switch (ret_stat) { \
case PROC_SUCCESS_STATUS: \
return CS_SUCCEED; \
\
case PROC_DEADLOCK_VICTIM: \
ASC_event("SYS_WARN", __LINE__, __FILE__, \
"Warning! RPC DeadLock Victim, Ret Stat %d, Retry %d", \
ret_stat, counter); \
ASC_sleep(rpc_retry_sleep)
continue; \
\
case PROC_RESOURCE_ERR: \
ASC_event("RPCSPACE", __LINE__, __FILE__, \
"Critical Resource Error; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep)
continue; \
\
case PROC_MISSING_OBJECT: \
ASC_event("RPC_ERR", __LINE__, __FILE__, \
"Error: Missing RPC Object; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep)
continue; \
\
case PROC_DATATYPE_ERR: \
ASC_event("RPC_ERR", __LINE__, __FILE__, \
"RPC Datatype Error; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep)
continue; \
\
case PROC_PERMISSION_ERR: \
ASC_event("RPC_ERR", __LINE__, __FILE__, \
"RPC Permission Error; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep)
continue; \
\
case PROC_SYNTAX_ERR: \
ASC_event("RPC_ERR", __LINE__, __FILE__, \
"RPC Syntax Error; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep)
continue; \
\
case PROC_MISC_ERR: \
ASC_event("RPC_ERR", __LINE__, __FILE__, \
"Misc User RPC Error; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep)
continue; \
\
case PROC_NON_FATAL_INT_ERR: \
ASC_event("RPC_ERR", __LINE__, __FILE__, \
"Non Fatal Internal Error; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep)
continue; \
\
case PROC_SYS_LIMIT_ERR: \
ASC_event("RPCSPACE", __LINE__, __FILE__, \
"System Limit Reached; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep)
continue; \
\
case PROC_FATAL_INCON1: \
case PROC_FATAL_INCON2: \
ASC_event("RPCSPACE", __LINE__, __FILE__, \
"Error: Fatal Inconsistency; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep)
continue; \
\
case PROC_CORRUPT_TBL_IDX: \
ASC_event("RPCSPACE", __LINE__, __FILE__, \
"RPC - Corrupt Table/Index; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep)
continue; \
\
case PROC_CORRUPT_DATABASE: \
ASC_event("RPCSPACE", __LINE__, __FILE__, \
"RPC - Corrupt DataBase; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep)
continue; \
\
case PROC_HARDWARE_ERR: \
ASC_event("RPCSPACE", __LINE__, __FILE__, \
"RPC - Hardware Error; Sleeping %d Mins, Retry %d", \
rpc_error_sleep/60, counter); \
ASC_sleep(rpc_error_sleep)
continue; \
\
default: \
ASC_event("RPC_ERR", __LINE__, __FILE__, \
"Error: Unknown RPC Error [%d] - Failing RPC",ret_stat); \
return CS_FAIL; \
} \
} \
else \
return CS_SUCCEED;
#define SET_UP_QUERY \
\
ASC_diag(NULL, KERNEL_LEVEL, "Invoke RPC", __LINE__, __FILE__, \
"Invoking RPC %s, using DBPROC @ %08X", rpc_name, dbproc); \
\
start = ASC_cur_tm(); \
dbrpcinit(dbproc, rpc_name, (CS_SMALLINT)0); \
if (rpc_params != NULL) { \
va_start(param_list, rpc_params); \
for (param_ptr = rpc_params; \
param_ptr->paramname!= END_PARAM_TABLE; param_ptr++) { \
param_val = va_arg(param_list, CS_BYTE *); \
if (param_ptr->datalen == 1) \
len = 1; \
else \
len = (param_ptr->type == SYBCHAR)? \
strlen((char *) param_val): param_ptr->datalen; \
if (dbrpcparam(dbproc, param_ptr->paramname, \
param_ptr->status, param_ptr->type, param_ptr->maxlen, \
len, param_val) == CS_FAIL) { \
ASC_event("RPC_ERR", __LINE__, __FILE__, \
"Error: %s dbrpcparam(%s) Failed", \
rpc_name, param_ptr->paramname); \
return CS_FAIL; \
} \
} \
va_end(param_list); \
} \
if (dbrpcsend(dbproc) == CS_FAIL) { \
ASC_event("RPC_ERR", __LINE__, __FILE__, \
"Error: dbrpcsend(%s) Failed", rpc_name); \
return CS_FAIL; \
} \
if (dbsqlok(dbproc) == CS_FAIL) { \
ASC_event("RPC_ERR", __LINE__, __FILE__, \
"Error: dbsqlok(%s) Failed", rpc_name); \
return CS_FAIL; \
} \
end = ASC_cur_tm(); \
if (strcmp(rpc_name, "kick_start") != 0) { \
ASC_diag(NULL, LOW_LEVEL, "Invoke RPC", __LINE__, __FILE__, \
"Success: dbsqlok (%s) succeeded in %.0fms", \
rpc_name, MS_DIFF(start, end)); \
}
Worse yet, these aren't the only ones - there are a whole bunch of these things! And don't tell me to change the code. I support the app - I do not enhance it...
Jerry
jerry_jeremiah,
For that much long code, you have little options left ;o) ... you have to use macros ... most compilers will not make that big a code as inline even if you specify them as inline.
Some compilers can let you see into macros (e.g. gdb with code compiled at 3rd level using gcc) ... however I am not sure if it allows stepping through that big a macro... so capacity is quite limited indeed
For that much long code, you have little options left ;o) ... you have to use macros ... most compilers will not make that big a code as inline even if you specify them as inline.
Some compilers can let you see into macros (e.g. gdb with code compiled at 3rd level using gcc) ... however I am not sure if it allows stepping through that big a macro... so capacity is quite limited indeed
Faced with debugging stuff like jerry_jeremiah's, I'd be tempted to set up my build environment to preprocess first, then build the preprocessed files. Then you could debug them...
That's definitely macro abuse. Exapanding every function out to a couple hundred lines of code must lead to incredible code bloat.
That's definitely macro abuse. Exapanding every function out to a couple hundred lines of code must lead to incredible code bloat.
ASKER
Thanks for all the replies.
but one by Sunnycoder suited me best.
SunnyCoder, Please as you have written "..Macros are faster.."
But why macros are faster than functions(inline).
Thanks TO all again.
but one by Sunnycoder suited me best.
SunnyCoder, Please as you have written "..Macros are faster.."
But why macros are faster than functions(inline).
Thanks TO all again.
I would like to stress that they are just marginally faster .... My source of information had derived this conclusion emperically ...
C++ faq offers some information
http://www.parashift.com/c++-faq-lite/inline-functions.html#faq-9.3
C++ faq offers some information
http://www.parashift.com/c++-faq-lite/inline-functions.html#faq-9.3
Macro isn't type checked and does not evaluate arguments, but simply takes the string passed to the macro and replace each occurence of macro argument in the text of macro with actual string for that parameter. It may give you sometimes very surpising results. For instance macro:
#define MAX(x,y) ((x)>(y)?(x):(y))
when called like this:
y = 2;
x = 2;
x = MAX(++x, y);
will return 4! not 3, because in the code the macro is expanded to:
x = ((++x)>(y)?(++x):(y))
If you define inline function for that purpose the result will be 3.
inline int Max(int x, int y)
{
return x>y?x:y;
}