Search code examples
c++polymorphismearly-binding

C++ early binding and late binding


I read about early and late binding in C++:

int add (int x, int y)
{
  return x+y;
}

int main()
{
   int a=add(5,6);//early binding
   int (*p_add)(int,int)=add;

   int b=p_add(5,19);
}

Why can't int b=p_add(5,19) be resolved at compile time? We all know it is associated with the add function at compile time. Then why can't we resolve it at compile time same as add function? My problem is that if i know add(x,y) at compile time then I may predict p_add at compile time too.


Solution

  • Here's what gcc and Clang produce for your code as it stands right now:

    main:                                   # @main
        xor     eax, eax
        ret
    

    code on Godbolt

    So in this case, we don't really have either early or late binding. Rather, we have no binding to the function at all--you didn't use the result you got from calling the function (either directly or via a pointer), so the compiler simply didn't generate any code to call the function at all.

    We can repair that with code on this order:

    #include <iostream>
    
    int add (int x, int y)
    {
      return x+y;
    }
    
    int main()
    {
       int a=add(5,6);//early binding
       int (*p_add)(int,int)=add;
    
       int b=p_add(5,19);
       std::cout << b;
    }
    

    In this case, the compiler still detect that the result of the function doesn't depend on anything at compile time, so it computes the value at compile time, and prints it out as a constant:

    mov esi, 24
    mov edi, OFFSET FLAT:_ZSt4cout
    call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)ant:
    

    Code on Godbolt

    So, we still don't have any real "binding" to the function. Let's have it use inputs it won't know until run-time:

    #include <iostream>
    #include <cstdlib>
    
    int add (int x, int y)
    {
      return x+y;
    }
    
    int main()
    {
        int x1 = rand();
        int x2 = rand();
    
       int a=add(x1, x2);//early binding
       int (*p_add)(int,int)=add;
    
       int b=p_add(x1,x2);
       std::cout << b;
    }
    

    This source produces the following object code:

    call rand
    mov ebx, eax
    call rand
    mov edi, OFFSET FLAT:_ZSt4cout
    lea esi, [rbx+rax]
    call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
    

    The compiler is still aware that the pointer consistently points to one particular function, so even though the source code shows the function being called via a pointer, in the object code we don't call the function via a pointer...and in fact, we still don't call the function at all. Instead, the code for the body of the function has been generated inline.

    To get an actual function call via a pointer, we can have a pointer that refers to either of two different functions, and it won't be apparent until run-time which of the two to use in a particular case. For example:

    #include <iostream>
    #include <cstdlib>
    
    int add (int x, int y)
    {
      return x+y;
    }
    
    int sub(int x, int y) { 
        return x-y;
    }
    
    int main()
    {
        int x1 = rand();
        int x2 = rand();
    
        int z = rand() % 2;
    
       int (*p_add)(int,int) = z ? add : sub;
    
       int b=p_add(x1,x2);
       std::cout << b;
    }
    

    This (finally!) make the call via a pointer actually happen as a call via a pointer:

      call rand
      mov edx, OFFSET FLAT:sub(int, int) ; start by assuming we'll subract
      mov esi, r12d
      mov edi, ebp
      test al, 1                         ; then see if we have an odd or even number
      mov eax, OFFSET FLAT:add(int, int)
      cmove rax, rdx                     ; if necessary, point to add
      call rax                           ; and finally call the function via the pointer
      mov edi, OFFSET FLAT:_ZSt4cout
      mov esi, eax
      call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
    

    Code on Godbolt

    Summary

    If it's apparent at compile time what function will be called, the compiler probably won't generate code to call the function via a pointer, even if that's what the source code shows.