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;
};