struct EventListener
{
virtual void ClientEvent(...) = 0;
virtual void RecvEvent(...) = 0;
};
class Client
{
EventListener* m_eventListener;
public:
Client() : m_eventListener(0) {}
explicit Client(EventListener &eventListener) : m_eventListener(&eventListener) {}
void eventListener(EventListener &eventListener) {m_eventListener=&eventListener;}
...
protected:
void FireClientEvent() {if(m_eventListener)m_eventListener->ClientEvent(...);}
void FireRecvEvent() {if(m_eventListener)m_eventListener->RecvEvent(...);}
};
class Server : public EventListener
{
std::list<Client*> m_Clients;
public:
void addClient(std::auto_ptr<Client> client) {client->eventListener(*this);m_Clients.push_back(client.release());}
};
class ServerEx : public Server
{
...
};
Здравствуйте, в программировании я новичок, как решить свою проблему догадываюсь, но не уверен что это решение будет правильным и в стиле c++.
Есть класс сервера, в котором храниться список ссылок на клиентов. Когда клиент принимает информацию от устройства, эта информация должна передаваться через вызов функции HandleRecvData() в класс сервера, где и будет обработана. Адрес функции HandleRecvData(), которая сейчас является статическим методом сервера, я передаю клиенту через конструктор.
Но теперь возникла проблема, мне нужно от сервера наследовать класс, и в нем переопределить функцию HandleRecvData().
Определить ее как виртуальную — не получается, компилятор начинает грязно ругаться. вот теперь сижу и думаю, как это все грамотно "отрефакторить"
typedef void (HandleDataFunc)( const string, const char*, const int);
// класс клиента
class AMClient
{
protected:
HandleDataFunc* HandleRecvData;
public:
AMClient( SOCKET sSocket, sockaddr_in socket_addr, HandleDataFunc* HandleDataCallback )
{
HandleClientEvent = HandleEventCallback;
HandleRecvData = HandleDataCallback;
}
};
// класс сервера
class AMServer
{
protected:
list<AMClient*> mClientList;
static void HandleRecvData( const string ClientName, const char *pData, const int Size );
AMClient* CreateClient( SOCKET sSocket, sockaddr_in socket_addr );
public:
AMServer( const string& pIPAddrStr, unsigned short PortNumber );
virtual ~AMServer();
};
AMClient* AMServer::CreateClient( SOCKET sSocket, sockaddr_in socket_addr )
{
return new AMClient( sSocket, socket_addr, HandleRecvData );
}
Единственное что сейчас приходит на ум — сделать функцию CreateClient() виртуальной, и в классе наследнике переопределить ее как-то так:
class AMOpcServer : public AMServer
{
private:
static void HandleRecvData2( const string ClientName, const char *pData, const int Size );
virtual AMClient* CreateClient( SOCKET sSocket, sockaddr_in socket_addr );
};
AMClient* AMServer::CreateClient( SOCKET sSocket, sockaddr_in socket_addr )
{
return new AMClient( sSocket, socket_addr, HandleRecvData2 );
}
Но выглядит это совсем ужасно. Ну и собственно вопрос, как сделать это все "in da c++ style"
Здравствуйте, A.Lokotkov, Вы писали:
AL>Определить абстрактный класс (интерфейс), в котором определена чисто виртуальная функция, соответствующая коллбэку. Сервер будет реализовывать сей интерфейс и при создании клиента передавать тому this вместо адреса функции.
то есть что-то вроде паттерна
Strategy, я правильно понимаю?
Здравствуйте, neherim, Вы писали:
N>то есть что-то вроде паттерна Strategy, я правильно понимаю?
Не мучьте себя сопоставлением с паттернами. В ответе ниже показан вариант реализации.
Здравствуйте, Анатолий Широков, Вы писали:
АШ> void addClient(std::auto_ptr<Client> client) {client->eventListener(*this);m_Clients.push_back(client.release());}
Небольшая поправка:
m_Clients.push_back(client.get());
client.release();
Либо использовать ptr_list из буста.
И еще деструктор EventListener стоит сделать protected.