Solved

Function ptr -> void * -> Function ptr

Posted on 2001-08-30
13
563 Views
Last Modified: 2008-02-01
I have this code, which does not compile.

  { void (CTestaesr23Dlg::*pfnwSC)(void);
    m_Map.SetAt ("func", (void *) &pfnwSC);
  }

  //void (CTestaesr23Dlg::*pfn) (void) = &CTestaesr23Dlg::func;
  //(this->*pfn)();

  { void (CTestaesr23Dlg::*pfn) (void);
    void *apfn;
    m_Map.Lookup ("func", apfn);
    pfn = (void (CTestaesr23Dlg::*)(void)) apfn;
    (this->*pfn)();
  }

Essentially, I want to make a map of strings to member function pointers.  That is, given a string, I want a pointer to a class member function.  I am using the MFC class CMapStringToPtr because I think this is what's necessary.

Unfortunately, I can't seem to figure out how to cast a void * pointer back to a void (CTestaesr23Dlg::*)(void) pointer.  Is this how I approach the problem?  What am I doing wrong?

Thanks for any help.
0
Comment
Question by:helpmealot
  • 3
  • 3
  • 3
  • +3
13 Comments
 
LVL 6

Expert Comment

by:Triskelion
ID: 6443170
How many functions do you have?
If it's only a few, you may be able to use just a static struct with both the strings and the pointers to functions.
0
 
LVL 9

Expert Comment

by:BeyondWu
ID: 6443418
You can't use
pfn = (void (CTestaesr23Dlg::*)(void)) apfn;
because it protected by compiler.
but you can cheat it with this:
memcpy((void*)&pfn, apfn, sizeof(apfn));
0
 
LVL 49

Accepted Solution

by:
DanRollins earned 50 total points
ID: 6443713
I'm glad you asked this question.  I had to brush off an old technqiue I used in a mini-language interpretter I wrote some time ago.  The problem is that you can't call member fn, except static member fns, through a pointer. See
Old Style (K&R) Declarations Are Not Supported in C++ (Q79845)
http://support.microsoft.com/support/kb/articles/Q79/8/45.ASP
and
INFO: Creating a Function Pointer to a C++ Member Function (Q94579)
http://support.microsoft.com/support/kb/articles/Q94/5/79.ASP

static member fns are a pain in the butt because they can't access members.  Almost useless.

But we all know that MFC routes messages to class member fns fns in the MESSAGE_MAP table, so there must be a way...  There is a workaround that looks awkward at first but it quite useable.

in the header
=-=-=--=-=-=-=-=-=-=-=-=-=-
class CMyDlg : public CDialog
{
public:
    CD6Dlg(CWnd* pParent = NULL); // std constructor
    static CD6Dlg* myThis;

