Improve company productivity with a Business Account.Sign Up

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 589
  • Last Modified:

Function ptr -> void * -> Function ptr

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
helpmealot
Asked:
helpmealot
  • 3
  • 3
  • 3
  • +3
1 Solution
 
TriskelionCommented:
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
 
BeyondWuCommented:
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
 
DanRollinsCommented:
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: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

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.

 
DanRollinsCommented:
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
 
smitty1276Commented:
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
 
smitty1276Commented:
It looks like DanRollins already said essentially the same thing I did... sorry.
0
 
ZoppoCommented:
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
 
helpmealotAuthor Commented:
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
 
DanRollinsCommented:
>> 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
 
helpmealotAuthor Commented:
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
 
smitty1276Commented:
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
 
helpmealotAuthor Commented:
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
 
ZoppoCommented:
>Thanks Zoppo, I have learned something new today and am a better man because of it!
You're welcome...
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Get expert help—faster!

Need expert help—fast? Use the Help Bell for personalized assistance getting answers to your important questions.

  • 3
  • 3
  • 3
  • +3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now