#опытным
Допустим, вам нужно написать tcp и udp версии клиента для одной и той же задачи. Можно использовать динамический полиморфизм и переопределить все специфические методы. Но тогда у нас будет оверхед на динамическую диспетчеризацию вызова.
Зачем тратить драгоценные миллинаносекунды, если можно воспользоваться compile-time полиморфизмом, а конкретно паттерном CRTP?
Вот сильно упрощенная и обрезанная версия того, как это может выглядеть:
template <typename Derived>
class ClientBase {
protected:
int sock_ = -1;
sockaddr_in serv_addr_{};
std::string server_ip_;
uint16_t port_;
Derived& derived() { return static_cast<Derived&>(this); }
public:
void send_message(const std::string& message) {
ssize_t bytes_sent = derived().send_impl(message);
if (bytes_sent < 0) {
throw std::runtime_error("Send failed");
}
}
};
class TcpClient : public ClientBase<TcpClient> {
public:
static constexpr int PROTOCOL_TYPE = SOCK_STREAM;
ssize_t send_impl(const std::string& message) {
return send(sock_, message.c_str(), message.length(), 0);
}
};
class UdpClient : public ClientBase<UdpClient> {
public:
static constexpr int PROTOCOL_TYPE = SOCK_DGRAM;
ssize_t send_impl(const std::string& message) {
return sendto(sock_, message.c_str(), message.length(), 0,
reinterpret_cast<sockaddr>(&serv_addr_), sizeof(serv_addr_));
}
};
И меня всегда напрягало, что в любой статье вот эти *_impl методы находятся в публичном интерфейсе наследников. Зачем они там - непонятно и это по сути детали реализации. Надо бы их скрыть.
Но как? База CRTP должна иметь доступ к этим методам.
Вот тут-то мы и используем трюк. Сделаем все члены наследников приватными и дадим базе CRTP доступ к кишкам наследников через friend:
template <typename Derived>
class ClientBase {
// ...
};
class TcpClient : public ClientBase<TcpClient> {
private:
// ...
friend class ClientBase<TcpClient>;
};
class UdpClient : public ClientBase<UdpClient> {
private:
// ...
friend class ClientBase<UdpClient>;
};
И все. Теперь все члены приватные, ClientBase имеет ко всем из них доступ и публичный интерфейс задает только ClientBase, ничего лишнего нет.
Не так много народу знает об этой технике, поэтому решил о ней отдельно рассказать.
Hide your secrets. Stay cool.
#template