Link to home
Start Free TrialLog in
Avatar of rpmodell
rpmodell

asked on

Pointer to member function

I desperately need an untyped address of a member function... I'll explain. For instance, I have this class:

  class TestClass
  {
  public:
    void MyMemberFunction(void) {;}
  };

And I have a pointer:

  void * MyPointer;

How do i get the value of MyPointer to be the address of TestClass::MyMemberFunction, for instance, something like:

  MyPointer = TestClass::MyMemberFunction;

Or for some other compilers:

  MyPointer = &TestClass::MyMemberFunction;

In practice, something like

  asm call MyPointer;

Should be able to work after this (no, this is not what i intend to do :)

But then without the compilation errors. Btw, I don't care about the stuff like the pointer of an object of TestClass or something like that, that's already taken care off.
Thanks...
Avatar of NT_Programmer
NT_Programmer

There are two different ways.

1.  if the func is static:

class MyClass
{
   static void MyMemberFunc( int );
};

// declare the pointer like this:
void (*psfnP)(int) = &MyClass::MyMemberFunc;

and use it like this:
(*psfnP)(30);

2.  if it is not static:

class MyClass
{
   void MyMemberFunc( int );
};

// declare the pointer like this:
void (Data::*pmfnP)(int) = &MyClass::MyMemberFunc;

// and use it like this:
MyClass myclass;

(myclass.*pmfnP)(10);
Avatar of jkr
You can't do this in this way; a 'void*' can never be a pointer to a member funcion

 class TestClass
  {
  public:
    void MyMemberFunction(void) {;}
  };

typedef void ( TestClass::*PMPTR)();
// the type created above ALWAYS points to a memer function of class 'TestClass'
// which takes no arguments and returns nothing

PMPTR pMethodPointer = &TestClass::MyMemberFunction;

void main( void)
{
TestClass test;

( test.*pMethodPointer)();
}  

NOTE that you cannot use this pointer without an instance of the class - this is because method pointer are not 'real' pointer, but indices to method tables...
If you want to define a pointer-to-member-function and use that pointer to acually call the function for an existing object then you can use the ->* (or .*) operator, here is an example:

// define type - ptr to member
typedef void (TestClass::*FundPtr)();

..
..
FuncPtr f_ptr = TestClass::MyMemberFunction;

TestClass t_obj, *p_t_obj;

// call the function
(t_obj.*f_ptr)();

// or, for pointers
(p_t_obj->*f_ptr)();


Please note that you must place () around the function call becuase the compiler will interpret:
p_t_obj->*f_ptr()
as
p_t_obj->*(f_ptr()).

Udi.
sorry, did not see last two answers (I was busy typing my answer).

