Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 580
  • 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
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
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

Featured Post

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

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