C++ can compilers inline a function pointer?

12,332

Sure thing.

It knows the value of function is the same as the value it passes it, knows the definition of the function, so just replaces the definition inline and calls the function directly.

I can't think of a condition where a compiler won't inline a one-line function call, it's just replacing a function call with a function call, no possible loss.


Given this code:

#include <iostream>

template <typename Function>
void functionProxy(Function function)
{
    function();
}

struct Functor
{
    void operator()() const
    {
        std::cout << "functor!" << std::endl;
    }
};

void function()
{
    std::cout << "function!" << std::endl;
}

//#define MANUALLY_INLINE

#ifdef MANUALLY_INLINE
void test()
{
    Functor()();

    function();

    [](){ std::cout << "lambda!" << std::endl; }();
}
#else
void test()
{
    functionProxy(Functor());

    functionProxy(function);

    functionProxy([](){ std::cout << "lambda!" << std::endl; });
}
#endif

int main()
{
    test();
}

With MANUALLY_INLINE defined, we get this:

test:
00401000  mov         eax,dword ptr [__imp_std::endl (402044h)]  
00401005  mov         ecx,dword ptr [__imp_std::cout (402058h)]  
0040100B  push        eax  
0040100C  push        offset string "functor!" (402114h)  
00401011  push        ecx  
00401012  call        std::operator<<<std::char_traits<char> > (401110h)  
00401017  add         esp,8  
0040101A  mov         ecx,eax  
0040101C  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (40204Ch)]  
00401022  mov         edx,dword ptr [__imp_std::endl (402044h)]  
00401028  mov         eax,dword ptr [__imp_std::cout (402058h)]  
0040102D  push        edx  
0040102E  push        offset string "function!" (402120h)  
00401033  push        eax  
00401034  call        std::operator<<<std::char_traits<char> > (401110h)  
00401039  add         esp,8  
0040103C  mov         ecx,eax  
0040103E  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (40204Ch)]  
00401044  mov         ecx,dword ptr [__imp_std::endl (402044h)]  
0040104A  mov         edx,dword ptr [__imp_std::cout (402058h)]  
00401050  push        ecx  
00401051  push        offset string "lambda!" (40212Ch)  
00401056  push        edx  
00401057  call        std::operator<<<std::char_traits<char> > (401110h)  
0040105C  add         esp,8  
0040105F  mov         ecx,eax  
00401061  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (40204Ch)]  
00401067  ret  

And without, this:

test:
00401000  mov         eax,dword ptr [__imp_std::endl (402044h)]  
00401005  mov         ecx,dword ptr [__imp_std::cout (402058h)]  
0040100B  push        eax  
0040100C  push        offset string "functor!" (402114h)  
00401011  push        ecx  
00401012  call        std::operator<<<std::char_traits<char> > (401110h)  
00401017  add         esp,8  
0040101A  mov         ecx,eax  
0040101C  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (40204Ch)]  
00401022  mov         edx,dword ptr [__imp_std::endl (402044h)]  
00401028  mov         eax,dword ptr [__imp_std::cout (402058h)]  
0040102D  push        edx  
0040102E  push        offset string "function!" (402120h)  
00401033  push        eax  
00401034  call        std::operator<<<std::char_traits<char> > (401110h)  
00401039  add         esp,8  
0040103C  mov         ecx,eax  
0040103E  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (40204Ch)]  
00401044  mov         ecx,dword ptr [__imp_std::endl (402044h)]  
0040104A  mov         edx,dword ptr [__imp_std::cout (402058h)]  
00401050  push        ecx  
00401051  push        offset string "lambda!" (40212Ch)  
00401056  push        edx  
00401057  call        std::operator<<<std::char_traits<char> > (401110h)  
0040105C  add         esp,8  
0040105F  mov         ecx,eax  
00401061  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (40204Ch)]  
00401067  ret

The same. (Compiled with MSVC 2010, vanilla Release.)

Share:
12,332
peoro
Author by

peoro

Apparently, this user has nothing to say.

Updated on June 24, 2022

Comments

  • peoro
    peoro about 2 years

    Suppose I've got a function functionProxy that takes a generic parameter function and call its operator():

    template< typename Function > void functionProxy( Function function ) {
        function();
    }
    

    The object passed to it may be:

    • a functor:

      struct Functor {
          void operator()() const {
              std::cout << "functor!" << std::endl;
          }
      };
      
    • a function:

      void function( ) {
          std::cout << "function!" << std::endl;
      }
      
    • a (C++0x) lambda function:

      [](){ std::cout << "lambda!" << std::endl; }
      

    int main( )
    {
        functionProxy( Functor() );
        functionProxy( function );
        functionProxy( [](){ std::cout << "lambda!" << std::endl; } );
        return 0;
    }
    

    Will the compiler be able to inline function within functionProxy in all the above cases?

  • Potatoswatter
    Potatoswatter over 13 years
    Sounds good in principle, but does it pass the try and see test? I'm unfortunately too busy right now/these days to check. Very curious though as this has occurred to me.
  • GManNickG
    GManNickG over 13 years
    @Potatoswatter: Updated for your viewing pleasure.
  • peoro
    peoro over 13 years
    Uh, nice! Also GCC generates the same code (when optimizations are on).
  • Matthieu M.
    Matthieu M. over 13 years
    @GMan: I suppose (unless LTO kicks in) that it is necessary for the function to be defined inline (and not in another translation unit) ?
  • GManNickG
    GManNickG over 13 years
    @Matt: That sounds correct to me. I suppose I not only tested whether or not the call was inlined, but the functions as well. I'll test if you'd like, but I suspect that without the ability to inline the called function, it'll do the best it can and inline the call itself.
  • justin
    justin over 13 years
    although it actually adds to the instruction count, i've seen (specifically, older versions of) GCC pop one liners out of line when instruction counts are already high. if the (expanded) function body is already large, some simple calls may not be inlined.
  • Admin
    Admin about 11 years
    @GManNickG Hey Nick, I saw this answer a while ago and I am running some problems with function pointers in MSVC 2012, I was wondering if you could help me! stackoverflow.com/questions/16462800/… Sorry if it's unpolite I ask it this way, but I didn't know how else to do it. I'd would add a bounty if that helps! Kind regards Christian