[33] Pointers to member functions
(Part of C++ FAQ Lite, Copyright © 1991-2003, Marshall Cline, cline@parashift.com)


FAQs in section [33]:


[33.1] Is the type of "pointer-to-member-function" different from "pointer-to-function"?

Yep.

Consider the following function:

 int f(char a, float b);

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 was an ordinary function: "int (*)(char,float)".

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[33.2] How do I pass a pointer-to-member-function to a signal handler, X event callback, system call that starts a thread/task, etc?

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 void*, so you can pass the object pointer in the void*. Many real time operating systems do something similar for the function that starts a new task. Worst case you could store the object pointer in a global variable; this might be required for Unix signal handlers (but globals are, in general, undesired). In any case, the top-level function would call the desired member function on the object.

Here's an example of the worst case (using a global). Suppose you want to call Fred::memberFn() on interrupt:

 class Fred {
 public:
   void memberFn();
   static void staticMemberFn();  
// A static member function can handle it
   
...
 };
 
 
// Wrapper function uses a global to remember the object:
 Fred* object_which_will_handle_signal;
 
 void Fred_memberFn_wrapper()
 {
   object_which_will_handle_signal->memberFn();
 }
 
 int main()
 {
   
/* signal(SIGINT, Fred::memberFn); */   // Can NOT do this
   signal(SIGINT, Fred_memberFn_wrapper);  
// OK
   signal(SIGINT, Fred::staticMemberFn);   
// Also OK
   
...
 }

Note: static member functions do not require an actual object to be invoked, so pointers-to-static-member-functions are type compatible with regular pointers-to-functions.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[33.3] Why do I keep getting compile errors (type mismatch) when I try to use a member function as an interrupt service routine?

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.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[33.4] Why am I having trouble taking the address of a C++ function?

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.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[33.5] How can I avoid syntax errors when calling a member function using a pointer-to-member-function?

Do both a typedef and a #define macro.

Step 1: create a typedef:

 class Fred {
 public:
   int f(char x, float y);
   int g(char x, float y);
   int h(char x, float y);
   int i(char x, float y);
   
...
 };
 
 
// FredMemFn points to a member of Fred that takes (char,float)
 typedef  int (Fred::*FredMemFn)(char x, float y);

Step 2: create a #define macro:

 #define CALL_MEMBER_FN(object,ptrToMember)  ((object).*(ptrToMember))

(Normally I dislike #define macros, but you should use them with pointers to members because they improve the readability and writability of that sort of code.)

Here's how you use these features:

 void userCode(Fred& fred, FredMemFn memFn)
 {
   CALL_MEMBER_FN(fred,memFn)('x', 3.14);
   
// Would normally be: (fred.*memFn)('x', 3.14);
 }

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: #define macros are evil in 4 different ways: evil#1, evil#2, evil#3, and evil#4. But they're still useful sometimes. But you should still feel a vague sense of shame after using them.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[33.6] How do I create and use an array of pointer-to-member-function?

Use both the typedef and the #define macro described earlier, and you're 90% done.

Step 1: create a typedef:

 class Fred {
 public:
   int f(char x, float y);
   int g(char x, float y);
   int h(char x, float y);
   int i(char x, float y);
   
...
 };
 
 
// FredMemFn points to a member of Fred that takes (char,float)
 typedef  int (Fred::*FredMemFn)(char x, float y);

Step 2: create a #define macro:

 #define CALL_MEMBER_FN(object,ptrToMember)  ((object).*(ptrToMember))

Now your array of pointers-to-member-functions is straightforward:

 FredMemFn a[] = { &Fred::f, &Fred::g, &Fred::h, &Fred::i };

And your usage of one of the member function pointers is also straightforward:

 void userCode(Fred& fred, int memFnNum)
 {
   
// Assume memFnNum is between 0 and 3 inclusive:
   CALL_MEMBER_FN(fred, a[memFnNum]) ('x', 3.14);
 }

Note: #define macros are evil in 4 different ways: evil#1, evil#2, evil#3, and evil#4. But they're still useful sometimes. Feel ashamed, feel guilty, but when an evil construct like a macro improves your software, use it.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[33.7] Can I convert a pointer-to-member-function to a void*?

No!

 class Fred {
 public:
   int f(char x, float y);
   int g(char x, float y);
   int h(char x, float y);
   int i(char x, float y);
   
...
 };
 
 
// FredMemFn points to a member of Fred that takes (char,float)
 typedef  int (Fred::*FredMemFn)(char x, float y);
 
 #define CALL_MEMBER_FN(object,ptrToMember)  ((object).*(ptrToMember))
 
 int callit(Fred& o, FredMemFn p, char x, float y)
 {
   return CALL_MEMBER_FN(o,p)(x, y);
 }
 
 int main()
 {
   FredMemFn p = &Fred::f;
   void* p2 = (void*)p;                  
//  illegal!!
   Fred o;
   callit(o, p, 'x', 3.14f);             
// okay
   callit(o, FredMemFn(p2), 'x', 3.14f); 
// might fail!!
   
...
 }

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.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[33.8] Can I convert a pointer-to-function to a void*?

No!

 int f(char x, float y);
 int g(char x, float y);
 
 typedef int(*FunctPtr)(char,float);
 
 int callit(FunctPtr p, char x, float y)
 {
   return p(x, y);
 }
 
 int main()
 {
   FunctPtr p = f;
   void* p2 = (void*)p;              
//  illegal!!
   callit(p, 'x', 3.14f);            
// okay
   callit(FunctPtr(p2), 'x', 3.14f); 
// might fail!!
   
...
 }

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.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


E-Mail E-mail the author
C++ FAQ LiteTable of contentsSubject indexAbout the author©Download your own copy ]
Revised May 2, 2003