Cpp Function Pointer As Argumentative Essay

Using C++ Member Functions for C Function Pointers

The research department here at p-nand-q.com is pleased to announce another totally usefull study.... not!

Understanding the problem

The consensus on the internet is that you cannot pass C++ member function pointers to functions expecting C function pointers.

For example, a typical question is How to pass a method to qsort?, and the answer marked as "correct" says you should provide the address of a static function. Clearly that is too easy: static class member functions are mostly the same as plain old C functions, so in a way that answer is simply cheating... Our computer scientists (well, there is only one of them, and he isn't a really good scientist either) came up with an ingenious way for programmers to transcend classical logic and pass member functions.

Using C Function Pointers as C Function Pointers

Let's work this through by using a time-honoured C function: qsort.

Assume you have some infrastructure that allows you to write the following:

std::vector<int>test;// create an array of 100 ints with random dataSetupTestVectorWithRandomData(test,100);

If you want to sort that list with the C qsort function, you would traditionally have a C function and use it like the following:

intPlainOldCCompareFunc(constvoid*a,constvoid*b){return*static_cast<constint*>(a)-*static_cast<constint*>(b);}...// sort arrayqsort(&test[0],test.size(),sizeof(int),&PlainOldCCompareFunc);// verify it is indeed sortedassert(IsSortedAscending(test));

OK, so far this is the classical solution. But you're using a C function, and there is a rule in your contract that C functions get paid less than C++ functions, so you need to be seen passing a C++ method instead. What do you do?

Using Pointers to Static C++ Methods as C Function Pointers

Well, you declare the method as static. Example:

classClassWithStaticCallback{public:staticintStaticMemberFunction(constvoid*a,constvoid*b){return*static_cast<constint*>(a)-*static_cast<constint*>(b);}};...// sort arrayqsort(&test[0],test.size(),sizeof(int),&ClassWithStaticCallback::StaticMemberFunction);// verify it is indeed sortedassert(IsSortedAscending(test));

Well, this is the standard, boring answer you'd get if you asked regular people.

Using C++ Methods as C Function Pointers

But you're unsatisfied with that solution. It is too easy. It is too readable. It doesn't help you in securing a good job. You need more. And we are here to give it to you. We will show you how you can finally write code like this:

classClassWithCallback{public:ClassWithCallback(boolbSortAscending):m_bSortAscending(bSortAscending){}/* Look ma, no static! */intCompareMemberFunc(constvoid*a,constvoid*b){// access to member variables ...if(m_bSortAscending){return*static_cast<constint*>(a)-*static_cast<constint*>(b);}else{return*static_cast<constint*>(b)-*static_cast<constint*>(a);}}private:boolm_bSortAscending;};...ClassWithCallbacksortAscending(true),sortDescending(false);// sort array ascendingqsort(&test[0],test.size(),sizeof(int),QSortMemberFunctionCallback(&sortAscending,&ClassWithCallback::CompareMemberFunc));// verify it is indeed sortedassert(IsSortedAscending(test));// sort array descendingqsort(&test[0],test.size(),sizeof(int),QSortMemberFunctionCallback(&sortDescending,&ClassWithCallback::CompareMemberFunc));// verify it is indeed sortedassert(IsSortedDescending(test));

That is more like it. So how does it work?

How it works

Let's start by doing a short recap of what we know. Take a look at the following declaration:

typedefint(*LPFN_QSortCCallback)(constvoid*a,constvoid*b);typedefint(ClassWithCallback::*LPFN_QSortMemberFunctionCallback)(constvoid*a,constvoid*b);

So, a C function pointer is a different type than a C++ member function pointer. When the compiler sees a C++ member function like this:

intClassWithCallback::CompareMemberFunc(constvoid*a,constvoid*b)

what it really does is create a C function like this:

intCompareMemberFunc(ClassWithCallback*this,constvoid*a,constvoid*b)

It creates a hidden "this-pointer". This immediately explains why the two function pointers are not compatible: one points to a function receiving two args, the other to a function receiving three args. To put it another way: the caller of the function pointer will provide only two arguments, not three. (By the way: this also explains why functions work: they have no object associated, so there is no hidden this-pointer generated, so their syntax looks the same). So what we need to do is this

  • We must provide the Caller with a compatible C function: a function of two arguments
  • In that function, we somehow must identify the missing "this"-Pointer and use that to call the member function pointer.

To put it yet another way: somehow, our function needs state.

Now, at this point an obvious solution can be seen: use global variables. Example:

classClassWithCallback{...intCompareMemberFunc(constvoid*a,constvoid*b){...}staticintC_Compatible_StaticFunc(constvoid*a,constvoid*b){// somehow, somewhere there is a global ClassWithCallback pointer:returng_pClassWithCallback->CompareMemberFunc(a,b);}};

However, this has a couple of problems:

  • It uses a global variable. It can point only to one object at any one time. You cannot have two separate functions (threads, cores) call the callback at the same time, because the global variable can have only one value
  • Really, what you would like to have is something like this:
    • Generate a function + global variable on the fly
    • Fill it with the value for a particular call

Enter templates. The basic idea is this: each template instantiation will create a different set of functions, including a different set of static functions.

At the core of our solution is a template very much like this:

template<intcontext>classDynamicQSortCallback{public:staticintGeneratedStaticFunction(constvoid*a,constvoid*b){returnStaticInvoke(context,a,b);}};

When the compiler sees us using , it will generate a function , and when it sees us using , it will generate a different function with a different context. This means the following holds true:

&DynamicQSortCallback<0x00>::GeneratedStaticFunction!=&DynamicQSortCallback<0x01>::GeneratedStaticFunction!=...!=&DynamicQSortCallback<n>::GeneratedStaticFunction

Armed with this information you should be able to come up with a solution on your own; but let's walk slowly through the following anyway. We start off with the two obvious declarations shown above:

typedefint(*LPFN_QSortCCallback)(constvoid*a,constvoid*b);typedefint(ClassWithCallback::*LPFN_QSortMemberFunctionCallback)(constvoid*a,constvoid*b);

We need a class that holds the state of the C++ member function callback. It will do a couple of things in addition to this:

  • The class will represent a "slot" that can be allocated for use by a member function callback
  • The class gets a unique C callback for this "slot".
  • Slots can be "reserved" and "freed"

This is the code:

// this object holds the state for a C++ member function callback in memoryclassQsortCallbackBase{public:// input: pointer to a unique C callback. QsortCallbackBase(LPFN_QSortCCallbackpCCallback):m_pClass(NULL),m_pMethod(NULL),m_pCCallback(pCCallback){}// when done, remove allocation of the callbackvoidFree(){m_pClass=NULL;// not clearing m_pMethod: it won't be used, since m_pClass is NULL and so this entry is marked as free}// when free, allocate this callbackLPFN_QSortCCallbackReserve(ClassWithCallback*instance,LPFN_QSortMemberFunctionCallbackmethod){if(m_pClass)returnNULL;m_pClass=instance;m_pMethod=method;returnm_pCCallback;}protected:staticintStaticInvoke(intcontext,constvoid*a,constvoid*b);private:LPFN_QSortCCallbackm_pCCallback;ClassWithCallback*m_pClass;LPFN_QSortMemberFunctionCallbackm_pMethod;};

Next, we come up with a slight modification of our template shown above.

template<intcontext>classDynamicQSortCallback:publicQsortCallbackBase{public:DynamicQSortCallback():QsortCallbackBase(&DynamicQSortCallback<context>::GeneratedStaticFunction){}private:staticintGeneratedStaticFunction(constvoid*a,constvoid*b){returnStaticInvoke(context,a,b);}};

There are two important changes here:

  1. The class is derived from , so it actually can be accessed like a slot.
  2. The constructor passes a pointer to the unique C function to the base data.

Now as for implementing the actual callback: let's first look at the interface definition for it:

classQSortMemberFunctionCallback{public:QSortMemberFunctionCallback(ClassWithCallback*instance,LPFN_QSortMemberFunctionCallbackmethod);~QSortMemberFunctionCallback();public:operatorLPFN_QSortCCallback()const{returnm_cbCallback;}boolIsValid()const{returnm_cbCallback!=NULL;}private:LPFN_QSortCCallbackm_cbCallback;intm_nAllocIndex;private:QSortMemberFunctionCallback(constQSortMemberFunctionCallback&os);QSortMemberFunctionCallback&operator=(constQSortMemberFunctionCallback&os);};

Essentially, this is a simple decorator class: the constructor maps the input - a C++ class pointer and a C++ member function pointer - and identifies the unique C callback function for it. The rest of the class is just "mechanics": being able to call the function, checking if the mapping was successful.

Next, we create an array of up to 16 slots. That means, up to 16 functions can be using this technique either in parallel or recursive. Obviously, the number of slots is an implementation issue: you can easily use macros to provide space for much more allocations.

staticQsortCallbackBase*AvailableCallbackSlots[]={newDynamicQSortCallback<0x00>(),newDynamicQSortCallback<0x01>(),newDynamicQSortCallback<0x02>(),newDynamicQSortCallback<0x03>(),newDynamicQSortCallback<0x04>(),newDynamicQSortCallback<0x05>(),newDynamicQSortCallback<0x06>(),newDynamicQSortCallback<0x07>(),newDynamicQSortCallback<0x08>(),newDynamicQSortCallback<0x09>(),newDynamicQSortCallback<0x0A>(),newDynamicQSortCallback<0x0B>(),newDynamicQSortCallback<0x0C>(),newDynamicQSortCallback<0x0D>(),newDynamicQSortCallback<0x0E>(),newDynamicQSortCallback<0x0F>(),};

Now we begin putting it all together. The used by our slot is simple: it has a context, it simply looks up the data for that context and invokes it:

intQsortCallbackBase::StaticInvoke(intcontext,constvoid*a,constvoid*b){return((AvailableCallbackSlots[context]->m_pClass)->*(AvailableCallbackSlots[context]->m_pMethod))(a,b);}

The decorator class uses a standard RAII pattern to allocate / free the slot:

QSortMemberFunctionCallback::QSortMemberFunctionCallback(ClassWithCallback*instance,LPFN_QSortMemberFunctionCallbackmethod){intimax=sizeof(AvailableCallbackSlots)/sizeof(AvailableCallbackSlots[0]);for(m_nAllocIndex=0;m_nAllocIndex<imax;++m_nAllocIndex){m_cbCallback=AvailableCallbackSlots[m_nAllocIndex]->Reserve(instance,method);if(m_cbCallback!=NULL)break;}}QSortMemberFunctionCallback::~QSortMemberFunctionCallback(){if(IsValid()){AvailableCallbackSlots[m_nAllocIndex]->Free();}}

And that's it! Download the sourcecode + VS2010 project + binaries here.

Sorry for the double-answer, but seeing the original post makes it a lot more clear what you're looking for, and it's a related but kind of different use of function pointers. What you have there is a callback.

Let's say you want a function to download a file from the Internet. So you write:

Now, that's great, right up until you try to download a very large file. Suddenly is taking a long time to finish, and you don't know how long it'll be. Boy, things would be a lot easier if you only had a progress indicator of some sort to let you know how far along you are! But can't return until it's finished, and you don't want to teach itself about your progress indicator, because that would lead to high coupling and make it harder to reuse in another context.

What you can do instead is give it a callback, a function pointer that uses to call you back after you call it. In fact, you'd probably want two:

(Where is a function pointer that takes one int as an argument.) It would call once to set the size of the file, and repeatedly to inform you of how far along it is in the download. With these two pieces of information available, you can wire up a progress bar or some other type of progress indicator, without having to couple directly to the progress bar control.

That's the sort of power that callbacks make available. You see them a lot in code that deals with long-running events. Downloads, for example, or code for playing sound and music. In a music callback, you can perform custom edits to the sound as it's being played to add effects, or read the sound buffer and display a representation of it on an equalizer.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *