anderkve / FYS3150

https://anderkve.github.io/FYS3150
26 stars 14 forks source link

Error message during linking #45

Closed JohanCarlsen closed 1 year ago

JohanCarlsen commented 2 years ago

Hello, again. I'm getting an error message that I can’t seem to figure out what means. It says duplicate symbol '_l' in: /var/folders/fd/gvc1898s16bcdlf5sq87j_kw0000gn/T/project_2-08497a.o /var/folders/fd/gvc1898s16bcdlf5sq87j_kw0000gn/T/source-588e01.o duplicate symbol '_k' in: /var/folders/fd/gvc1898s16bcdlf5sq87j_kw0000gn/T/project_2-08497a.o /var/folders/fd/gvc1898s16bcdlf5sq87j_kw0000gn/T/source-588e01.o ld: 2 duplicate symbols for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) What does the duplicate mean?

anderkve commented 2 years ago

Hi @JohanCarlsen!

First, here's a StackOverflow post about this type of problem: https://stackoverflow.com/a/18204117

I'll try to explain it in more detail below.

A "duplicate symbol" or "multiple definition" linker error typically appears if a variable or function is defined (not only declared) in a header file (.hpp file), which is then included into multiple source files (.cpp files). Say you have a function func that you have defined entirely in a header file:

my_header.hpp:

void func()
{
  // Here's the function body, so this is a function *definition*
}

Now say you include this header in two separate cpp files:

file_1.cpp:

#include "my_header.hpp"

// Some code here

file_2.cpp:

#include "my_header.hpp"

// Some other code here

Each of the cpp files will compile just fine, i.e. the command g++ -c file_1.cpp will successfully produce the object file file_1.o, and the command g++ -c file_2.cpp will successfully produce file_2.o. But since the full definition of the function func was included in both cpp files, both file_1.o and file_2.o now contain this definition.

So, at the last stage of building, when the object files are linked (the command g++ file_1.o file_2.o), you get a linker error saying that you have duplicate symbols or multiple definitions. The problem is that if some part of your code calls the function func, the linker can't know if that means the func from file_1.o or the func from file_2.o. (The same problem would of course occur if you didn't have a header file, but simply copy-pasted the function definition into both cpp files.)

The solution is to only declare the function in my_header.hpp

my_header.hpp:

void func();

and add a new cpp file my_header.cpp with the full definition:

my_header.cpp:

#include "my_header.hpp"

void func()
{
  // Here's the function body, so this is a function *definition*
}

Now when you compile all three cpp files, you end up with the three object files file_1.o, file_2.o and my_header.o. They all contain the declaration of func (via my_header.hpp) -- that is, each object file knows that a function called func exists somewhere -- but only my_header.o contains the full definition. So there is no ambiguity as to what a call to func means.

Above I used the example of multiple definitions of a function. If your code uses "global variables", that is, variables that live outside all functions, you can get a similar issue. (In general there are very few situations where you actually need to have global variables -- in most cases you get better code design by avoid using global variables.) As the StackOverflow post details, for global variables a line like

double x;

counts as a definition. If you instead want to just declare a global variable you can use

extern double x;

which effectively says that "the variable x is external -- it's definition will be provided through one of the other object files during linking". But again, if you find yourself defining variables outside functions (or classes), it may be worth reconsidering the code design.

Hope this helps!

JohanCarlsen commented 2 years ago

Wow, thank you so much for that informative answer! =) Well, I have a src file with the definitions of the functions from the code snippets. In the function jacobi_eigensolver I need to call the function jacobi_rotate. Here I have to send in k and l, right, so there variables need to be declared beforehand. But if I should not have them as global variables, how should I do it?

Now, I have this in my header file:

`// Include guard

ifndef __header_hpp__

define __header_hpp__

include

include

include

include

// header file to be included in the main program project_2.cpp // declaring functions defined in source.cpp

double max_offdiag_symm(const arma::mat& A, int& k, int& l); void jacobi_rotate(arma::mat& A, arma::mat& R, int k, int l); void jacobi_eigensolver(const arma::mat& A, double eps, arma::vec& eigenvalues, arma::mat& eigenvectors, const int maxiter, int& iterations, bool& converged);

int k; int l;

// End of include gurad

endif`

Here, I can see that I have defined k and l. But they are not defined anywhere else, only used in the source file.

JohanCarlsen commented 2 years ago

Oh my god, I fixed it! First off, the definition of k and l happened, as you said, in the header file. So, since the source file included this, and the main file also included this, does this mean that the variables were defined in both the source and the main file?

Also, I had defined them as type double, which obviously does not match with the arguments of the functions.

Now the program builds perfectly! Thank you again, so much for the good answer!

anderkve commented 2 years ago

Wow, thank you so much for that informative answer! =)

:)

Oh my god, I fixed it!

Great stuff!

First off, the definition of k and l happened, as you said, in the header file. So, since the source file included this, and the main file also included this, does this mean that the variables were defined in both the source and the main file?

Yep!

To help others that might encounter a similar issue, I'll reply to your original follow-up question:

In the function jacobi_eigensolver I need to call the function jacobi_rotate. Here I have to send in k and l, right, so there variables need to be declared beforehand. But if I should not have them as global variables, how should I do it?

The variables k and l are only needed inside the function jacobi_eignesolver, or as input to the functions jacobi_rotate or max_offdiag_symmetric, which are themselves only called from inside jacobi_eignesolver. So that means that k and l can be defined inside the jacobi_eigensolver function, i.e. as "local variables" for this function. So part of your jacobi_eigensolver function should probably look something like this:

  \\ Some clever code... 

  int k = 0;
  int l = 0;
  double max_offdiag = 0.0;

  \\ More clever code... 

  \\ At some point it's time to call max_offdiag_symmetric, 
  \\ which will assign values to k and l, and return a value that 
  \\ we write to our variable max_offdiag:

  max_offdiag = max_offdiag_symmetric(A, k, l);

  \\ Yet more clever code... and at some point it's time to 
  \\ call jacobi_rotate, which will modify our two matrices A and R:

  jacobi_rotate(A, R, k, l);

  \\ Yet more clever code...