C++ - allow access through the base class (interface), forbid access through the derived class (concrete implementation)?

StackOverflow https://stackoverflow.com/questions/15571044

Question

Let's say I have pure abstract class IHandler and my class that derives from it:

class IHandler
{
public:
   virtual int process_input(char input) = 0;
};

class MyEngine : protected IHandler
{
public:
   virtual int process_input(char input) { /* implementation */ } 
};

I want to inherit that class in my MyEngine so that I can pass MyEngine* to anyone expecting IHandler* and for them to be able to use process_input. However I don't want to allow access through MyEngine* as I don't want to expose implementation details.

MyEngine* ptr = new MyEngine();
ptr->process_input('a');                           //NOT POSSIBLE
static_cast<IHandler*>(ptr)->process_input('a');   //OK
IHandler* ptr2 = ptr;                              //OK
ptr2->process_input('a');                          //OK

Can this be done via protected inheritance and implicit casting? I only managed to get:

conversion from 'MyEngine *' to 'IHandler *' exists, but is inaccessible

Since I come from C# background, this is basically explicit interface implementation in C#. Is this a valid approach in C++?

Additional:

To give a better idea why I want to do this, consider following:

Class TcpConnection implements communication over TCP, and in its constructor expects pointer to interface ITcpEventHandler. When TcpConnection gets some data on a socket, it passes that data to its ITcpEventHandler using ITcpEventHandler::incomingData, or when it polls for outgoing data it uses ITcpEventHandler::getOutgoingData.

My class HttpClient uses TcpConnection (aggregation) and passes itself to TcpConnection constructor, and does processing in those interface methods.

So TcpConnection has to implement those methods, but I don't want users using HttpClient to have direct access to ITcpEventHandler methods (incomingData, getOutgoingData). They should not be able to call incomingData or getOutgoingData directly.

Hope this clarifies my use case.

Was it helpful?

Solution

Deriving with protected makes the members of the base class inaccessible through a pointer to the derived class, and disallows the implicit conversion.

It seems to me that what you want is not to forbid access through the base class (interface), but rather through the derived class (concrete implementation):

class IHandler
{
public:
   virtual int process_input(char input) = 0;         //pure virtual
   virtual std::string name() { return "IHandler"; }  //simple implementation
};

class MyEngine : public IHandler
//               ^^^^^^
{
protected: // <== Make the functions inaccessible from a pointer
           //     or reference to `MyEngine`.

   virtual int process_input(char input) { return 0; }   //override pure virtual
   using IHandler::name;                                 //use IHandler version
};

Here, in the derived class you basically override the visibility of the process_input function, so that clients can only call them through a pointer or reference to the base class.

This way you will make this impossible:

MyEngine* ptr = new MyEngine();
ptr->process_input('a');   // ERROR!
std::cout << ptr->name();  // ERROR!

But this will be possible:

IHandler* ptr = new MyEngine();
ptr->process_input('a');   // OK
std::cout << ptr->name();  // OK

OTHER TIPS

In C++ protected and private inheritance serve the use of inheritance of the implementation. This is, you define a class with methods, a template class for example and when you want to use its functionality but not its interface, you inherit protected or private. So actually your base class would need to define the methods you want to use in the sub-class.

Here is a link on this topic. It really is difficult, I agree.

It's slightly hard to understand the real goal you hope to achieve here, because whether you call the method on the parent or child, as long as it's virtual the same one will be called.

That said you have a couple options.

You could make it so the user can't get a pointer (or object) of the child type by forcing a create call that returns an interface. Then you don't have to worry about artificial restrictions, they just can't get a child at all:

class Concrete : public Interface
{
public:
    static Interface* create() { return new Concrete; }

private:
    Concrete() { }
};

You could override the interface as protected as shown in a different answer.

You could utilize the non-virtual interface pattern to make the entire accessible public interface defined in the parent. Then it doesn't matter what object they have, they always get the public API from the interface class:

class Interface
{
public:
    void foo() { foo_impl(); }

private:
    virtual void foo_impl() = 0;
};

class Concrete
{
private:
    virtual void foo_impl() { }
};
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top