Please ignore it.
>> NOTE that you cannot use this pointer
>> without an instance of the class - this is
>> because method pointer are not 'real'
>> pointer, but indices to method tables...
What it actually "is" is implimentation defined, but if the procedure is not virtual the pointer probably is actually an ordinary pointer.  However C++ syntax rules still would not allow it to be converted to/from an ordinary pointer (Because such conversiuons wouldn't always be possible.)
The way to go is to avoid using the member directly, but establisch a call gate...

struct A
{
   static void call_f(A* a) { a.f(); }    // call gate for member f()
   void f();                                         // the member you want to call
};

Then you use a (ordinairy) pointer to the static call_f, as described above in the other comments.
This will even work with virtuals.
Luc
Avatar of rpmodell

ASKER


it seems that i've not been clear enough. the thing is, i don't care about the instance pointer and all that... what i need is very plain and simple, the address of the method. no typedefs, no instances, just the address. i'll explain a little bit differently:

  class TestClass
  {
  public:
    void Method(void);
  }

  void TestClass::Method(void)
  {
    int x;
    x = 5; // This instruction, or at least the first instruction of this procedure, is based at address 0x65a83f90
  }

  void * MyPointer;

  MyPointer = TestClass::Method; // MyPointer now has value 0x65a83f90

Of
 
  TestClass TestObject;
 
  MyPointer = TestObject.Method; // MyPointer now has value 0x65a83f90

i hope this is enough to get you on your way because all the answers and comments i can't use. sorry.
Suppose you can always use a union:

typedef void (TestClass::PMF)()

union
{
   void* pv;
   PMF   pf;
} u;

u.pf = &TestClass::Method;
cout << u.pv;
Opes the typedef should be

typedef void (TestClass::*PMF)()

and without the typedef

union
{
   void* pv;
   void (TestClass::*pf)(); // ??
} u;

u.pf = &TestClass::Method;
cout << u.pv;


And as template:
template<class PM>
void* get_func_address(PM pm)
{
  union
  {
     void* pv;
     PM pm;
  } u;
  u.pm = pm;
  return u.pv;
}

cout << get_func_address(&TestClass::Method);
Great idea, thanks. But do you have something with which i don't have to create something to get the pointer. What I want is to give the pointer as a parameter to a function... It would have to end up as something like this:

  MyFunction(MY_MACRO(Class::MemberFunction), (void *)ClassInstance);

If you, or someone else, could think of something for that, that would be great. If nothing else comes, i'll give you a B for that answer.
class A
{
public:
      virtual void func() {}
      static void func2() {}
};
void (A::*pf)() = A::func;
void (*pf2)() = A::func2;

As you see, it's different if the member function is static or virtual.
>>  i don't care about the instance pointer and all that.
If you intend to call this function at any time, you do need to care about that instance pointer.  The only time it might not matter is if you actually need to locate the function in memory, but i can't image a reason why you'd want that.

>> Suppose you can always use a union:
That is not guaranteed to work.  A pointer to member might not be a real pointer.  Especially if the member is virtual.

rpmodell, what is it that you want to do?  i.e. why do you want this pointer?  If you intend to call this function you are making a big mistake.  You are trying to break rules that are necessary to make C++ work, you won't succeed, fortunately there are ways to acheive what you want withing the rules.  If you need to the pointer for something else there may still be a way.  If you are under windows you can expert the procedure and use GetProcAddress() to get a pointer to the procedure.  Other than that I'm not sure what you can do.
to get the void pointers to function u can use:

void* pfv = (void*)(*((void**)&pf));
void* pfv2 = (void*)pf2;

u can do the second in one step:

void* pfv2 = (void*)(A::func2);

but for the first function u must do in two steps. You can create a macro for this.
widly, did you read the question history?  When you answer with information proposed by another expert it is like plagurism.  When you answer with an answer that has already been rejected it looks even worse.
nietod makes some good remarks now, about the use of such pointers. So far, getting a pointer to member function as a void pointer will be of little or no help, unless you only want to display it as a pointer...
Your 'example'
  MyFunction(MY_MACRO(Class::MemberFunction), (void *)ClassInstance);

indicates you want to do something with it. What?

BTW, the template can give an excellent replacement of 'MY_MACRO', but we'd need more information.
wildly, your new approach has the same problem that Kangaroo's does.  The value you get back may be meaninless.  A pointer to a member function may not be a true pointer.  
That's how MFC does it in OLE automation calls - as you see, an instance is needed (based on the assumption that it's a class derived from CCmdTarget):

/////////////////////////////////////////////////////////////////////////////
// Intel 386 version

#ifdef _X86_

__declspec(naked) void AFXAPI
_AfxDispatchCall(AFX_PMSG /*pfn*/, void* /*pArgs*/, UINT /*nSizeArgs*/)
{
      _asm
      {
            pop     edx         // edx = return address
            pop     eax         // eax = pfn
            pop     ecx         // ecx = pArgs
            add     ecx,[esp]   // ecx += nSizeArgs (=scratch area)
            mov     [ecx],edx   // scratch[0] = return address
            sub     ecx,[esp]   // ecx = pArgs (again)
            mov     esp,ecx     // esp = pArgs (usually already correct)
            pop     ecx         // ecx = this pointer (the CCmdTarget*)
            call    eax         // call member function
            ret                 // esp[0] should = scratch[0] = return address
      }
}
#endif // _X86_
I had no intention to give any meaning to such a pointer. I can not think of anything usefull to do with such a pointer.

