0

Firstly, let me clarify this question doesn't explain my doubt clearly. To set the context clear. I'm asking this question specifically with regard to function pointers in C/C++.

I know the difference between early binding and late binding and how it works. What I would like to understand is the following with one example using function pointers in C/C++:

In many textbooks, it has been mentioned :

advantage of late binding is that it is more flexible than early binding, because decisions about what function to call do not need to be made until run time.

Also, it mentions:

With late binding, the program has to read the address held in the pointer and then jump to that address. This involves one extra step, making it slightly slower.

#include <iostream>
using namespace std;

int Add(int nX, int nY)
{
    return nX + nY;
}

int Subtract(int nX, int nY)
{
    return nX - nY;
}

int Multiply(int nX, int nY)
{
    return nX * nY;
}

int main()
{
    int nX;
    cout << "Enter a number: ";
    cin >> nX;

    int nY;
    cout << "Enter another number: ";
    cin >> nY;

    int nOperation;
    do
    {
        cout << "Enter an operation (0=add, 1=subtract, 2=multiply): ";
        cin >> nOperation;
    } while (nOperation < 0 || nOperation > 2);

    // Create a function pointer named pFcn (yes, the syntax is ugly)
    int (*pFcn)(int, int);

    // Set pFcn to point to the function the user chose
    switch (nOperation)
    {
        case 0: pFcn = Add; break;
        case 1: pFcn = Subtract; break;
        case 2: pFcn = Multiply; break;
    }

    // Call the function that pFcn is pointing to with nX and nY as parameters
    cout << "The answer is: " << pFcn(nX, nY) << endl;

    return 0;
}

here, there is no advantage of using late binding and early binding as in the example below should be preferred.

int nResult = 0;
switch (nOperation)
{
    case 0: nResult = Add(nX, nY); break;
    case 1: nResult = Subtract(nX, nY); break;
    case 2: nResult = Multiply(nX, nY); break;
}

cout << "The answer is: " << nResult << endl;

Could someone explain with an easy example like below where late binding is advantageous and why should someone choose it over early binding?

Community
  • 1
  • 1
Vivek Vijayan
  • 337
  • 5
  • 19
  • 3
    One example is adding a plugin at runtime. How about embedding an Excel spreadheet in a Word document and being able to edit it from Word. In fact, you could invent a document format, an application to edit it and Word could late-bind to your program and people coulld use it. These are examples of late binding. If you see an advantage or not, that is up to you. – clarasoft-it Aug 05 '16 at 20:38
  • 1
    I'm asking specifically w.r.t **function pointers in C/C++**. Could someone please remove the hold and try to answer? – Vivek Vijayan Aug 05 '16 at 21:06
  • 1
    What makes you think late binding is related to `function pointers in C/C++`? It's not. See for example [How To Use Visual C++ to Access DocumentProperties with Automation](https://support.microsoft.com/en-us/kb/238393) - that's `C++` code automating MS Word using late binding, and there is no `function pointer` used or needed. – dxiv Aug 05 '16 at 22:29
  • @dxiv I agree with you completely. But I'm trying to understand it through a programming language that I know. I thought it would be easy to explain through function pointers. Wouldn't it? Is it too hard to explain in the context of using function pointers? – Vivek Vijayan Aug 05 '16 at 22:38
  • @VivekVijayan Still not sure what you mean by `in the context of using function pointers`. Just as an example, late binding in Windows OLE/COM essentially boils down to [IDispatch::GetIDsOfNames](https://msdn.microsoft.com/en-us/library/windows/desktop/ms221306.aspx) and [IDispatch::Invoke](https://msdn.microsoft.com/en-us/library/windows/desktop/ms221479.aspx). Neither uses function pointers, and I wouldn't know how to explain them `in the context of using function pointers`. You'll need to frame your question better (and perhaps more narrowly) since it's not at all clear what you are after. – dxiv Aug 05 '16 at 22:48
  • @dxiv Does this edit make it any clear? I just meant that function pointers could be used to show late-binding. Virtual functions are good examples of late-binding / runtime polymorphism too. – Vivek Vijayan Aug 05 '16 at 23:01
  • @VivekVijayan The sample code you just edited in is what I'd call *an indirect call via a function pointer*. I've never seen it called `late binding`. Maybe you should mention what `textbooks` you are referring to, since I don't think that's too common a usage. As for the question itself, suppose you replace the "*perform operation on nX, nY and return result*" task with "*round nX, nY to nearest multiples of 100, perform operation, then round result to nearest multiple of 100*". Try writing the latter with vs. without the function pointer - the former would be far simpler and more clear code. – dxiv Aug 05 '16 at 23:16
  • @VivekVijayan https://en.wikipedia.org/wiki/Late_binding – kfsone Aug 06 '16 at 00:13

1 Answers1

7

OK, I'm going to skip over the whole "early binding vs late binding" definition question and pretend you asked "why would someone use function pointers instead of a switch statement?"

Because function pointers are more flexible. They aren't static. Let's take the business end of your code:

int InvokeOperation(int nOperation, int nX, int nY)
{
    switch (nOperation)
    {
        case 0: return Add(nX, nY);
        case 1: return Subtract(nX, nY);
        case 2: return Multiply(nX, nY);
    }
}

That's nice and functional. But it's not flexible. Why? Because all of the functions that can be called are defined by InvokeOperation; if you want to add a new operation, you have to be able to change InvokeOperation.

By contrast, if you used function pointers, you can build a whole registry of operations:

using Func = int(*)(int, int);
struct Op{Func func; std::string name;};
std::vector<Func> funcs =
{
    {&Add, "Add"},
    {&Subtract, "Subtract"},
    {&Multiply, "Multiply"},
};

int InvokeOperation(int nOperation, int nX, int nY)
{
    return funcs[nOperation].func(nX, nY);
}

Now, if you want to add more operations, just insert elements into funcs. If InvokeOperation were part of some library, you wouldn't necessarily have the right to change it. With static binding, you would have an inflexible system; what it supports is what it will always support.

With dynamic binding, you can add whatever stuff you want, whether you have the right to modify the library directly or not.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982