0

So i have a project im trying to assemble and do as such im trying to split it into to files, i have a main.cpp and a constants header called client_constants.h and a folder called auxiliar_code with another header (aux_functions.h) and its implementation.

The 1st function of the aux_functions.h (Parse_Args) i use in the main but the remainder i want to use in another file in a different folder, the thing is when i compile the implementation the aux_functions.h alongside the main.cpp i get

/tmp/cc0VjBhg.o:(.bss+0x0): multiple definition of `client'
/tmp/ccRvZrMP.o:(.bss+0x0): first defined here
/tmp/cc0VjBhg.o:(.bss+0x4): multiple definition of `client_tcp'
/tmp/ccRvZrMP.o:(.bss+0x4): first defined here
/tmp/cc0VjBhg.o:(.bss+0x8): multiple definition of `err'
/tmp/ccRvZrMP.o:(.bss+0x8): first defined here
/tmp/cc0VjBhg.o:(.bss+0x20): multiple definition of `hintsClient'
/tmp/ccRvZrMP.o:(.bss+0x20): first defined here
/tmp/cc0VjBhg.o:(.bss+0x60): multiple definition of `hintsClientTCP'
/tmp/ccRvZrMP.o:(.bss+0x60): first defined here
/tmp/cc0VjBhg.o:(.bss+0x90): multiple definition of `resClient'
/tmp/ccRvZrMP.o:(.bss+0x90): first defined here

among other things and i dont get why because i used the header guards in every header and as such there shouldnt be a redefinition of any header and there is.

Am i missing anything or am i doing anything wrong?

main.cpp

#include "client_constants.h"
#include "auxiliar_code/aux_functions.h"
#include <stdlib.h>
#include <string>
#include <iostream>

int main(int argc, char **argv)
{
    std::string DSIP, DSport;  
    ParseArgs(argc, argv,DSIP,DSport);
    exit(EXIT_SUCCESS);
}

client_constants.h:

#ifndef CLIENT_CONSTANTS_H
#define CLIENT_CONSTANTS_H

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#define PORT "58011"
#define GPPORT "58048"
#define MAX_MESSAGE 1048576
#define MAX_LINE 320
#define MAX_BUFFER 1048576
#define MAX_USERS 32

int client,client_tcp, err;
struct addrinfo hintsClient;
struct addrinfo hintsClientTCP;
struct addrinfo *resClient;
struct sockaddr_in addr;
ssize_t n;
socklen_t addrlen;
char hostclient[256]; int hostname;

typedef struct msg{
    char MID[5];
    char UID[6];
    char textsize[11];
    char text[241];
    char fname[26];
    char fsize[11];
    char data[1025];
}msg;

typedef struct
{
    int num_users;
    char GNAME[26],GID[3];
    char UIDS[MAX_USERS][6];
    int num_msgs;
    msg messages[20];
}group;

#endif //client_constants.h

aux_functions.h:

#ifndef AUX_FUNCTIONS_H
#define AUX_FUNCTIONS_H

#include "../client_constants.h"
#include <string>
#include <iostream>

void ParseArgs(int argc, char *argv[],std::string &DSIP,std::string &DSport); //used in main
void printarray(char (*users)[6],int n);
void printmap();

#endif //aux_functions.h

aux_function.cpp:

#include "aux_functions.h"

void ParseArgs(int argc, char *argv[],std::string &DSIP,std::string &DSport)
{
    int option;
    int i = 1;
    while ((option = getopt(argc, argv, "n:p:")) != -1)
    {
        switch (option)
        {
        case 'n':
            i += 2;
            DSIP = std::string(argv[i-1]);
            break;
        case 'p':
            i += 2; 
            DSport = std::string(argv[i-1]);
            break;
        default:
            break;
        }
    }
    
    if (DSIP.size() == 0)
    {   
        hostname = gethostname(hostclient,sizeof(hostclient));
        if (hostname == -1) {
            perror("Error: in gethostname");
            exit(1);
        }
        DSIP = std::string(hostclient);
    }

    if (DSport.size() == 0)
        DSport  = std::string(GPPORT);
}

void print_msgs(msg messages[],int n)
{ 
    for (int i = 0; i < n+1; i++) {
        std::cout << "mid: "<< messages[i].MID << ' ' << "uid: "<< messages[i].UID << ' ' << "textsize: "<< messages[i].textsize << ' ' << "text: "<< messages[i].text << '\n' << "fname: "<< messages[i].fname << ' ' << "fsize: "<< messages[i].fsize << ' ' << "data: "<< messages[i].data << '\n'; 
    }
}

void empty_str(char str[],int len)
{
    for (int i=0; i<len;i++)
        str[i]=0;
}

Compile:

g++ "auxiliar_code"/aux_functions.cpp main.cpp -o user
Martim Correia
  • 483
  • 5
  • 16
  • 3
    You define your global variables (e.g. `client_tcp`) in the header file => they're defined in each translation unit which includes the header. My recommendation is to avoid global variables (e.g. see Эstatic initialization order fiasco" for more complicated examples). If you absolutely want to, define a variable in a single translation unit and _declare_ it in the header like `extern int client_tcp;` – yeputons Jan 05 '22 at 22:28
  • 2
    Header guards prevent multiple inclusions within a single [translation unit](https://en.wikipedia.org/wiki/Translation_unit_(programming)) but cannot protect you from multiple translation units including the same file and duplicating definitions. – user4581301 Jan 05 '22 at 22:28
  • 1
    Does this answer your question? [Multiple definition of first defined here gcc](https://stackoverflow.com/questions/19675685/multiple-definition-of-first-defined-here-gcc) – user17732522 Jan 05 '22 at 22:32
  • Unrelated: C++ learned a lot from C. One of the things it learned is the `typdef struct` trick. This is baked into C++, so all you need to do is `struct group { ... };` and you can subsequently refer only to `group`. – user4581301 Jan 05 '22 at 22:33

1 Answers1

1

Header guards only prevent the same header file from being included more than once in the same translation unit.

Header guards are insufficient to guarantee compliance with the One Definition Rule.

In your header file:

int client,client_tcp, err;

Every one of your .cpp files that #includes this header file will, therefore, define these objects. Keep in mind that #include is logically equivalent to physically inserting the included header file into the .cpp file that includes it.

Your header files will prevent that logical insertion from happening two or more times, so it will happen in every translation unit just once.

That's great, and the end result is: every translation unit (the .cpp file) that includes this header file will define these same objects. This is equivalent to just writing the above in every one of your .cpp files. It doesn't matter that it's in a header file, with header guards. Same thing will happen, the same link failure, if you copy/paste that in every .cpp file that includes this, instead. It's the same thing.

And this violates the One Definition Rule.

And that ultimately results in your link failure.

The correct way to do this is to declare these objects in only one of your .cpp files, and then declare them with the extern linkage in the header file.

This goes for every object that's responsible for the link failure. Every object that's defined in the header file violates the One Definition Rule, and must be fixed the same way.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • The usual guidance is the *define* then in only one of the .cpp files. One of the effects of using `extern` on declarations of variables at file scope is preventing those declarations from being definitions (all definitions *are* actually declarations, but not all declarations are definitions). – Peter Jan 06 '22 at 00:42