lord.didger
lord.didger

Reputation: 1407

mysterious linker error

I have written a simple program that use some classes and procol buffers. These classes are to make connecting and sending messages between computers easier. Compilation succeeded. However, linker says:

server.o: In function `main':

server.cpp:(.text+0x24): undefined reference to `dataExchange::Server::Server(unsigned int)'

server.cpp:(.text+0x39): undefined reference to `dataExchange::Server::accept()'

server.cpp:(.text+0x51): undefined reference to `dataExchange::Connection::receive()' collect2: ld returned 1 exit status

The code: server.cpp:

#include <iostream>

#include "connection.h"
#include "protocol.pb.h"

using namespace std;
using namespace msg;
using namespace dataExchange;

int main() {
  Server server(1234);
  while(1) {
    Connection con = server.accept();
    Annoucement ann = con.receive();
    cout << ann.typ() << endl;
  }
}

connection.h:

namespace dataExchange {

  class SocketMaintenance {
  protected:
    sockaddr_in addr;
    int s;
  public:
    SocketMaintenance(const unsigned int) throw();
    SocketMaintenance(const sockaddr_in, const int) throw();
    SocketMaintenance(const SocketMaintenance&) throw();
    ~SocketMaintenance();
    void write(const char*, const int);
  };

  class Connection {
    FileOutputStream* raw_output;
    FileInputStream* raw_input;
    char buffer[1024];
  public:
    Connection(const char*, const unsigned int);// throw(WrongAddress);
    Connection(const SocketMaintenance&);//throw(ConnectFailed);
    void send(const Message&) throw();
    Annoucement receive() throw();
  };

  class Server {
  public:
    Server(const unsigned int);// throw(BindFailed,ListenFailed);
    Connection accept() throw();
  };

}

What is the reason the linker fails?


procedure of building:

g++ server.cpp -c; g++ protocol.pb.cc -c; g++ connection.cpp -c; g++ server.o protocol.pb.o connection.o -lprotobuf

conection.cpp

using namespace std;
using namespace msg;
using namespace google::protobuf;
using namespace google::protobuf::io;

namespace dataExchange {

  class SocketMaintenance {
  protected:
    sockaddr_in addr;
    int s;
  public:
    SocketMaintenance(const unsigned int port) {
      memset(&addr, 0, sizeof(addr));
      addr.sin_family = AF_INET;
      addr.sin_port = htons(port);
      s = socket(AF_INET, SOCK_STREAM, 0);
    }
    SocketMaintenance(const sockaddr_in Addr, const int Socket) {
      addr = Addr;
      s = Socket;
    }
    SocketMaintenance(const SocketMaintenance& source) {
      addr = source.addr;
      s = source.s;
    }
    ~SocketMaintenance() {
      close(s);
    }
    void write(const char* buffer, const int n) {
      ::write(s, buffer, n);
    }
  };

  class Connection : public SocketMaintenance {
    FileOutputStream* raw_output;
    FileInputStream* raw_input;
    char buffer[1024];
  public:
    Connection(const char* IP, const unsigned int port) : SocketMaintenance(port) {
      if(inet_pton(AF_INET, IP, &addr.sin_addr)<=0) {
    throw WrongAddress();
      }
      if(connect(s, (sockaddr*)&addr, sizeof(addr))<0) {
    throw ConnectFailed();
      }
      raw_output = new FileOutputStream(s);
      raw_input = new FileInputStream(s);
    }
    Connection(const SocketMaintenance& source) : SocketMaintenance(source) {}
    ~Connection() {
      delete raw_output;
    }
    void send(const Message& msg) throw(EmptyMessage)  {
      CodedOutputStream* coded_output = new CodedOutputStream(raw_output);
      int n = msg.ByteSize();
      if(n<=0) throw EmptyMessage();
      coded_output->WriteVarint32(n);
      delete coded_output;
      raw_output->Flush();
      msg.SerializeToArray(buffer, n);
      SocketMaintenance::write(buffer, n);
    }
    Annoucement receive() throw() {
      CodedInputStream* coded_input = new CodedInputStream(raw_input);
      google::protobuf::uint32 n;
      coded_input->ReadVarint32(&n);
      char *b;
      int m;
      coded_input->GetDirectBufferPointer((const void**)&b, &m);
      Annoucement ann;
      ann.ParseFromArray(b, n);
      return ann;
    }
    
  };

  class Server : public SocketMaintenance {
  public:
    Server(const unsigned int port) : SocketMaintenance(port) {
      addr.sin_addr.s_addr = htonl(INADDR_ANY);
      if(bind(s, (struct sockaddr *)&addr, sizeof(addr))<0) {
    throw BindFailed();
      }
      if(listen(s, 5)<0) {
    throw ListenFailed();
      }
    }
    Connection accept() throw() {
      sockaddr_in client;
      socklen_t client_len;
      int client_socket = ::accept(s, (sockaddr*)&client, &client_len);
      return Connection(SocketMaintenance(client, client_socket));
    }
  };

}

Upvotes: 1

Views: 519

Answers (3)

Xeo
Xeo

Reputation: 131829

Forgot to compile and/or link the "connection.cpp", most likely. I atleast hope you have such a file, which implements all the functions?


I think you have some fundamental misunderstandings how the splitting in .h and .cpp works. In the .cpp, you do not simple redeclare the classes, this time with the functions defined inside of them. You need to do like this:

// connection.cpp
namespace dataExchange{
// define the constructor of Server
Server::Server(const unsigned int port)
  : SocketMaintenance(port)
{
  // ....
}

// define the accept function of Server
Connection accept() throw(){
  // ...
}
// ...
}

But this is only part of the problem, it seems you need to get a good C++ book. For example, you need to make any inheritence in the header that declares the class.

Upvotes: 4

NPE
NPE

Reputation: 500713

When you #include "connection.h", you get the declarations of Server's methods. This basically tells the compiler what methods exist, and enables the compilation to succeed.

In order to link your executable, you need to link against the definitions of those methods (i.e. the actual code that implements them). For classes defined in connection.h, these would typically reside in a file named connection.cpp or similar. You need to compile that file, and specify the resulting object file during the link step.

Upvotes: 1

karlphillip
karlphillip

Reputation: 93468

You should have a connection.cpp on your project that implements the constructor Server::Server(const unsigned int). Do you have it?

You can also help yourself and paste the command your are issuing to compile, you could be missing a file for linkage.

Upvotes: 0

Related Questions