[haiku-commits] Re: haiku: hrev47567 - src/kits/interface

  • From: Ingo Weinhold <ingo_weinhold@xxxxxx>
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Tue, 29 Jul 2014 06:55:02 +0200

On 28.07.2014 21:21, pulkomandy wrote:
The second strategy used at Be comes in when we run out of reserved
fields. It is a bit less flexible. Many classes define a virtual method
called Perform. This method takes a "perform code", and, when used, it
looks like this:

void A::Perform(int opcode, void* args)
{
        switch(opcode)
        {
                case OP_DOSOMETHING: DoSomething(args); return;
                // more cases
        }
}

This can emulate the behavior of virtual methods (you can override
Perform, and have it call other methods in subclasses for some ops). It
replaces the vtable with a switch to dispatch the calls, which is slower
but doesn't have the problems of shifting offsets when adding new
opcodes

While I recall having read something similar in a BeOS documentation or newsletter, this is actually not the purpose of this mechanism, at least not the main purpose. Consider the following example with a chain of subclasses: BBase <- BDerived <- User. The former two are system classes, the third is a third-party application class. Let's say they have the following vtables:

BBase:
{
BBase::foo
BBase::Perform
BBase::reserved0
}

BDerived:
{
BDerived::foo
BDerived::Perform
BBase::reserved0
}

User:
{
User:foo
BDerived::Perform
BBase::reserved0
}

Now the system implementer (Be, us) decides to add a new virtual method to BBase and also override it in BDerived. This changes the vtables of BBase and BDerived:

BBase:
{
BBase::foo
BBase::Perform
BBase::bar
}

BDerived:
{
BDerived::foo
BDerived::Perform
BDerived::bar
}

However, unless the User class is rebuilt with the new headers, its vtable remains the same. That is, if on a User object the bar() method is invoked, it will actually call BBase::reserved0. That symbol has of course been provided for binary compatibility. But how is it implemented. We can't make a virtual function call to bar(), since we'd end up calling ourselves again. Invoking BBase::bar() non-virtually would skip BDerived::bar(), though.

This is where Perform is used. We invoke Perform(PERFORM_CODE_BAR). Since that had been overridden by BDerived originally, the call ends up in BDerived::Perform(), which in turn calls BDerived::bar() non-virtually.

Perform() would be superfluous (for this purpose) had we (Be) decided to override the reserved0() in BDerived in the first place.

The mechanism can be seen in action e.g. in BView for the layout management related virtual functions.

CU, Ingo


Other related posts: