Reputation: 1680
Using g++, when two compilation units "a1.o" and "a2.o" both define and use the same weak symbol, the linker will silently resolve to the first occurrence of the symbol wherever it is used. As a result, the behavior of the application will depend on the order of the object files on the linker command line. What can be done to ensure that these symbols are resolved locally to each compilation unit?
For instance, as a minimalist example, if I have the following source files:
a1.cpp:
#include <iostream>
struct A
{
void foo() {std::cerr << __FILE__ << std::endl;}
};
void bar1() {A a; a.foo();}
a2.cpp:
#include <iostream>
struct A
{
void foo() {std::cerr << __FILE__ << std::endl;}
};
void bar2() {A a; a.foo();}
main.cpp:
void bar1();
void bar2();
int main()
{
bar1();
bar2();
}
and compile them with:
for i in a1 a2 main ; do g++ -c -o $i.o $i.cpp ; done
The output will depend on the relative position of a1.o and a2.o on the linker command line:
g++ -o main main.o a{1,2}.o ; ./main
a1.cpp
a1.cpp
g++ -o main main.o a{2,1}.o ; ./main
a2.cpp
a2.cpp
I'd like to get the same result as if using the '-fno-weak' command line option:
for i in a1 a2 main ; do g++ -fno-weak -c -o $i.o $i.cpp ; done
g++ -o main main.o a{1,2}.o ; ./main
a1.cpp
a2.cpp
but '-fno-weak' seems to lead to other complications. What are the alternatives (besides not inlining and fixing collisions)?
For those wondering what could be a typical use case: when writing mock components it is sometimes convenient to have header-only implementations. Different test fixtures end-up having different mock implementations of the same component type, which becomes an issue when all the fixtures are linked into a single test runner.
Upvotes: 2
Views: 766
Reputation: 206607
You asked:
What are the alternatives (besides not inlining and fixing collisions)?
Use local namespace
s or anonymous namespace
s.
a1.cpp:
#include <iostream>
namespace A1_namespace
{
struct A
{
void foo() {std::cerr << __FILE__ << std::endl;}
};
}
using namespace A1_namespace;
void bar1() {A a; a.foo();}
or
#include <iostream>
namespace
{
struct A
{
void foo() {std::cerr << __FILE__ << std::endl;}
};
}
void bar1() {A a; a.foo();}
Make similar changes to a2.cpp.
Upvotes: 4