Solved

How to cast a function pointer in C++?

Posted on 2008-10-03
14
1,492 Views
Last Modified: 2013-12-14
I have a struct that contains as one of its members a function pointer.  The message pump has an array of these structs and loops through it each time a message is received in a TCP port, executing the handler function if there's a listener for it.  It looks like this:

struct MessageQueue
{
     unsigned long msgID;
     char *rawRecordData;
     void (OAtm::*handler)();
};

Problem is, OAtm is a base class from which different vendor classes are derived.  So, when one of the derived classes registers one of its listeners, how do I cast it from a void(OAtm::*)(void) pointer to a void(Derived::*)(void)?
0
Comment
Question by:cuziyq
  • 4
  • 4
  • 3
  • +2
14 Comments
 
LVL 9

Expert Comment

by:jhshukla
ID: 22638644
Why do you need to do that? I mean the MessageQueue is expecting a void(OAtm::*)(void) pointer. Did you want to cast the other way. from derived to base?
0
 
LVL 9

Expert Comment

by:jhshukla
ID: 22638665
Oh I see now. you already have a function pointer but depending on the object type call one of the derived class functions. I would recommend using virtual functions. If you have control over OAtm source, you could write the handler function and call virtual function from within that handler. e.g.

class OAtm {
public:
    virtual void handlerFunc() = 0;
    inline void handlerWrapper() { handlerFunc(); }
};

class Derived {
public:
    void handlerFunc() { do something }
};

You could try passing the handlerFunc directly but I am not very sure how reliable it would be.
0
 
LVL 14

Author Comment

by:cuziyq
ID: 22638812
The problem is that the base class won't know the names of the functions that will eventually need to be overridden.

Basically, what this application does is receives raw data from various vendors.  All they need to do is send data to a TCP port that we listen on.  They provide specifications as to where the field boundaries are and what data they will contain.

The base class simply contains a raw record holder and functions to add this information into a database.  Each derived class has whatever functions it needs to crack the data into its constituent parts so that it can be passed to the database in a standardized form, echoed back to the sender, or forwarded on to a 3rd party.

Here's the kicker:
Some vendors will have dozens of messages that we'll need to process, and others will only have one or two, depending on what that vendor's line of business is.  Therefore, each derived class may need to have a dozen handler functions, or it may only need to have one or two.  With the myriad of different types of data we're handling, it's not feasible to have a virtual function in the base simply for the sake of being able to override it in a derived class.

So here was my thinking:
When the derived class wants to register one of its handler functions, it casts it as an (OAtm::*)() instead of a void(Derived::*)().

I know that casting a derived class to a base class pointer is generally bad, but since each derived class is responsible for registering its own messages, there will never be a situation where the service will be trying to follow a pointer to a function that doesn't exist.

Sorry for the long description.  I figure a background on what I am trying to do would help out a bit :-)
0
 
LVL 2

Expert Comment

by:gravit9
ID: 22639581
It seems that what you are trying to do can not be done the way you want it to. You will not be able to access the derived class data once you cast to the base class, unless you cast it back to the derived. What you can do is modify the struc to:

struct MessageQueue
{
     unsigned long msgID;
     char *rawRecordData;
     OAtm* vendor;
};

the derived class will by somehting like
class deriv : public OAtm
{
 // data
  void handle();
};

and then when you call the handler use a macro to typecast the base class from the pointer 'vendor' to derived and then call the handler.
0
 
LVL 9

Expert Comment

by:jhshukla
ID: 22639643
I should have asked this earlier: how is the member function called? I mean there has to be an object on which the function is invoked; where and how is that object obtained?
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 22640135
Consider using a functiooid instead of a simple function pointer :

        http://www.parashift.com/c++-faq-lite/pointers-to-members.html#faq-33.10

It allows for a lot more flexibility, and you won't have to cast anything.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 22650117
>>>> I would recommend using virtual functions.
The concept of message maps is taken from MFC where by means of a wizard you could add new handler functions for Window messages to your class. The wizard adds a new entry to a macro controled table in the cpp file of the class. Each entry of the table contains the message id(s) of the message to handle and a function pointer to a - wizard generated - member function of the class. By means of macros the function pointers were casted from a prototype defined in the baseclass of all of these classes to the 'override' function of the derived class.

The functionality of these message maps is comparable with that of virtual functions. At runtime the dispatch function of the messages finds out which class(es) was/were associated to handle a message and calls a virtual function which iterates the message maps in order to find an appropriate handler. The concept is so mighty that it even can call multiple handler functions in a class hierarchy.

The advantage over virtual functions is that there is no baseclass function needed beside of the prototyp of all handler functions and that the member functions can be named individually. Each type of handler function has its specific argument signature which was supplied by the caller and made available in the message map by an appropriate cast.

The disadvantage is the ugly casts needed. And of course you need to store the pointer of the target object with the window that dispatches the message. In MFC that is done by SetWindowLong.

0
Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 22650186
>>>> How to cast a function pointer in C++?

You may look at the file afxmsg_.h in the atlmfc\include subfolder (mfc\include in VC6) where you can see the different macros for casting.
0
 
LVL 14

Author Comment

by:cuziyq
ID: 22654636
Yes, yes, MFC message maps are the exact model I would like to try and follow here!  I've looked at afxwin.h, but the #define macros are buried so deep it's hard to understand what's going where.  Could you demystify it for me?

