Solved

Function ptr -> void * -> Function ptr

Posted on 2001-08-30
13
566 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
Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
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
 
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

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

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

Suggested Solutions

When writing generic code, using template meta-programming techniques, it is sometimes useful to know if a type is convertible to another type. A good example of when this might be is if you are writing diagnostic instrumentation for code to generat…
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…
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
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.

828 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