Yep.
Consider the following function:
The type of this function is different depending on whether it is an ordinary function or a non-static member function of some class:
Note: if it's a static member function of class Fred, its type is the
same as if it were an ordinary function: "
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
Don't.
Because a member function is meaningless without an object to invoke it on, you can't do this directly (if The X Window System was rewritten in C++, it would probably pass references to objects around, not just pointers to functions; naturally the objects would embody the required function and probably a whole lot more).
As a patch for existing software, use a top-level (non-member) function as a
wrapper which takes an object obtained through some other technique.
Depending on the routine you're calling, this "other technique" might be
trivial or might require a little work on your part. The system call that
starts a thread, for example, might require you to pass a function pointer
along with a
Here's an example of the worst case (using a global). Suppose you want to
call
Note: static member functions do not require an actual object to be
invoked, so pointers-to-static-member-functions are
usually type-compatible with regular pointers-to-functions. However,
although it probably works on most compilers, it actually would have to be an
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
This is a special case of the previous two questions, therefore read the previous two answers first.
Non-static member functions have a hidden parameter that corresponds to the this pointer. The this pointer points to the instance data for the object. The interrupt hardware/firmware in the system is not capable of providing the this pointer argument. You must use "normal" functions (non class members) or static member functions as interrupt service routines.
One possible solution is to use a static member as the interrupt service routine and have that function look somewhere to find the instance/member pair that should be called on interrupt. Thus the effect is that a member function is invoked on an interrupt, but for technical reasons you need to call an intermediate function first.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
Short answer: if you're trying to store it into (or pass it as) a pointer-to-function, then that's the problem this is a corollary to the previous FAQ.
Long answer: In C++, member functions have an implicit parameter which points to the object (the this pointer inside the member function). Normal C functions can be thought of as having a different calling convention from member functions, so the types of their pointers (pointer-to-member-function vs. pointer-to-function) are different and incompatible. C++ introduces a new type of pointer, called a pointer-to-member, which can be invoked only by providing an object.
NOTE: do not attempt to "cast" a pointer-to-member-function into a pointer-to-function; the result is undefined and probably disastrous. E.g., a pointer-to-member-function is not required to contain the machine address of the appropriate function. As was said in the last example, if you have a pointer to a regular C function, use either a top-level (non-member) function, or a static (class) member function.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
Do both a typedef and a
Step 1: create a typedef:
Step 2: create a
(Normally I dislike
Here's how you use these features:
I strongly recommend these features. In the real world, member function invocations are a lot more complex than the simple example just given, and the difference in readability and writability is significant. comp.lang.c++ has had to endure hundreds and hundreds of postings from confused programmers who couldn't quite get the syntax right. Almost all these errors would have vanished had they used these features.
Note:
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
Use both the typedef and the
Step 1: create a typedef:
Step 2: create a
Now your array of pointers-to-member-functions is straightforward:
And your usage of one of the member function pointers is also straightforward:
Note:
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
No!
Please do not email me if the above seems to work on your particular version of your particular compiler on your particular operating system. I don't care. It's illegal, period.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
No!
Please do not email me if the above seems to work on your particular version of your particular compiler on your particular operating system. I don't care. It's illegal, period.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
Use a functionoid.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
Functionoids are functions on steroids. Functionoids are strictly more powerful than functions, and that extra power solves some (not all) of the challenges typically faced when you use function-pointers.
Let's work an example showing a traditional use of function-pointers, then we'll translate that example into functionoids. The traditional function-pointer idea is to have a bunch of compatible functions:
Then you access those by function-pointers:
Sometimes people create an array of these function-pointers:
In which case they call the function by accessing the array:
With functionoids, you first create a base class with a pure-virtual method:
Then instead of three functions, you create three derived classes:
Then instead of passing a function-pointer, you pass a
You can create an array of them in almost the same way:
This gives us the first hint about where functionoids are strictly more powerful than function-pointers: the fact that the functionoid approach has arguments you can pass to the ctors (shown above as ...ctor-args...) whereas the function-pointers version does not. Think of a functionoid object as a freeze-dried function-call (emphasis on the word call). Unlike a pointer to a function, a functionoid is (conceptually) a pointer to a partially called function. Imagine for the moment a technology that lets you pass some-but-not-all arguments to a function, then lets you freeze-dry that (partially completed) call. Pretend that technology gives you back some sort of magic pointer to that freeze-dried partially-completed function-call. Then later you pass the remaining args using that pointer, and the system magically takes your original args (that were freeze-dried), combines them with any local variables that the function calculated prior to being freeze-dried, combines all that with the newly passed args, and continues the function's execution where it left off when it was freeze-dried. That might sound like science fiction, but it's conceptually what functionoids let you do. Plus they let you repeatedly "complete" that freeze-dried function-call with various different "remaining parameters," as often as you like. Plus they allow (not require) you to change the freeze-dried state when it gets called, meaning functionoids can remember information from one call to the next.
Okay, let's get our feet back on the ground and we'll work a couple of examples to explain what all that mumbo jumbo really means.
Suppose the original functions (in the old-fashioned function-pointer style) took slightly different parameters.
When the parameters are different, the old-fashioned function-pointers approach is difficult to use, since the caller doesn't know which parameters to pass (the caller merely has a pointer to the function, not the function's name or, when the parameters are different, the number and types of its parameters) (do not write me an email about this; yes you can do it, but you have to stand on your head and do messy things; but do not write me about it use functionoids instead).
With functionoids, the situation is, at least sometimes, much better. Since a
functionoid can be thought of as a freeze-dried function call, just
take the un-common args, such as the ones I've called y and/or
z, and make them args to the corresponding ctors. You may also pass
the common args (in this case the int called x) to the ctor,
but you don't have to you have the option of passing it/them to the pure
virtual
Then instead of three functions, you create three derived classes:
Now you see that the ctor's parameters get freeze-dried into the functionoid when you create the array of functionoids:
So when the user invokes the
As I've already hinted, one of the benefits of functionoids is that you can
have several instances of, say, Funct1 in your array, and those
instances can have different parameters freeze-dried into them. For example,
Another benefit of functionoids is apparent if we change the example from an
array of functionoids to a local functionoid. To set the stage, let's go back
to the old-fashioned function-pointer approach, and imagine that you're trying
to pass a comparison-function to a
Then different callers would pass different function-pointers depending on what they thought was best:
We can easily translate this example into one using functionoids:
Given this example as a backdrop, we can see two benefits of functionoids over
function-pointers. The "ctor args" benefit described above, plus the fact
that functionoids can maintain state between calls in a thread-safe
manner. With plain function-pointers, people normally maintain state
between calls via static data. However static data is not intrinsically
thread-safe static data is shared between all threads. The functionoid
approach provides you with something that is intrinsically thread-safe
since the code ends up with thread-local data. The implementation is trivial:
change the old-fashioned static datum to an instance data member inside the
functionoid's this object, and poof, the data is not only
thread-local, but it is even safe with recursive calls: each call to
Note that we've gained something without losing anything. If you want thread-global data, functionoids can give you that too: just change it from an instance data member inside the functionoid's this object to a static data member within the functionoid's class, or even to a local-scope static data. You'd be no better off than with function-pointers, but you wouldn't be worse off either.
The functionoid approach gives you a third option which is not available with the old-fashioned approach: the functionoid lets callers decide whether they want thread-local or thread-global data. They'd be responsible to use locks in cases where they wanted thread-global data, but at least they'd have the choice. It's easy:
Functionoids don't solve every problem encountered when making flexible software, but they are strictly more powerful than function-pointers and they are worth at least evaluating. In fact you can easily prove that functionoids don't lose any power over function-pointers, since you can imagine that the old-fashioned approach of function-pointers is equivalent to having a global(!) functionoid object. Since you can always make a global functionoid object, you haven't lost any ground. QED.
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
Yes.
If you have a small functionoid, and in the real world that's rather common, the cost of the function-call can be high compared to the cost of the work done by the functionoid. In the previous FAQ, functionoids were implemented using virtual functions and will typically cost you a function-call. An alternate approach uses templates.
The following example is similar in spirit to the one in
the previous FAQ. I have renamed
The difference between this approach and the one in the previous FAQ is that the fuctionoid gets "bound" to the caller at compile-time rather than at run-time. Think of it as passing in a parameter: if you know at compile-time the kind of functionoid you ultimately want to pass in, then you can use the above technique, and you can, at least in typical cases, get a speed benefit from having the compiler inline-expand the functionoid code within the caller. Here is an example:
When the compiler compiles the above, it might inline-expand the call which might improve performance.
Here is one way to call the above:
Aside: as was hinted at in the first paragraph above, you may also pass in the names of normal functions (though you might incur the cost of the function call when the caller uses these):
[ Top | Bottom | Previous section | Next section | Search the FAQ ]
E-mail the author
[ C++ FAQ Lite
| Table of contents
| Subject index
| About the author
| ©
| Download your own copy ]
Revised Mar 1, 2006