views:

329

answers:

4

Hello Group,

I read sometime back (probably on c.l.c++.moderated) that virtual function calls can be templatized. I tried something on the following lines.

#include <iostream>

template<class T, class FUN> 
void callVirtual(T& t, FUN f){ 
   (*t.*f)(); 
} 


struct Base{ 
   virtual ~Base(){} 
   virtual void sayHi()=0; 
}; 


struct Derived : public Base{ 
   void sayHi(){ 
      std::cout << "Hi!" << std::endl; 
   } 
}; 


void Test(){ 
   Base* ptr = new Derived; 
   callVirtual(ptr,&Base::sayHi); 
} 

int main()
{
   Test();
   return 0;
}

Output:
Hi!

The templatized method though given the address of pure virtual base member method at compile time calls the correct method at runtime. Is it legal in standard C++ to take address of a pure virtual member?

Thanks in advance

EDIT-1: I removed the second part of the question 'how does it work?'. Looks like that is what is catching attention.

EDIT-2: I searched c.l.c++.moderated and came across this link (http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/5ddde8cf1ae59a0d). The consensus seems like since the standard does not restrict it, it is vaild.

EDIT-3: After reading the codeproject article (thanks to ovanes), i am thinking that the compilers do some magic. Since virtual functions are implemented via vtable (which is compiler specific), taking address of a virtual function always gives the offset in the vtable. Depending on the 'this' pointer used, the appropriate function (whose address is at the offset) is called. I am not sure how to vindicate this though as the standard does not say anything abt it!

A: 

I would guess that it's undefined. I couldn't find anything in the spec.

virtual methods are implemented using the concept called a vtable.

I would say that it's compiler implementation specific. I don't really think it matters that it's a pure virtual, the same would happen if it's just virtual as well.

I just compiled your code with Visual Studio 2008 and dissembled the exe. What VS2008 does is create a thunk function to jump to the vtable entry using the passed in 'this' pointer.

This is the setup and call to the callVirtual template function.

push    offset j_??_9Base@@$B3AE ; void (__thiscall *)(Base *)
lea     eax, [ebp+ptr]
push    eax             ; Base **
call    j_??$callVirtual@PAUBase@@P81@AEXXZ@@YAXAAPAUBase@@P80@AEXXZ@Z ; callVirtual<Base *,void (Base::*)(void)>(Base * &,void (Base::*)(void))

So it passing the function pointer to the thunk function: j_??_9Base@@$B3AE

; void __thiscall Base___vcall_(Base *)
j_??_9Base@@$B3AE proc near
jmp     ??_9Base@@$B3AE ; [thunk]: Base::`vcall'{4,{flat}}
j_??_9Base@@$B3AE endp

All the thunk function is doing is using the vtable to jump to the real class method.

Shane Powell
Thanks. Do you know a section of C++ standard which mentions member-function pointers to pure virtuals. I tried finding but could not loacte any specific details. Seems to me that since member-pointers to an incomplete type are allowed, member-pointers to pure-virtuals are also allowed.
Abhay
I mis-read the question. I'll update my answer.
Shane Powell
+1 Your edited answer comes close to an 'answer' to this question :-). I am begining to think that taking address of virtual functions (pure or not) and invoking it is more towards an implementation-defined aspect of the language.
Abhay
It's surprising how much is not defined and left up to the implementor of the compiler to choose whatever implementation they want.
Shane Powell
@Shane Powell: The answer is incorrect. There's absolutely nothing undefined or implementation-defined about the OP's code. The code is valid and the behavior is defined by the standard. Disassembling the compiled code will not help here. The compiler is just implementing the standard behavior in one possible way.
AndreyT
+1  A: 

Sure it is. Your code is no different than merely calling the pure virtual method like this:

void Test()
{ 
   Base* ptr = new Derived; 
   ptr->sayHi();
   delete ptr;
}

The only difference is that you have another mechanism for doing the call, and in this case through callVirtual().

Magnus Skog
Ahh, and this another mechanism involves taking address of a pure virtual member function. That is what i was mainly concerned with.
Abhay
+1  A: 

As Magnus Skog said, the template part isn't really relevant. What it boils down to is that:

(ptr->* &Base::sayHi)()

seems to work, but

ptr->Base::sayHi()

obviously doesn't because sayHi is pure virtual.

I haven't been able to find anything in the standard about what happens when you take the address of a virtual, or pure virtual, function though. I'm not sure it's legal. It works in GCC and MSVC though, and Comeau's online compiler doesn't complain either.

Edit

Even if it is valid, as your edits say, I'm still wondering what it means.

If we assume for simplicity that sayHi is non-pure (so a definition of Base::sayHi exists), then what happens if I take the address of it? Do I get the address of Base::sayHi, or the address of the function that the vtable points to (Derived::sayHi in this case)?

Apparently, compilers assume the latter, but why? Calling ptr->Base::sayHi() calls sayHi in the base class, but taking the address of Base::sayHi gives me the address of Derived::sayHi

It seems inconsistent to me. Is there some rationale behind this that I'm missing?

jalf
I am confused too. C++ continues to baffle me :-(
Abhay
+1  A: 

Hi!

This following article extensively discusses member function pointers in C++, how they are implemented and where are the flaws. It also handles virtual member function pointers and much more. I think it will answer all your questions.

http://www.codeproject.com/KB/cpp/FastDelegate.aspx

It also shows how to implement delegates in C++ and what pitfalls you might trap in.

With Kind Regards,

Ovanes

ovanes
An excellent read, Thanks. Looks like i got my answer(See edit-3).
Abhay