user1413793
user1413793

Reputation: 9347

Link Error: Duplicate Symbol

I have 4 source files listed below:

//a.h

#pragma once

namespace proj {
class A {} a;
} // namespace proj

//b.h

#pragma once

namespace proj {
int foo();
} // namespace proj

// b.cpp

#include "proj/a.h"

namespace proj {
int foo() {
  A b = a;
  return 0;
}
} // namespace proj

// c.cpp

#include "proj/a.h"
#include "proj/b.h"

using namespace proj;

int main() {
  A b = a;
  foo();
  return 0;
}

When I try to compile c.cpp I get the following link error:

duplicate symbol proj::a      in:
    buck-out/gen/proj/c#compile-c.cpp.ob5f76e97,default/c.cpp.o
    buck-out/gen/proj/b#default,static/libb.a(b.cpp.o)
duplicate symbol ___odr_asan._ZN4proj1aE in:
    buck-out/gen/proj/c#compile-c.cpp.ob5f76e97,default/c.cpp.o
    buck-out/gen/proj/b#default,static/libb.a(b.cpp.o)
ld: 2 duplicate symbols for architecture x86_64
collect2: error: ld returned 1 exit status

Build failed: Command failed with exit code 1.
stderr: duplicate symbol proj::a      in:
    buck-out/gen/proj/c#compile-c.cpp.ob5f76e97,default/c.cpp.o
    buck-out/gen/proj/b#default,static/libb.a(b.cpp.o)
duplicate symbol ___odr_asan._ZN4proj1aE in:
    buck-out/gen/proj/c#compile-c.cpp.ob5f76e97,default/c.cpp.o
    buck-out/gen/proj/b#default,static/libb.a(b.cpp.o)
ld: 2 duplicate symbols for architecture x86_64
collect2: error: ld returned 1 exit status

I assume this is happening because b.cpp is compiled independently from c.cpp and so the pre-processor includes the header a.h in each file individually and when it comes time to link, the linker finds two versions of the symbol a.

How do I declare one instance of a class (in this case a) that is usable across my entire program and avoid the above link error?

Reference

I am using gcc-7 (gcc-7 (Homebrew GCC 7.2.0_1) 7.2.0) on Mac OS X 10.13.3 with -std=c++17.

Build system

This should be irrelevant to the question, but including anyways in case someone finds it helpful.

I am using buck to compile the code (although this should be irrelevant) with the following BUCK file:

cxx_library(
    name='a',
    exported_headers=['a.h'],
    visibility=['PUBLIC'],
)

cxx_library(
    name='b',
    exported_headers=['b.h'],
    srcs = ['b.cpp'],
    deps = [':a'],
    visibility=['PUBLIC'],
)

cxx_binary(
    name='c',
    srcs = ['c.cpp'],
    deps = [':a', ':b'],
)

Upvotes: 3

Views: 11172

Answers (2)

user9212993
user9212993

Reputation:

For pre c++17 standard release solutions:1

The reason for the linker error is quite obvious

//a.h

#pragma once

namespace proj {
    class A {} a; // Declares proj::A proj::a implicitly as an instance
                  // everywhere a.h is included.
                  // Thus the linker gets confused which one to use primarly.
}

Alternative 1 (one single instance):

How do I declare one instance of a class (in this case a) that is usable across my entire program and avoid the above link error?

// a.h
#pragma once

namespace proj {
    class A {};
    extern A a;
}

// a.cpp
#include "a.h"

namespace proj {
    proj:A a;
}

Alternative 2 (an instance per translation unit):

// a.h
#pragma once

namespace proj {
    class A {};
}

// b.cpp   
#include "a.h"

namespace { // <<< unnamed (aka anonymous) namespace
            //     privately visible for translation unit
    proj:A a;
}

// c.cpp   
#include "a.h"

namespace {
    proj:A a;
}

1)Otherwise probably @Barry's answer applies best.

Upvotes: 1

Barry
Barry

Reputation: 304142

Since this is tagged C++17, you can take advantage of the new inline variables language feature:

namespace proj {
    class A {};
    inline A a;
} // namespace proj

inline variables now behave the same way as inline functions: your multiple definitions of a get collapsed into one.

Upvotes: 5

Related Questions