Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Bound Member Functions

Status
Not open for further replies.

Wereotter

Programmer
Aug 30, 2004
17
0
0
US
I am using a C-based library (sqlite) from my C++ program. This library includes a function which takes a pointer to a callback. I'd like to use a member function as the callback.

sqlite3_exec(sldbDatabase, "", &(this->AddFarmFromRow), this, &sError);

The first parameter passed to the callback function is the parameter in bold above. I know, from other programs I've done, that this should work just fine, if I can get the address to this function. This is what I get instead, however:

error C2276: '&' : illegal operation on bound member function expression

According to MSDN, this error relates to taking the address of a virtual function. Unfortunately, this isn't a virtual function. Even if it were, I could still make it work if I could extract the function pointer from the virtual function call table. Thus, my question:

How can I get a pointer to a member function for a specific object?
 
Make it a static function i.e. something like
Code:
class Fred
{
public:
   // SQLite calls this one
   static void AddFarmFromRow (Fred* me)
   {
      me->AddFarmFromRow ();
   }

   // This avoids having to put me-> in front of everything
   void AddFarmFromRow()
   {
       // do whatever you want with your member variables
   }
}

...
Fred derf;
sqlite3_exec(sldbDatabase, "", Fred::AddFarmFromRow, &derf, &sError);
 
That is definitely an option, but one I was hoping to avoid. Before you posted, I'd already implemented this, and it works fine, but I'd still like to know why exactly I can't get the address of a member function, even a non-virtual one.
 
Different types of calls.
A function of a class requires a this_call that is a C++ type call.
The sqllite function is c-based and is expecting to make a c_call.
So they are incompatible.
Making theclass funtion static effectively turns it to into standard c_call and so it works ok.
(but could present other 'problems')
 
A function of a class requires a this_call that is a C++ type call.
OK, yes. However, when all is said and done, the calling convention is actually (in this case) __cdecl with the addition of a 32-bit pointer to the beginning of the argument list. While this may not hold true on every platform, I'd bet that it holds true on most, and it certainly will hold true for all the platforms for which I am compiling.
 
It's not a C/C++ calling convention issue. Member function call impicitly pass this pointer to the class object. C-like callback function can't do it (or accept and use this pointer). Member 'callback' function can't accept its object pointer when callback invokes it via an ordinal function pointer. You (we;) need statics or some kind of a proxy...
No this pointer in a static member function - and no problems. Its pointer looks like (and has all right of) as an ordinal pointer to a function.
 
Code:
class Example
{
public:
	void __stdcall foo_member(int x) {}
};

void __stdcall foo_global(int x) {}

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	Example e;

	e.foo_member(1);
	foo_global(1);

	return 0;
}
Becomes
Code:
14:   int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
15:   {
00401060   push        ebp
00401061   mov         ebp,esp
00401063   sub         esp,44h
00401066   push        ebx
00401067   push        esi
00401068   push        edi
00401069   lea         edi,[ebp-44h]
0040106C   mov         ecx,11h
00401071   mov         eax,0CCCCCCCCh
00401076   rep stos    dword ptr [edi]
16:       Example e;
17:
18:       e.foo_member(1);
00401078   push        1
0040107A   lea         eax,[ebp-4]
0040107D   push        eax
0040107E   call        @ILT+20(Example::foo_member) (00401019)
19:       foo_global(1);
00401083   push        1
00401085   call        @ILT+15(foo_global) (00401014)
20:
21:       return 0;
0040108A   xor         eax,eax
22:   }
0040108C   pop         edi
0040108D   pop         esi
0040108E   pop         ebx
0040108F   add         esp,44h
00401092   cmp         ebp,esp
00401094   call        __chkesp (004010f0)
00401099   mov         esp,ebp
0040109B   pop         ebp
0040109C   ret         10h

As you see, the calls are identical, as long as a calling convention is explicitly specified, except for the this pointer. Interestingly, I found that
Code:
&Example::foo_member
works, but the pointer is unbound, thus I am not certain if it may be passed as a 32-bit absolute pointer, and indeed, the compiler will not allow the required cast. Thus, I need a bound pointer, not an unbound pointer.
 
Code:
template<class Type> void* MemberAddress(Type pFunction)
{
	void* pFuncAddress;

	__asm mov eax, pFunction
	__asm mov pFuncAddress, eax

	return pFuncAddress;
}

void* pAddress = MemberAddress(&Example::foo_member);
You may then call this as with any other function, simply passing a pointer to an Example object as the first parameter. This, as one would expect, only works for non-virtual methods, which was what I wanted.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top