Dwie klasy przechowujące wskaźniki na siebie nawzajem.

0

Założenia:
Mam klasę Server, która coś wykonuje(tutaj dodaje 10 do zapytania).
Z klasą Server komunikuję się za pomocą klasy Terminal.
Chciałbym, aby Terminal zawsze był gotowy na nowe zapytania i nie czekał na odpowiedź. Server, po wykonaniu zadania ma powiadomić Terminal i podać mu wynik.
Wymyśliłem sobie, że Terminal będzie przechowywał wskaźnik do Servera, a Server wskaźnik do Terminala.
No i tu pojawia się error: invalid use of incomplete type.
Da się to jakoś obejść nie wprowadzając dodatkowych klas?

#include <iostream>

class Server;

class Terminal{
public:
    void setServer(Server* server){
        server = server;
    }
    void send(int request){
        server->send(request); //tutaj invalid use of incomplete type 'class Server'
    }
    void receive(int response){
        std::cout<<response;
    }
private:
    Server* server;
};

class Server{
public:
    Server(Terminal* terminal){
        this->terminal = terminal;
    }
    void send(int request){
        terminal->receive(request+10);
    }
private:
Terminal* terminal;
};

int main(){
    Terminal* terminal = new Terminal();
    Server* server = new Server(terminal);
    terminal->setServer(server);
}

https://wandbox.org/permlink/x6T5zRxNCNQBIqhb

3

Każda z twoich dwóch klas potrzebuje ** definicji ** tej drugiej, bo wywołuje na niej funkcje jakieś (terminal->receive i server->send). Stąd forward ** deklaracja ** nic nie pomoże. Przenieś każdą z tych klas do osobnego headera, implementacje do cpp. Co więcej, takie circural dependency zawsze powinieneś dobrze przemyśleć, bo zazwyczaj wskazują na nieprzemyślaną architekture.

Swoją drogą, masz inny problem w kodzie.

    void setServer(Server* server){
        server = server; // tutaj oba server są tym samym obiektem - parametrem funkcji. Musisz zrobić this->server = server. server(server) zadziałałoby na liście inicjalizacyjnej ctora
    }
0

@bl4ster: wydahe sie ze wzorzec observer bylby ok(jeśli się mylę proszę o sprostowanie)

ewentualnie komunikacja pi tcp
termibal nasluchuje na porcie a serwer wysyła na port

ogólnie
metoda server send która wywołuje metodę terminal receive, czyli z polskiego na nasze
metoda receive na prawdę wysyła coś a nie odbiera

a metoda server send tak na prawdę wywołuje metodę odbierz na terminalu

nawet w sensie hak sobie to opowiem to sensu nie ma
zresztą spróbuj sobie sam opowiedzieć co się dzieje
wykorzystując nazwy metod i odpowiedz czy Tobie o to chodziło

https://pl.m.wikipedia.org/wiki/Obserwator_(wzorzec_projektowy

zobacz

0

No w tym przykladzie mamy podwójny wzorzec obserwator, bo Terminal jest zarówno observable jak i observe i to samo dotyczy Server.

0

Kolego, poczytaj troche o std::weak_ptr. Masz idealnego kandydata na to.

1

Podziel to na pliki nagłówka i implementacji...

#include <iostream>

class Server;

class Terminal{
public:
    Terminal();
    void setServer(Server& server);
    void send(int request);
    void receive(int response);
private:
    Server* serverPtr;
};

class Terminal;

class Server{
public:
    Server(Terminal& terminal);
    void send(int request);
private:
    Terminal& terminal;
};

Terminal::Terminal(): serverPtr{nullptr} {}

void Terminal::setServer(Server& server_) {
    serverPtr = &server_;
}

void Terminal::send(int request) {
    if(serverPtr) {
        serverPtr->send(request);
    }
}

void Terminal::receive(int response) {
    std::cout << response;
}

Server::Server(Terminal& terminal_): terminal{terminal_} {
}

void Server::send(int request) {
    terminal.receive(request+10);
}
 
int main(){
    Terminal terminal;
    Server server(terminal);
    terminal.setServer(server);
}

Nie odnosząc się do logiki tego co wymyśliłeś, jeśli naprawdę w dużym projekcie będzie taki "klincz wywołań cyklicznych", to są inne rozwiązania...

1

Możesz też stworzyć prosty kanał komunikacyjny :P

struct Message
{
  std::function< void( const Message& ) > response;
  int value;
  bool handled;
};

Terminal preparuje wiadomość:

void Terminal::Send( int request )
{
  Message msg;
  msg.response = std::bind( &Terminal::Receive, this, std::placeholders::_1 );
  msg.value = request;
  msg.handled = false;
  server->Process( msg );
}

Serwer odbiera, przetwarza i odpowiada:

void Server::Process( const Message& msg )
{
  Message resp;
  resp.value = msg.value + 10;
  resp.handled = true;
  msg.response( resp );
}

I terminal otrzymuje odpowiedź:

void Terminal::Receive( const Message& msg )
{
  cout << "Serwer " << msg.handled ? "przetworzyl " : "nie przetworzyl " << "żądania. Zwrócony kod: " << msg.value << "\n";
}

Wtedy wystarczy w terminal.h tylko deklaracja class Server; i include w terminal.cpp.
W server.h definiujesz message i tyle. O terminalu nie trzeba nawet wspominać serwerowi ;)

0

Dzięki za odpowiedzi, najbardziej podoba mi się rozwiązanie @tajny_agent.
Jednak tu chyba jest błąd:

tajny_agent napisał(a):
void Terminal::Receive( const Message& msg )
{
  cout << "Serwer " << msg.handled ? "przetworzyl " : "nie przetworzyl " << "żądania. Zwrócony kod: " << msg.value << "\n";
}

U mnie nie kompiluje się, dostaję error:

/home/lkre/workspace/c++/untitled2/terminal.cpp:20: error: invalid operands of types ‘const char [17]’ and ‘const char [27]’ to binary ‘operator<<’
     std::cout << "Serwer " << msg.handled ? "przetworzyl " : "nie przetworzyl " << "żądania. Zwrócony kod: " << msg.value << "\n";
                                                                                    ^

Ten warunek powinien być chyba w nawiasach:

    std::cout << "Serwer " << (msg.handled ? "przetworzyl " : "nie przetworzyl ")<< "żądania. Zwrócony kod: " << msg.value << "\n";

1 użytkowników online, w tym zalogowanych: 0, gości: 1