In your example
MyFunction(MY_MACRO(Class::MemberFunction), (void *)ClassInstance);
you pass an 'instance pointer'. Why do so if you also state that you "don't care about the instance pointer"?
jkr, can you explain in more C++ish terminology? I'm afraid I don't speak assembler.
ok ok, i'll tell what i want to do... i want to use a member function for a wndproc. if you dig in the delphi inprise source code you can find a function which is called "MakeInstanceObject" or something like that (forms.pas). this makes a single pointer from a member function by dynamically writing some assembler code to memory and returning a pointer to the first instruction... i want to use this, but there are two problems. first, i don't use pascal, but c++. second, i don't use builder/delphi but visual c++. i got it to work using __closure in builder, but when i wanted to transfer it to visual c++, it didn't work anymore (it doesn't know __closure). what i intend to do is dynamically write some assembler code that moves the this pointer on ecx and calls the function. this is the actual code that does this:

BEGIN OF CODE

  #pragma pack(push, 1)
  typedef struct tagOBJECTINSTANCE
  {
    BYTE bCode;
    DWORD dwOffset;
    union {
      struct tagOBJECTINSTANCE * pNext;
      struct {
        LPVOID lpMethod;
        LPVOID lpObject;
      };
    };
  } OBJECTINSTANCE, *POBJECTINSTANCE;

  typedef struct tagINSTANCEBLOCK
  {
    struct tagINSTANCEBLOCK * pNext;
    BYTE bCode[2];
    LPVOID pWndProcPtr;
    POBJECTINSTANCE pInstances[INSTANCECOUNT];
  } INSTANCEBLOCK, * PINSTANCEBLOCK;
  #pragma pack(pop)

  PINSTANCEBLOCK pInstBlockList = NULL;
  POBJECTINSTANCE pInstFreeList = NULL;

  /* Standard window procedure
   * In    ECX = Address of method pointer
   * Out   EAX = Result
   */

  extern "C" LONG _declspec(naked) StdWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  {
  asm {
    PUSH    EBP
    MOV     EBP, ESP
    PUSH    lParam
    PUSH    wParam
    PUSH    message
    PUSH    hWnd
    PUSH    [ECX + 4]
    CALL    [ECX]
    POP     EBP
    RET     0x0010
  }
  }

  LONG CalcJmpOffset(LPVOID lpSrc, LPVOID lpDest)
  {
    return LONG(lpDest) - (LONG(lpSrc) + 5);
  }

  LPVOID MakeObjectInstance(LPVOID lpMethod, LPVOID lpObject)
  {
    PINSTANCEBLOCK pBlock;
    POBJECTINSTANCE pInstance;
    POBJECTINSTANCE pResult;

    if(pInstFreeList == NULL) {
      pBlock = (PINSTANCEBLOCK)VirtualAlloc(NULL, 4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
      pBlock->pNext = pInstBlockList;
      pBlock->bCode[0] = 0x59; // POP ECX
      pBlock->bCode[1] = 0xE9; // JMP StdWndProc
      pBlock->pWndProcPtr = LPVOID(CalcJmpOffset(&pBlock->bCode[1], StdWndProc));
      pInstance = POBJECTINSTANCE(&pBlock->pInstances);
      do {
        pInstance->bCode = 0xE8; // CALL NEAR PTR Offset
        pInstance->dwOffset = CalcJmpOffset(pInstance, &pBlock->bCode);
        pInstance->pNext = pInstFreeList;
        pInstFreeList = pInstance;
        LONG(pInstance) += sizeof(OBJECTINSTANCE);
        } while(LONG(pInstance) - LONG(pBlock) < LONG(sizeof(INSTANCEBLOCK)));
      pInstBlockList = pBlock;
      }
    pResult = pInstFreeList;
    pInstance = pInstFreeList;
    pInstFreeList = pInstance->pNext;
    pInstance->lpMethod = lpMethod;
    pInstance->lpObject = lpObject;

    return pResult;
  }

  void FreeObjectInstance(LPVOID pObjectInstance)
  {
    if(pObjectInstance != NULL) {
      POBJECTINSTANCE(pObjectInstance)->pNext = pInstFreeList;
      pInstFreeList = (POBJECTINSTANCE)pObjectInstance;
      }
  }


END OF CODE

if someone lost me here, don't worry, it's pretty advanced stuff... so, if someone could describe a macro for me that converts ANY member function (NOT static, but a normal __stdcall member function) to a void * pointer (LPVOID), i would be very very gratefull. thanks guys.
That is code to perform a non-static member function call.  (it is not code to get this pointer)  It gets a pointer to the function to be called in EAX.  (this is a real pointer, that is passed to the function, it does not generate this pointer) and it gets a pointer to the object in ECX,  and it removes some "extra" data from the top o the stack so that the parameters for the function to be called are at the top of the strack.  Then it calls the function.
>> i want to use a member function for a wndproc
You can't do this directly.  But you can do it indirectly.

A window procedure must be an "ordinary" procedure.   That is it cannot be  a non-static member procedure.  This is because a non-static member procedure takes an additional hidden parameter--a pointer to the object that it is to work on, the "this" pointer.  As you can see in jkr's code above this pointer is passed in ECX.  Windows will not pass this parameter to a procedure, because it was not designed to call non-static member procedures.  Any attempt to make it call a non-static member procedure directly will fail.   You may be able to make it compile, but you can't make it survive.

continues.
The secret to making it work is to use a "interface" procedure.  This is an ordinary procedure.  either a global procedure or a staitc member procedure.  (i.e. a procedure that does not work on an object).  This procedure is called by windows.  This procedure then obtains a pointer to an object of some sort and then calls the non-static member procedure of that object.  

This works and does not break and C++ rules.  

The trick might be the "obtains a pointer to an object" part.  Where does it get this pointer from?  I tmay be stored ina global variable, if there will be only one such object.   Or you might want to store a pointer to the object inside the window's extra data space.  The interface procedure can use GetWindowLong() to obtain this pointer, cast it to the right pointer tyupe, then call the member procedure.

Let me know if you have any questions.
>>As you can see in jkr's code above this pointer is passed
>>in ECX

But the instance addrss can be stored in the *window* class' extra bytes 'Set/GetWindowLong( ..., GWL_USERDATA,...);'
Ah, once again straight C++ is victorious!
>> But the instance addrss can be stored in the
>> *window* class' extra bytes 'Set/GetWindowLong( ...,
>> GWL_USERDATA,...);'
The instance address, yes.  The pointer to the member procedure, maybe not.  (although there really should be no need to store it.) The pointer to the object is an ordinary pointer.  The pointer to the member is not.  it may not be the same size as an ordinary pointer.
But if you have a pointer to an object, and a pointer to a method your can call the method on the object (provided that every thing matches).
Adjusted points to 250
guys, you really aren't helping me here... this code works. it's tested code and it works perfectly using builder c++ (although there are already a few changes made for work with visual c++, namely the two pointers instead of a __closure pointer). what i need is no comments on the exisiting code because it works just fine and it's what i need. what i do need is a way to freaking fill the pointers i want. so, once again: lpMethod needs to get the pointer of a NON-STATIC MEMBER FUNCTION and lpObject needs to get the pointer to an object (which will probably just be (LPVOID)this). so, what i need is this:

#define MY_MACRO /* tell me what i need to type here*/

..
..
..

  wc.lpfnWndProc = (WNDPROC)MakeObjectInstance(MY_MACRO(TestClass::MyWndProc), TestClassObject);

..
..
..

i hope you have enough information now... thanks.
>> you really aren't helping me here.
Yes we are, you aren't listening.

In your code you have the following

>>  /* Standard window procedure
>>   * In    ECX = Address of method pointer
>>   * Out   EAX = Result
>>  */
>>
>>                   PUSH    [ECX + 4]

that cannot be guaranteed to work in C++.  That assumes that ECX has the _address_  of the function you need to call.  C++ contains no features for producing addresses.  You can use & to generate a pointer, but a pointer does not necessarily contain an address.  In particular pointers to members may contain offsets into virutal function tables, they may contain offsets to data members, they may be structures that contain multiple elements.

Now in windows, you can use GetProcAddress() to get a pointer to a procedure.  

But you really need to ask yourself  "Why"?  You cannot justify this approach.  It is depend upon implimentation details that are subject to change without notice.  There are legitimate C++ methods that will acheive the same ends with complete safety and no additional work.
And probably less work. All true, you've based the design on a non-portable feature. There are portable ways to do this, there always is.
>> this code works. it's tested code and it
>> works perfectly using
>> builder c++
By the way, the reason it works is because it is relying on implimentation details that are true for builder.  It won't work for VC because it uses a different implimentation.  (ECX must be used to pass the "this" pointer).  You can make it work for VC too, but then it won't work for builder--unless you use conditional compilation.  Then it will work on both, unless either of them changes their implimentation of the "this call", which is certainly possible (likely really, they have changed many of their implimentations in the past.)
Yes, I agree with nietod.  In addition I think:

I answered the question you posed.  But the more we answered you, the more you said, "No that's not what I meant, I mean I want it this way." and kept changing it.  Until finally you want us to write something that works with some strange concoction you got from Pascal.

If your question was this complicated, you should have posted all of this info up front instead of leading us on and continually telling us we are wrong.
guys, ur all 100% right... the reason i used this approach in the first place was because i had no idea how to solve it differently. for some reason the stupid developers of windows (thanks microsoft) didn't give a way to give a user data value with the wndproc so you can't write a generic wndproc, what you can do with most callback functions... if you can help me with this problem (which really is the problem), i would really appreciate this. sorry if i wasn't clear with the question, ur all right, but i thought i was. i hope you have enough information now.
thanks.
Right On Commander!

Though we have some ideas, can you give 'specifications'.

Basically you want a Window Procedure that relates to a member function of a specific class/object?
>> or some reason the stupid developers
>> of windows (thanks microsoft) didn't give a
>> way to give a user data value with the wndproc
Actually they did.  And they gave you a much more powerful way than they do with most other callback procedures.  They gave you the window extra data.  This allows you to store any amount of additional data.  (I use it to store a pointer to a window object, just like you need to do.)  

In addition, they gave you two other mechanisms.  You can also store data in the window class (GetClassLong()) and you can store properties with a window (SetProp() and GetProp()).  The properties give you much more freedom than the other mechanism, but are a little slower.

I think if you re-read the suggestion I made about using an interface procedure, I think you will find it works perfectly.  I use it in my software all the time (I'm not the only one I'm sure) and my source compiles and works under both VC and BCB.
Almost there guys. SetWindowLong and GetWindowLong works perfectly, thanks for that one. There is still one problem remaining though. Before CreateWindowEx returns with a hWnd, there are already 5 messages send to the windowproc, being:
  NC_CREATE
  NC_CALCSIZE
  WM_CREATE
  WM_SETSIZE and
  WM_MOVE
Because SetWindowLong can't be called before CreateWindowEx returns, GetWindowLong won't succede and will return 0 instread of a pointer to an instance. If we can get over this small humb, we're finished. Again, thanks for the help till now.
ASKER CERTIFIED SOLUTION
Avatar of nietod
nietod

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks guys, especially Nietod. It works great. I'm just ignoring every message that comes before the WM_NCCREATE (returning -1, if someone could tell me what the normal #define for this is, it'd be even more happy, but i think this is enough for now :). I hope i can help you if you need some help in the future. See you on ExpertsExchange. Bye for now.
Rather than ignoriing the message, it would be safest to pass it to the default window procedure. i.e.

if (Msg != WM_NCCREATE)
   return DefWindowProc(hWnd,Msg,wParam,lParam);
else
  *  *  *

>> what the normal #define for this is
For what?
For the standard failed result. Something like ..._FAILED. Doesn't really matter, this is great... I think this is enough for me to finish what I'm doing. Thanks.
>> For the standard failed result
There is non.  Every message is different.  Most messages return 0, but some return other values of significance.   Most messages don't have a "failed" option.  i.e the message has to be handled or the program is screwed.