CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 1 of 1
  1. #1
    Join Date
    Oct 2002
    Location
    Timisoara, Romania
    Posts
    14,360

    C++ General: What are the differences between inline functions and macros?

    Q: What are inline functions good for?

    A: Inline functions are best used for small functions such as accessing private data members. The main purpose of these inline functions (usually one or two lines of code) is to return state information about objects; short functions are sensitive to the overhead of function calls. Longer functions spend proportionately less time in the calling/returning sequence and benefit less from inlining. An inline function typically saves the overhead of:
    • Function calls (including parameter passing and placing the object's address on the stack)
    • Preservation of caller's stack frame
    • New stack-frame setup
    • Return-value communication
    • Old stack-frame restore
    • Return

    The inline and __inline specifiers instruct the compiler to insert a copy of the function body into each place the function is called. The insertion (called "inline expansion" or "inlining") occurs only if the compiler's cost/benefit analysis show it to be profitable. Inline expansion alleviates the function-call overhead at the potential cost of larger code size.

    Because there is no guarantee that an inline function will actually be inlined, you can bypass the compiler optimization by using the __forceinline keyword. This is Microsoft specific.

    Quote Originally Posted by MSDN
    The __forceinline keyword overrides the cost/benefit analysis and relies on the judgment of the programmer instead. Exercise caution when using __forceinline. Indiscriminate use of __forceinline can result in larger code with only marginal performance gains or, in some cases, even performance losses (due to increased paging of a larger executable, for example).
    However, this still does not force the compiler to actually inline every function you declared that way...

    Quote Originally Posted by MSDN
    The compiler treats the inline expansion options and keywords as suggestions. There is no guarantee that functions will be inlined. You cannot force the compiler to inline a particular function, even with the __forceinline keyword.
    For further information, please refer to the MSDN.

    In C++ a class's member functions can be declared inline either by using the inline keyword or by placing the function definition within the class definition.
    Code:
    class foo
    {
      int a;
    public:
      int getA() {return a;} // implicitly inline
    };

    Q: I’m confused about the use of inline functions and macros. What is the difference between them?

    A: Inline functions are similar to macros because they both are expanded at compile time, but the macros are expanded by the preprocessor, while inline functions are parsed by the compiler. There are several important differences:
    • Inline functions follow all the protocols of type safety enforced on normal functions.
    • Inline functions are specified using the same syntax as any other function except that they include the inline keyword in the function declaration.
    • Expressions passed as arguments to inline functions are evaluated once. In some cases, expressions passed as arguments to macros can be evaluated more than once.



    Q: When should I use macro and when inline functions?

    A: Besides the difference already pointed out, you also must have in mind that because macros are expanded at pre-compile time, you cannot use them for debugging, but you can use inline functions.
    Code:
    #define max(a,b) (a>b?a:b)
    
    class foo
    {
    public:
      inline int maxim(int a, int b);
    };
    
    inline int foo::maxim(int a, int b)
    {
      return a > b ? a : b;
    }
    
    int main()
    {
      foo f;
      int x = max(1,2);
      int y = f.maxim(1,2);
      return 0;
    }
    In this example you can put a breakpoint in foo::maxim and step into the method, though it is an inline function. (if maxim was defined in the body of the foo class the keyword inline would have not been necessary) However, because macros are expanded before compilation actually starts, you cannot do that with the macro max. It is expanded at pre-compile time to:
    Code:
    int main()
    {
      foo f;
      int x = 1>2?1:2;
      int y = f.maxim(1,2);
      return 0;
    }
    Also you must be carefully with macros, because they can have the arguments evaluated more than once. Here is an example:
    Code:
    #include <iostream>
    
    using namespace std;
    
    #define max(a,b) (a>b?a:b)
    
    int main()
    {
      int a = 0;
      int b = 1;
      int c = max(a++, b++);
    
      cout << a << endl << b << endl;
    
      return 0;
    }
    The intention is to have the program print 1 and 2, but because the macro is expanded to:
    Code:
    int c = a++ > b++ ? a++ : b++;
    b will be incremented twice, and the program prints 1 and 3.

    I’ve seen several ugly ways of using macros. The example below synthesizes the idea of bad macro usage:
    Code:
    #define INIT          \
      if(pSomeData == NULL) {    \
        pSomeData = new int;  \
        *pSomeData = 0;      \
      } else {          \
        *pSomeData = 0;      \
      }
    
    class foo
    {
      int* pSomeData;
    public:
      foo();
      void do_something();
    };
    
    foo::foo()
    {
      INIT
      // do other things
    }
    
    void foo::do_something()
    {
      INIT
      // do something here
    }
    Never do something like that. You can put the initialization in a member function of foo.

    Here is just another bad example of macros:
    Code:
    #include <iostream>
    
    using namespace std;
    
    #define BEGIN          \
      if(index<0 || index>=5)    \
        return;          \
      goo temp = _goo[index];    \
      temp.Init();        \
      
    #define END            \
      temp.Close();
    
    class goo
    {
    public:
      goo(){}
      goo(const goo& cpy) {}
      const goo& operator=(const goo& rval) {  return *this;}
      void Init() { cout << "goo::init" << endl;}
      void Action() {cout << "goo::action" << endl;}
      void Reaction() {cout << "goo::reaction" << endl;}
      void Close() {cout << "goo::close" << endl;}
    };
    
    class foo
    {
      goo *_goo;
    public:
      foo();
      ~foo();
      void do_something(int index);
      void do_something_else(int index);
    };
    
    foo::foo()
    {
      _goo = new goo[5];
    }
    
    foo::~foo()
    {
      delete [] _goo;
    }
    
    void foo::do_something(int index)
    {
      BEGIN  
      temp.Action();
      END
    }
    
    void foo::do_something_else(int index)
    {
      BEGIN
      temp.Reaction();
      END
    }
    
    int main()
    {
      foo f;
      f.do_something(0);
      f.do_something_else(4);
    
      return 0;
    }
    If you have something like this in your code then that’s a sign of bad design and must use factorization.


    Q: From what I've seen so far, I should be cautious with macros. Aren't macros useful after all?

    A: There are many useful macros. There are predefined macros, some ANSI compliant, some Microsoft specific. Here are ANSI-Compliant Predefined Macros:
    • __DATE__ : The compilation date of the current source file.
    • __FILE__ : The name of the current source file.
    • __LINE__ : The line number in the current source file.
    • __STDC__ : Indicates full conformance with the ANSI C standard.
    • __TIME__ : The most recent compilation time of the current source file.
    • __TIMESTAMP__ : The date and time of the last modification of the current source file, expressed as a string literal.

    Read more about these predefined macros in MSDN.

    Very useful macros are ASSERT and TRACE (which comes in several formes). Read this FAQ about how to use ASSERT. If you look how it is defined in afx.h:
    Code:
    #define TRACE              ::AfxTrace
    #define THIS_FILE          __FILE__
    #define ASSERT(f) \
    	do \
    	{ \
    	if (!(f) && AfxAssertFailedLine(THIS_FILE, __LINE__)) \
    		AfxDebugBreak(); \
    	} while (0) \
    you can see that it uses __FILE__ and __LINE__ to indicate the source file and the line where the assertion failed.

    The other macro that I've mention, TRACE, sends the specified string to the debugger of the current application (just as ATLTRACE2 which has the same behaviour).

    By mentioning just these macros doesn't mean they are the only useful macros. They are just some examples of useful macros. You should use macros when you cannot do something with an inline function or where the code will be more clean (maintainable) with the use of macros.


    Q: Where can I read more about macros and inline functions?

    A: Here are some links:



    Last edited by Andreas Masur; July 23rd, 2005 at 01:08 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured