Apologizing ahead of time for what I'm certain is a very simple problem, but I've been unable to find a solution for this. I'm putting a very simple C++
project together, but having trouble using a header file appropriately.
The directory structure is below:
.
├── mainGraph
│ └── hnsw.cpp
└── utils
├── distances.cpp
└── distances.h
The distances header file (distances.h
):
#ifndef DISTANCES
#define DISTANCES
#include <vector>
enum class Metric { Cosine, Euclidean};
double distance(const std::vector<double>& A, const std::vector<double>& B, Metric metric);
double similarity(const std::vector<double>& A, const std::vector<double>& B, Metric metric);
#endif
And finally hnsw.cpp
:
#include "../utils/distances.h"
#include <vector>
#include <iostream>
int main(){
std::vector<double> A = {0.1, 0.5, 0.7, 1.1};
std::vector<double> B = {0.5, 0.3, 0.8, 0.9};
std::cout << distance(A, B, Metric::Cosine) << '\n';
}
Now, when I actually try to compile hnsw.cpp
with the following command: g++ hnsw.cpp --std=c++11
I get the following error:
Undefined symbols for architecture x86_64:
"distance(std::__1::vector<double, std::__1::allocator<double> > const&, std::__1::vector<double, std::__1::allocator<double> > const&, Metric)", referenced from:
_main in hnsw-ad0e05.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
If I move the main into the distances.cpp
file then everything works smoothly. For the purposes of brevity I'm not posting that whole file as it's sizable and I'm fairly certain it's not relevant. If there are any resources about understanding the general process here that would also be very helpful.
If you don't want to build individual object files for later linking, but instead build the whole executable file in one go, you need to specify all the participating translation units on the command line:
g++ -std=c++11 hnsw.cpp ../utils/distances.cpp
The output file will be called a.out
, unless you also override that name with the -o
flag.
Usually you would compile translation units separately and link them in a separate step:
g++ -std=c++11 mainGraph/hnsw.cpp -c -o mainGraph/hnsw.o
g++ -std=c++11 utils/distances.cpp -c -o utils/distances.o
g++ -std=c++11 mainGraph.hnsw.o utils/distances.o -o myprog
You also wouldn't maintain these commands by hand, but stick them into a Makefile, or some equivalent build system. The key point is that you don't need to recompile translation units that haven't changed just because you changed some part of your source code.
When you run g++ hnsw.cpp --std=c++11
, you're telling the compiler to compile hnsw.cpp and link the output with the c++ standard library to create an executable. The linker fails to find the implementation of distances
because it lives in distances.cpp.
You need to compile both files and link them together. There are many ways to achieve this, but the simplest is to specify both source files when invoking gcc, like this:
g++ -std=c++11 hnsw.cpp ../utils/distances.cpp
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With