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.