Search code examples
c++headerduplicatesincludesymbols

Duplicate symbol --- Difference between definition and declaration


Problem

I'm new to C++, and I'm doing a homework problem that plots various distributions in the console, but my project won't compile. When I had everything in the same file, it all worked fine and dandy, but my professor wants us to split things up into an hpp file, a cpp file and a main file. I just moved everything over and now it won't compile due to a duplicate symbol error.

Definition vs. Declaration

I understand that you want to only include declarations of functions in your header file, so I added those but kept the actual definitions of the functions in the cpp file. I have an include statement in the cpp file which is including the header file and another one in main.cpp which including the cpp file.
I understand that my problem may have something to do with my include statements, but I'm lost on how to fix it.

distributions.hpp

This file is the class definition and the declarations of the four functions I wrote

#include <iostream>

class DistributionPair
{
  public:
    DistributionPair(std::uint32_t minValue, std::uint32_t maxValue) :
        minValue(minValue),
        maxValue(maxValue),
        count(0)
    {
    }

    std::uint32_t minValue;
    std::uint32_t maxValue;
    std::uint32_t count;
};

std::vector<DistributionPair> generateUniformDistribution(std::uint32_t, std::uint32_t, std::uint32_t, std::uint8_t);
std::vector<DistributionPair> generateNormalDistribution(std::uint32_t, float, float, std::uint8_t);
std::vector<DistributionPair> generatePoissonDistribution(std::uint32_t, std::uint8_t, std::uint8_t);
void plotDistribution(std::string, const std::vector<DistributionPair>&, const std::uint8_t);

distributions.cpp

This file contains the rest of the definitions of those four functions

#include <iostream>
#include <iomanip>
#include <random>
#include "distributions.hpp"

std::vector<DistributionPair> generateUniformDistribution(std::uint32_t howMany, std::uint32_t min, std::uint32_t max, std::uint8_t numBins)
{......

main.cpp

This is the main function that plots the distributions

#include <iostream>
#include "distributions.cpp"

int main()
{......

Error Messages

The following is the error message that I get when I'm compiling my code:

duplicate symbol 'generateUniformDistribution(unsigned int, unsigned int, unsigned int, unsigned char)' in:
    /Users/ryanegbert/Desktop/c++/assign2/build/RandDistributions.build/Debug/RandDistributions.build/Objects-normal/x86_64/distributions.o
    /Users/ryanegbert/Desktop/c++/assign2/build/RandDistributions.build/Debug/RandDistributions.build/Objects-normal/x86_64/main.o
..........

ld: 4 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

If I use an #include "distributions.hpp" then I get a different compile error:

/Users/ryanegbert/Desktop/c++/assign2/main.cpp:6:20: error: implicit instantiation of undefined template 'std::__1::vector<DistributionPair, std::__1::allocator<DistributionPair> >'
    auto uniform = generateUniformDistribution(100000, 0, 79, 40);

since my functions are being defined in distributions.cpp.

I am using Xcode v11.0 on Mac.

Does anyone have any help on why I may be getting this compile errors?


Solution

  • Please note it is a linker error, not a compiler one.

    Think the executable building as a two-stage process.

    • On first stage, the compiler reads source code and generates .o files.
    • On second stage, the linker reads .o (and other) files and generates executable files.

    The .o files have both binary code and symbol references. The compiler generates two kind of things: binary code with the functions defined, and symbol references with the functions declared but not defined. The linkers task is to solve all references, i.e. associate all symbol references with binary code, to build a complete executable file.

    What seems to happen in your example is that your building scripts are doing the following:

    • Compile 'main.cpp' + 'distributions.cpp' (included) into 'main.o'
    • Compile 'distributions.cpp' into 'distributions.o'
    • Link together 'main.o' + 'distributions.o' into the final executable

    That way, the linker has two copies of the compiled code from distributions.cpp, one in main.o and the other in distributions.o

    To solve it, you have to do one of these:

    • Instruct the linker to only use 'main.o' for creating the executable file

    or,

    • Replace #include "distributions.cpp" by #include "distributions.hpp" so that your main.o file will contain only the symbol references, and the linker will glue together symbols and code from distributions.o into the executable file.