Solved

C++ Inline fucntion vs. macro

Posted on 2004-03-30
10
36,444 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
  • 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
Gigs: Get Your Project Delivered by an Expert

Select from freelancers specializing in everything from database administration to programming, who have proven themselves as experts in their field. Hire the best, collaborate easily, pay securely and get projects done right.

 
LVL 45

Accepted Solution

by:
sunnycoder earned 100 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

Courses: Start Training Online With Pros, Today

Brush up on the basics or master the advanced techniques required to earn essential industry certifications, with Courses. Enroll in a course and start learning today. Training topics range from Android App Dev to the Xen Virtualization Platform.

Question has a verified solution.

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

This article is meant to give a basic understanding of how to use R Sweave as a way to merge LaTeX and R code seamlessly into one presentable document.
Whether you’re a college noob or a soon-to-be pro, these tips are sure to help you in your journey to becoming a programming ninja and stand out from the crowd.
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.

808 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