When you do your BEGIN_MESSAGE_MAP(), the macro inserts some code into the file that initializes a struct with values exactly the way I want to do here.  But what I don't understand is the AFX_PMSG member of the AFX_MSGMAP struct simply translates to CCmdTarget::*.  CCmdTarget is a base class for all of MFC, and does not contain the stuff needed for, say, a CDialog.  So how does an object derived from CDialog place a function pointer in a CCmdTarget*?  My BODQ object derives from OAtm, but the struct will not accept a BODQ::* pointer (it's defined in the struct as (OAtm::*handler)(), and the compiler gives me the error "cannot convert from 'void (__thiscall BODQ::* )(void)' to 'void (__thiscall OAtm::* )(void)'.

It seems like I am doing the same kind of thing MFC is, but MFC is waaay more compilcated, so it may be obfuscating the fact that the pointer types don't match somehow.
0
 
LVL 14

Author Comment

by:cuziyq
ID: 22654654
I've thought about the whole functionoid idea, but it just seems convoluted to me to have a bazillion objects lying around for each of the functions that need to be executed.  Maybe I'm just not fully understanding the concept.
0
 
LVL 53

Expert Comment

by:Infinity08
ID: 22654669
You just have to look at it as a safer version of function pointers that also allows for a lot more than is possible with simple function pointers.
0
 
LVL 39

Accepted Solution

by:
itsmeandnobodyelse earned 500 total points
ID: 22654938
>>>> So how does an object derived from CDialog place a function pointer in a CCmdTarget*?  

The message map is an array of a struct which expects a pointer to a member function 'void CCmdTarget::afxHandler(void)'.  To be able to insert a function with prototype 'long MyDialog::myHandler(AnyType* p1, UINT n)' you simply need a cast. That works cause the dispatch function actually would do the reverse cast and actually calls a function UnknownClass::yourHandler(AnyType* p1, UINT n) where the pointer for the 'UnknownClass' instance was stored with the window handle. Hence, the dispatcher has a valid object (more accurate a valid pointer to a object) and a known function prototype. These components were all to actually call the member function which function pointer was stored in the message map.

>>>> the compiler gives me the error "cannot convert from 'void (__thiscall BODQ::* )(void)' to 'void (__thiscall OAtm::* )(void)'.
Yes, that is the disadvantage I told of. These casts were very tricky as you need to have a chain of casts rather than one single cast to make things working.

Look at the ON_NOTIFY macro which translates to

#define ON_NOTIFY(wNotifyCode, id, memberFxn) \
      { WM_NOTIFY, (WORD)(int)wNotifyCode, (WORD)id, (WORD)id, AfxSigNotify_v, \
            (AFX_PMSG) \
            (static_cast< void (AFX_MSG_CALL CCmdTarget::*)(NMHDR*, LRESULT*) > \
            (memberFxn)) },

The interesting parts begin with

    (AFX_PMSG)  


what actually is a C cast where the AFX_PMSG is a function pointer defined as


    typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);


i. e. it is a member function of CCmdTarget with void return and void (no) argument. The (AFX_PMSG) is the final cast to satisfy the type which was required by the struct which represents one entry of a MFC message map.

The 'inner' cast is a static_cast which now has the needed arguments but still is a member of CCmdTarget:

      static_cast< void (AFX_MSG_CALL CCmdTarget::*)(NMHDR*, LRESULT*) >

Ignore the AFX_MSG_CALL which is only a specifier whether the arguments are put from left to right or right to left into the stack. You see, the return vallue of the function prototype still is void, but now the function has two arguments NMHDR* and LRESULT*. If you would generate a handler for notifying - say of a list control - you exactly would have the same arguments, a NMHDR* and LRESULT*. So, the only thing the cast was casting is that it turns a CYourDialog object to its baseclass object what is a CCmdTarget. The main principle is: you can't make a cast which both changes the argument list *and* the object (class) of a member function. But you can make a cascade of cast where the inner cast changes the derived class to (one of) its base class(es) and the out cast turns the argument list to void.

 

   
0
 
LVL 14

Author Comment

by:cuziyq
ID: 22661697
OK, I studied afxwin.h carefully, and reread your last post about a dozen times.  I don't need a chain of casts the way MFC does because the functions themselves all take the same argument list.  That just leaves the one cast from (BODQ::*)() to (OAtm::*)().

As it turns out, I was only missing one thing -- a typedef.

typedef void(OAtm::*CAST)();

Simply adding that one line, and then doing (CAST)void(BODQ::*handlerFxn)() makes it work the way I had it before I even posted this question.  Previously, I was trying something ridiculously convoluted like (void(OAtm::*)())void(BODQ::*handler)(), which obviously doesn't work.

So now, I guess the only question left is why does typedefing the cast work, but you can't do it inline?  Not really a big deal since I am happy I solved my problem.  But I am still curious.  Are there too many parenthesis to confuse the compiler or something?
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 22663027
>>>> why does typedefing the cast work, but you can't do it inline?

Generally, a typedef can only help, i. e. you should be able to make the cast without typedef as well.

But, I must admit that I never casted function pointers not using typedef's. I think the syntax of function pointers - exaggerated by class member function pointers - is weird and badly thought out by a *technician*. So, unfortunately, I can't help you with your final question ... or better said, it makes too much efforts to dive into that issue ;-)
0

Featured Post

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

Suggested Solutions

Article by: SunnyDark
This article's goal is to present you with an easy to use XML wrapper for C++ and also present some interesting techniques that you might use with MS C++. The reason I built this class is to ease the pain of using XML files with C++, since there is…
This article shows you how to optimize memory allocations in C++ using placement new. Applicable especially to usecases dealing with creation of large number of objects. A brief on problem: Lets take example problem for simplicity: - I have a G…
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now