Home > Mobile >  allow access to some methods only to direct children
allow access to some methods only to direct children

Time:07-13

Suppose I have a type Test with a print() method and another type SubTest which inherits from Test.

Now I have a new type SubSubTest which inherits from SubTest. Is it possible to prevent SubSubTest from accessing the print() method of Test but allow SubTest to access it? Basically, allow access to some methods only to direct children. All this will be done with CRTP classes so no virtual functions.

class Test {
protected:
        void print() {
                std::cout << "in Test" << std::endl;
        }
};

class SubTest: public Test {
        void print() {
                Test::print();
        }
};

class SubSubTest: public SubTest {
        void print() {
                Test::print(); // error not allowed
        }
};

EDIT:

#include <iostream>

enum class Type {
        whatever,
};

template <typename T>
class SSLsocket;

struct Socket {
        void send(char *buff, int size)
        {
                std::cout << "Socket send" << std::endl;
        }
        void on_message(char *buff, int size);
        Type type; // public for simplicity
private:
        template <typename T>
        void dispath_message(SSLsocket<T> *sock, char *buff, int size);

        int fd;
};

template <typename T>
class SSLsocket
{
public:
        void send(char *buff, int size)
        {
                std::cout << "SSLsocket send" << std::endl;
                socket.send(buff, size);
        }

private:
        friend Socket;
        void on_message(char *buff, int size)
        {
                std::cout << "SSLsocket on_message" << std::endl;
                reinterpret_cast<T *>(this)->on_message(buff, size);
        }

private:
        Socket socket;
};

template <typename T>
class websocket : public SSLsocket<websocket<T>>
{
public:
        void send(char *buffer, int size)
        {
                std::cout << "websocket send" << std::endl;
                SSLsocket<websocket<T>>::send(buffer, size);
        }

private:
        friend SSLsocket<websocket<T>>;
        void on_message(char *buff, int size)
        {
                std::cout << "websocket on_message" << std::endl;
                reinterpret_cast<T *>(this)->on_message(buff, size);
        }
};

class whatever : public websocket<whatever>
{
public:
        void send(char *buffer, int size)
        {
                std::cout << "whatever send" << std::endl;
                websocket<whatever>::send(buffer, size);

                // MUST BE IMPOSSIBLE
                // SSLsocket<websocket<whatever>>::send(buffer, size);
        }

private:
        friend websocket<whatever>;
        void on_message(char *buff, int size)
        {
                std::cout << "whatever on_message" << std::endl;
                send(nullptr, 0);
        }
};

void Socket::on_message(char *buff, int size)
{
        std::cout << "socket on_message" << std::endl;
        switch (type) {
        case Type::whatever: {
                auto *w = reinterpret_cast<whatever *>(this);
                dispath_message(w, buff, size);
                break;
        }
        }
}

template <typename T>
void Socket::dispath_message(SSLsocket<T> *sock, char *buff, int size)
{
        sock->on_message(buff, size);
}


int main()
{
        // There are no members in the classes so to simplify the example 
        // we only build the socket class which will be cast later.
        Socket sock;
        sock.type = Type::whatever;
        sock.on_message(nullptr, 0);
}

The goal is to replace callbacks by direct calls.

The Socket type will have to know all the final types (like our whatever type) to do the first cast. This is not a recommended practice but there will be very few additions and there is a way to make a clean file with lots of warnings so that every edition is done carefully.

The main problem is that the whatever class can directly call the send function of the SSLsocket class.

Pipeline:

The on_message function starts from the Socket class to the final class.

The send function goes from the final class to the Socket class.

CodePudding user response:

You could declare print as private in the base class and make class Subtest a friend:

class Test
{
  friend class Subtest;

private:
  void print() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};

class SubTest : public Test
{
public:
  void print() { Test::print(); }
};

class SubSubtest : public SubTest
{
public:
  void print() { Test::print(); }
};

This does, however, have the disadvantage that Subtest can access all the private members of the base class. That would be soluble if you weren't using CRTP, but as you are it probably isn't. So, as @paolo says, please post your CRTP code and I'll see if anything can be done.

CodePudding user response:

In this instance, you can have SubTest inherit from Test privately, which in turn means that SubTest can see all of Test's public and protected members, and re-exports them as private. This means that SubSubTest will not be able to see the members exposed from Test.

class Test
{
protected:
  void print() { std::cout << __PRETTY_FUNCTION__ << std::endl; }
};

class SubTest : private Test
{
public:
  void print() { Test::print(); }
};

class SubSubtest : public SubTest
{
public:
  void print() { Test::print(); } // failure 
};

Edit

Based on your example code, this solution will not work due to you needing to cast your whatever type to SSLsocket<websocket<whatever>>. In this instance, I would wonder whether CRTP is the right choice for what you're trying to implement; SSLsocket is not enhancing a behaviour of an existing type, but is rather being adapted by other types. In this instance, why not instead treat websocket and whatever as adapters? In that case, your SSLsocket would be a private member variable of websocket hence unavailable to whatever. Effectively something like so:

struct isocket
{
  virtual void send(char *, int) = 0;
  virtual void on_message(char *, int) { }
}

struct fsocket : public isocket
{
  void send(char *data, int length) override { fwrite(fd, data, length); }

private:
  int fd; 
};

template <typename Socket>
struct sslsocket : public isocket
{
  void send(char *data, int length) override { _base.send(...); }
private:
  Socket _base;
};

struct websocket : public isocket
{
  void send(char *data, int length) override { ... };
private:
  sslsocket _base;
};

struct whatever : public isocket
{
  void send(char *data, int length) override { ... };
private:
  websocket _base;
};
  •  Tags:  
  • c
  • Related