    static void sDoSomething() {myThis->DoSomething();} ; // stub
    void DoSomething(); // the actual member fn
    static void sStopIt() {myThis->StopIt();} ; // stub
    void StopIt(); // the actual member fn
...

in the cpp file
=-=-=--=-=-=-=-=-=-=-=-=-=-
CD6Dlg* CD6Dlg::myThis= 0; // static var; must be instantiated
//----------------------------------------- ctor
CMyDlg::CMyDlg(CWnd* pParent /*=NULL*/)
     : CDialog(CD6Dlg::IDD, pParent)
{
    ...
    myThis= this;  // important: do this in the ctor
}

//-----------------------------------
// Now you can write your fns normally and they can access
// member vars and fns
void CD6Dlg::DoSomething()
{
    MessageBox("In Do Something!");
}
void CD6Dlg::StopIt()
{
    MessageBox("In Stop It!");
}

=-=-=-=-=-=-=-=-=-=-=-
Given this layout, you can use...

typedef void (*VFuncPtr_v)(void);
VFuncPtr_v pfn;
....
pfn= sDoSomething; // the static version
pfn(); //will call member fn DoSomething;


Now for the second part... using CMapStringToPtr and calling the fn...

void CD6Dlg::OnButton1()
{
    CMapStringToPtr m_map; // really belongs in header, of course

    //---------- you will probably init the map in the ctor
    m_map["DOSOMETHING"] = sDoSomething;
    m_map["STOPIT"]      = sStopIt;

    VFuncPtr_v pfn= (VFuncPtr_v)m_map["DOSOMETHING"];
    pfn(); // excute this->DoSomething()

    pfn= (VFuncPtr_v)m_map["STOPIT"];
    pfn();  // excute this->StopIt()

    // or just for fun...
    ((VFuncPtr_v)m_map["STOPIT"])(); // this->StopIt()
}

=-=-=-=-=-=-=-=-=-=-=-
Now, here is one little bonus that I used in my implementation.  It is a pain to need to define all of these duplicate fns, so I wriote  a macro...

#define DECLARE_VOIDFNPTR(fn) \
static void s##fn () {myThis-> fn ();} ; \
void fn ();

class CD6Dlg : public CDialog
{
...
    DECLARE_VOIDFNPTR( DoSomething );
    DECLARE_VOIDFNPTR( StopIt );

=-=-=-=-=-=-=-=-=-=-=-
It is also possible to do the same thing that MFC does vis-a-vis creating a table automatically (so you dont need to do all of the...  
   m_map["WHATEVER"] = sWhatever;
lines.  But I'll leave that as an excercise to the student.

=-=-=-=-=-=-=-
Finally, I'm quite certain that some of this can be done in a more typesafe way by using templates and CTypedPtrMap, but I think you are getting your 50 pts worth already, so I won't go into it.

-- Dan
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 6443721
One important point.  When using the "myThis" technique, you need to remember that the object (CMyDlg) must be a singleton.  If you instantiate another copy of the object, the static myThis will be wrong.

-- Dan
0
 
LVL 2

Expert Comment

by:smitty1276
ID: 6443918
I had that problem before... I don't remember how well it worked, but I think I used a static member function and a static pointer to an instance of the class, which I would set before calling the function... I think.  It was a while back.

class myClass
{
  int myData;
 
  //static member func and static pointer
  static void staticFunc( void );
  static MyClass *currentInstance;
}

static void myClass::staticFunc( void )
{
  cout << currentInstance->myData << endl;
}

You can then use it like this...
Supposing you want to call the staticFunc() function of the instance inst...

myClass inst;

//set pointer to desired instance
MyClass::currentInstance = &inst;

//call the member function (or a pointer to it, or whatever)
MyClass::staticFunc();


0
 
LVL 2

Expert Comment

by:smitty1276
ID: 6443921
It looks like DanRollins already said essentially the same thing I did... sorry.
0
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
LVL 31

Expert Comment

by:Zoppo
ID: 6444440
BTW, DanRollins, I don't agree with this:
>The problem is that you can't call member fn, except static
> member fns, through a pointer.

You can call even a none-static member function against an
object through a pointer exactly as helpmealot does it:
(this->*pfn)();
0
 

Author Comment

by:helpmealot
ID: 6445706
I think I have found a solution that seems to be most straightforward.

Rather than use the CMapStringToPtr class, I've switched to the CMap class, which uses templates.

I now have the following code, which does compile and works perfectly:

-------

void CTestaesr23Dlg::OnButton1()
{ CMap<CString, LPCSTR, void (CTestaesr23Dlg::*) (void), void (CTestaesr23Dlg::*) (void)> m_Map;

  { m_Map.SetAt ("func", func);
    m_Map.SetAt ("func2", func2);
  }

  { void (CTestaesr23Dlg::*pfn) (void);
    m_Map.Lookup ("func", pfn);
    (this->*pfn)();

    void (CTestaesr23Dlg::*pfn2) (void);
    m_Map.Lookup ("func2", pfn2);
    (this->*pfn2)();
  }
}

------------------

func and func2 are both executed.
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 6446244
>> BTW, DanRollins, I don't agree with this:
>> >The problem is that you can't call member fn, except static
>> > member fns, through a pointer

You are right.  This works:

typedef void (CMyDlg::*VFuncPtrToMember_v)(void);

void CMyDlg::OnButton1()
{
    VFuncPtrToMember_v  pfnNonStatic= DoSomething; // non-static
    (this->*pfnNonStatic)();
}

Thanks Zoppo, I have learned something new today and am a better man because of it!
-==-=-=-=--==-=-=-

helpmealot,
It looks like you solved this one yourself.  I have one tip, though.  If you use a typedef, the code is quite a bit cleaner:

typedef void (CMyDlg::*VFnPtr_v)(void);
void CMyDlg::OnButton1()
{
    CMap<CString, LPCSTR, VFnPtr_v, VFnPtr_v> m_Map;
    m_Map.SetAt ("func", DoSomething);
    m_Map.SetAt ("func2", StopIt);

    VFnPtr_v pfn;
    m_Map.Lookup ("func", pfn);
    (this->*pfn)();
}

-- Dan
0
 

Author Comment

by:helpmealot
ID: 6446770
Ah good point, I hadn't thought of that.  Thank you! :)  If there are no objections, I suppose I'll delete this question in a day or two.
0
 
LVL 2

Expert Comment

by:smitty1276
ID: 6447098
Actually, you should choose the comment that gave you the most advice and accept it as the answer to award the points to that expert.
0
 

Author Comment

by:helpmealot
ID: 6448172
Yes, you are right, I appologize.  It's only 50 points anyway.  I therefore have decided to award points to DanRollins as his comments helped me the most overall.

Thank you for the input everyone!
0
 
LVL 31

Expert Comment

by:Zoppo
ID: 6449876
>Thanks Zoppo, I have learned something new today and am a better man because of it!
You're welcome...
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Errors will happen. It is a fact of life for the programmer. How and when errors are detected have a great impact on quality and cost of a product. It is better to detect errors at compile time, when possible and practical. Errors that make their wa…
Written by John Humphreys C++ Threading and the POSIX Library This article will cover the basic information that you need to know in order to make use of the POSIX threading library available for C and C++ on UNIX and most Linux systems.   [s…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
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.

863 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

22 Experts available now in Live!

Get 1:1 Help Now