[Ns-developers] C++ virtual method overloading and inheritance = disaster?
Gustavo Carneiro
gjcarneiro at gmail.com
Wed Jul 9 04:52:40 PDT 2008
2008/7/9 Raj Bhattacharjea <raj.b at gatech.edu>:
> On Tue, Jul 8, 2008 at 7:29 PM, Gustavo Carneiro <gjcarneiro at gmail.com>
> wrote:
> [snip]
> > Compiling the above program I get:
> >
> > gjc at dark-tower:tmp$ g++ test.cc
> > test.cc: In function 'int main()':
> > test.cc:18: error: no matching function for call to
> 'UdpSocket::Bind(int)'
> > test.cc:11: note: candidates are: virtual int UdpSocket::Bind()
> >
> > WTF? How come a Bind() method in UdpSocket hides the other Bind() method
> > defined in the base Socket class?
>
> It doesn't, the C++ type system just isn't as smart as you assume it
> is in this example. You declared a UdpSocket, and if you look in your
> UdpSocket struct, it only has a single API, just as the compiler
> reports. Because you declared a stack allocated UdpSocket, this is
> the expected behavior. To make the virtual magic happen, you need a
> base class pointer pointing to a subclass.
>
> >
> > Commenting out the Bind() method in UdpSocket makes the program compile.
> >
>
> I don't believe this is the right approach, but I'm not a python
> bindings guru. More on this below.
>
> > I bring this example this because this problem appears in NS-3, and
> Python
> > bindings suffer.
> >
> [more code snipped]
> >
> > Now, what happens next. When I call socketFactory.CreateSocket () from
> > Python, the Python bindings know that the method is supposed to return
> > Socket*. However, they also know that there exists a known public class
> > UdpSocket, and use C++ RTTI (typeid(*pointer)) to discover this is
> actually
> > a UdpSocket object, not a Socket. Finally, when I call socket.Bind
> > (address) from Python, the call fails because apparently only one Bind()
> > method exists, and not the other.
>
> This seems to be the error to me...in order to make C++ virtual
> methods and inherticance work just so, you NEED to use the base class
> pointer. Is this downcast using the RTTI a necessary feature of the
> bindings? If you don't downcast, your problems are solved. That is,
> using the following code for your example main does exactly what you
> want, because the base class is what contains ALL the info about the
> virtual API:
>
> int main(void)
> {
> Socket* sock = new UdpSocket();
> sock->Bind (123);
> }
>
> >
> > A long story just to explain why we need to fix UdpSocket, adding the
> > missing Bind method. And to vent some of my frustration at this (having
> > spent some hours trying to support this use case in PyBindGen only to
> find
> > out that C++ itself does not support it :P).
>
> To reiterate, I think that C++ does support this case just fine, its
> the downcasting you do based upon the RTTI that is the issue. Just
> call the API on the base class pointer.
I understand that using the base class type works. However I don't agree
with you in that the code is OK. It is not OK because it violates the
Liskov substitution principle [1], since UdpSocket is supposed to be an
implementation of the abstract Socket class, not just another class.
The documentation of the UdpSocket class says:
> * This class exists solely for hosting UdpSocket attributes that can
> * be reused across different implementations.
>
Clearly re-declaring pure virtual methods does not in any way help the reach
goal of the class of hosting attributes. So I agree with Mathieu in that we
should just eliminate the pure virtual methods declarations of the
intermediate class.
[1] http://en.wikipedia.org/wiki/Liskov_substitution_principle
--
Gustavo J. A. M. Carneiro
INESC Porto, Telecommunications and Multimedia Unit
"The universe is always one step beyond logic." -- Frank Herbert
More information about the Ns-developers
mailing list