5

I have a class B that inherits publicly from A:

class A {
    private:
    virtual void method();
}

class B : public A {
    private:
    void method();
}

Now, I need to somehow call the original A::method() within B::method(), without invoking the copy constructor for A.
A is defined in a library I'm trying to extend, so I can't change this code (make method protected for example). Is it possible to somehow cast the this ptr within B::method() and slice off the overridden method?

I'm using an external interface that calls A::method(). This interface correctly calls my overridden B::method(), but I can't seem to make the interface call within B::method() not generate a stack overflow.

jww
  • 97,681
  • 90
  • 411
  • 885
BarrensZeppelin
  • 345
  • 1
  • 4
  • 15
  • 1
    See also http://stackoverflow.com/questions/2170688/private-virtual-method-in-c, suggesting that the authors of class `A` were trying to protect against this. – Thomas Dec 17 '14 at 18:06
  • I think you're going to face nothing but issues with your proposed approach. 1. probably not virtual 2. Probably doesn't have a virtual destructor 3. The developers didn't intend this method to be overridden or called outside of its implementation (hence private and not protected). Looks like you're trying to do something they intended not to happen. – Freddy Dec 17 '14 at 18:10
  • 1
    @cdhowie: they can be `private` and they can still be overridden. If it weren't for the not-so-infrequent need of having to call the base version to do some operations on it, it would be a reasonable idiom (for a while I advocated making all `virtual` functions `private` but I realized that this is **wrong**: they shall be `protected` instead). – Dietmar Kühl Dec 17 '14 at 18:13
  • @DietmarKühl Yeah, protected virtual is what I've always done for virtual methods that shouldn't be public. Still, it's nice to know the private virtual option exists (even though I probably won't make much use of it). – cdhowie Dec 17 '14 at 18:17
  • 1
    This is a sign `method()` isn't meant to be called explicitly by subclasses; you might break the state of the object by doing so (which would be bad)! If you feel that `method()` should, in fact, be protected, I would contact the library supplier and/or just change the source yourself, if it is available to you. – Julian Dec 17 '14 at 19:28

2 Answers2

4

Since private method can't be called qualified and the override can't be undo you won't be able to call the private method without another object. Casting the object won't have any effect because the way virtual functions are handled is part of the actual object.

In the past I had advocated making all virtual functions (other than the destructor) private but the need to call the base class version is actually not that uncommon. Thus, virtual functions should not be private but rather protected. Of course, if an interface actually makes its virtual functions private that design mistake can't be undone by the user of this interface.

Seeing that answers advocating horrible hacks (#define private protected) get upvotes, I'd recommend to rather rely on non-virtual member functions being added not changing the object layout and edit the header file to add a suitable function:

class A {
private:
        virtual void method();
protected:
    void call_method() { this->A::method(); }
};

This change has a much more local effect and is also just not portable. However, it merely relies on the object layout not being changed by adding a non-virtual (inline) method and an access specifier. No warnings at the language level or so would be affected.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • Thankfully, at least the author of the interface can later correct that error in an ABI-preserving way (at least I don't know any ABI where that won't preserve the libraries public (and protected) interfaces). – Deduplicator Dec 17 '14 at 18:21
  • 1
    @Deduplicator: ... and your non-conforming hack relies heavily on the ABI not changing in any way when the access specifier is used. If we are talking no-conforming hacks, I'd actually just edit the header and add a non-`virtual` `protected` `inline` function which calls the function with a suitable qualifier... – Dietmar Kühl Dec 17 '14 at 18:22
  • Well, a +1 for that suggestion. – Deduplicator Dec 17 '14 at 18:30
  • @DietmarKühl I'm interested in why that would be non-conforming. Which part of the spec says that? – cdhowie Dec 17 '14 at 19:16
  • @cdhowie: you mean, adding the member above? It is a violation of the One Definition Rule: `A` is defined differently when compiling the modified header than when it was compiled for the library. I'm right now too lazy to find chapter and verse. – Dietmar Kühl Dec 17 '14 at 19:18
  • @DietmarKühl Right, okay. I assume that adding a friend declaration to the class would likewise break ODR. – cdhowie Dec 17 '14 at 19:19
  • 1
    @cdhowie: yes. Any change to the definition would be ODR violation. They are unlikely to affect the layout of the object and cause actual problems but undefined behavior can behave exactly as you'd hope it would behave and then choose to behave entirely different when it is really important for the software to work correctly (e.g., when the sales person is making the supposedly final demo before closing the multi-million deal: it won't go down well to explain "well, you know, our program just had a bit of undefined behavior"). – Dietmar Kühl Dec 17 '14 at 19:23
  • @DietmarKühl Yes, I am frequently espousing the dangers of UB myself -- I just wanted clarification on what constitutes a violation of the ODR. – cdhowie Dec 17 '14 at 19:35
3

There is no conforming way to call the private function in the derived class.

At least if there is no friend or member-template you can specialize with your own private type to subvert the intent while obeying the letter of the law.

  1. Still, there is the #define private public-hack, which might work on your implementation.
    It's Undefined Behavior though, so don't complain that it does not work everywhere. Probable failure-mode is linking-error, though it can in theory get arbitrarily bizarre.

  2. Far more reliable would be adding a non-virtual protected inline-forwarder, though that means actually editing the header.

  3. Also, gcc has -fno-access-control to disable all access-checking: https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/C_002b_002b-Dialect-Options.html#C_002b_002b-Dialect-Options

    Turn off all access checking. This switch is mainly useful for working around bugs in the access control code.

Community
  • 1
  • 1
Deduplicator
  • 44,692
  • 7
  • 66
  • 118