Home > Blockchain >  C linking error: duplicate symbol for different source file
C linking error: duplicate symbol for different source file

Time:10-09

If two variables as same type and name in different source files, the linker command failed with duplicate symbol error, although two source files are not related.

This is my project structure.

CMakeLists.txt

cmake_minimum_required(VERSION 3.0.0)
project(duplicate-symbol-test VERSION 0.1.0)

add_executable(duplicate-symbol-test main.cpp a.hpp a.cpp b.hpp b.cpp)
target_compile_features(duplicate-symbol-test PUBLIC cxx_std_17)

a.hpp

#pragma once

struct A{
    int getRandomNumber() const;
};

a.cpp

#include "a.hpp"

#include <random>

std::random_device rd;
std::mt19937 gen { rd() };
std::uniform_int_distribution<int> dis { 0, 100 };

int A::getRandomNumber() const{
    return dis(gen);
}

b.hpp

#pragma once

struct B{
    int getRandomNumber() const;
};

b.cpp

#include "b.hpp"

#include <random>

std::random_device rd;
std::mt19937 gen { rd() };
std::uniform_int_distribution<int> dis { 0, 100 };

int B::getRandomNumber() const{
    return dis(gen);
}

main.cpp

#include <iostream>
#include "a.hpp"
#include "b.hpp"

int main(int, char**) {
    A a;
    std::cout << a.getRandomNumber() << std::endl;
    B b;
    std::cout << b.getRandomNumber() << std::endl;

    return 0;
}

When build, the cmake build system shows,

[main] Building folder: duplicate-symbol-test

[build] Starting build

[proc] Executing command: /opt/homebrew/bin/cmake --build /Users/admin/Desktop/duplicate-symbol-test/build --config Debug --target all --

[build] [1/2 50% :: 0.661] Building CXX object CMakeFiles/duplicate-symbol-test.dir/b.cpp.o

[build] [2/2 100% :: 0.742] Linking CXX executable duplicate-symbol-test

[build] FAILED: duplicate-symbol-test

[build] : && /opt/homebrew/opt/llvm/bin/clang -g -arch arm64 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX12.3.sdk -Wl,-search_paths_first -Wl,-headerpad_max_install_names CMakeFiles/duplicate-symbol-test.dir/main.cpp.o CMakeFiles/duplicate-symbol-test.dir/a.cpp.o CMakeFiles/duplicate-symbol-test.dir/b.cpp.o -o duplicate-symbol-test && :

[build] duplicate symbol '_dis' in:

[build] CMakeFiles/duplicate-symbol-test.dir/a.cpp.o

[build] CMakeFiles/duplicate-symbol-test.dir/b.cpp.o

[build] duplicate symbol '_gen' in:

[build] CMakeFiles/duplicate-symbol-test.dir/a.cpp.o

[build] CMakeFiles/duplicate-symbol-test.dir/b.cpp.o

[build] duplicate symbol '_rd' in:

[build] CMakeFiles/duplicate-symbol-test.dir/a.cpp.o

[build] CMakeFiles/duplicate-symbol-test.dir/b.cpp.o

[build] ld: 3 duplicate symbols for architecture arm64

[build] clang-14: error: linker command failed with exit code 1 (use -v to see invocation)

[build] ninja: build stopped: subcommand failed.

[proc] The command: /opt/homebrew/bin/cmake --build /Users/admin/Desktop/duplicate-symbol-test/build --config Debug --target all -- exited with code: 1 and signal: null

[build] Build finished with exit code 1

How should I solve it?

CodePudding user response:

How should I solve it?

By making all symbols local to their translation unit. Global non-const variables have by default external linkage, thus two variables with the same qualified name clash during linking.

This can be fixed by either:

  • declaring them static - forcing internal linkage.
  • putting them into an anonymous namespace - ensure unique qualified name.

I recommend the second option as it works for types too:

namespace{
    std::random_device rd;
    std::mt19937 gen { rd() };
    std::uniform_int_distribution<int> dis { 0, 100 };
}
int B::getRandomNumber() const{
    return dis(gen);
}

Unrelated to the issue but there is usually no reason to keep rd around, std::mt19937 gen { std::random_device{}() }; works too. Just be warned that random_device can be deterministic and produce the same sequence each time, check with your implementation.

  • Related