Solved

Function ptr -> void * -> Function ptr

Posted on 2001-08-30
13
560 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
Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

 
LVL 30

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 30

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

Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

Join & Write a Comment

Introduction This article is the first in a series of articles about the C/C++ Visual Studio Express debugger.  It provides a quick start guide in using the debugger. Part 2 focuses on additional topics in breakpoints.  Lastly, Part 3 focuses on th…
Many modern programming languages support the concept of a property -- a class member that combines characteristics of both a data member and a method.  These are sometimes called "smart fields" because you can add logic that is applied automaticall…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.

760 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

17 Experts available now in Live!

Get 1:1 Help Now