?
Solved

C++ Inline fucntion vs. macro

Posted on 2004-03-30
10
Medium Priority
?
36,454 Views
Last Modified: 2011-08-18
hello.
Please, what is difference between an inline function and a macro in C++ since both are expanded in-line.
waiting..
thanks.
0
Comment
Question by:soodsandeep
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
  • 3
  • 2
  • +2
10 Comments
 
LVL 33

Expert Comment

by:hongjun
ID: 10721129
The difference is in interpreting arguments. Arguments for inline functions are typed, therefore compiler can apply some type checking to that function calls.
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;
   }
0
 
LVL 33

Expert Comment

by:hongjun
ID: 10721131
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.
0
 
LVL 33

Expert Comment

by:hongjun
ID: 10721135
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.
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 45

Accepted Solution

by:
sunnycoder earned 400 total points
ID: 10721167
Hi soodsandeep,

1. macros are processed by the preprocessor while inline functions are processed by the compiler
2. for macros the code is literally copied into the location it was called from. So if the user passes a "double" instead of an "int" then problems could occur. However, if this senerio happens with an inline function the compiler will complain about incompatible types.
3. macros are marginally faster

Sunnycoder
0
 
LVL 14

Expert Comment

by:wayside
ID: 10723595
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_MESSAGE_MAP.
0
 
LVL 5

Expert Comment

by:jerry_jeremiah
ID: 10730476
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_DECLARATIONS
    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
0
 
LVL 45

Expert Comment

by:sunnycoder
ID: 10730573
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
0
 
LVL 14

Expert Comment

by:wayside
ID: 10731514
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.
0
 

Author Comment

by:soodsandeep
ID: 10738216
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.
0
 
LVL 45

Expert Comment

by:sunnycoder
ID: 10738614
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

0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

There is an easy way, in .NET, to centralize the treatment of all unexpected errors. First of all, instead of launching the application directly in a Form, you need first to write a Sub called Main, in a module. Then, set the Startup Object to th…
Having just graduated from college and entered the workforce, I don’t find myself always using the tools and programs I grew accustomed to over the past four years. However, there is one program I continually find myself reverting back to…R.   So …
This tutorial covers a step-by-step guide to install VisualVM launcher in eclipse.
Viewers will learn how to properly install Eclipse with the necessary JDK, and will take a look at an introductory Java program. Download Eclipse installation zip file: Extract files from zip file: Download and install JDK 8: Open Eclipse and …
Suggested Courses

777 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question