1

Consider a class that belongs to a project linked to dynamic library (foo.dll), with foo.lib generated on the side too:

class IMP_EXP_DIRECTIVE_MACRO Foo
{
     void bar()
     {
         // do something
     }
 };

Then there is also another project bar (generating static library bar.lib) that includes this class. The project does not link with foo.

Finally there's an application project (resulting in .exe), which links with both foo.lib and bar.lib. When it does, linker shouts with LNK2005 error that Foo::bar is already defined in both foo.lib (which is good) and in bar.lib, which is unexpected and weird.

When treating bar.lib with dumpbin.exe /SYMBOLS according to https://learn.microsoft.com/en-us/cpp/build/reference/symbols?view=vs-2017 output shows that Foo::bar is indeed defined in foo.lib (!). That is strange, because according to this brilliant https://stackoverflow.com/a/4955288 answer, any member function that's declared and defined in the body of a class is implicitly inlined, therefore doesn't cause ODR problems.

Finally, the most interesting part. When moving Foo::bar definition to .cpp file and leaving only method's declaration in a class body, problem disappears.

Why is that so? Any idea what is being done wrong? I think I've read the whole internet about this topic and tried all various VS configuration switches, but only moving definition to .cpp helps. In principle I can eventually do it, but it doesn't seem like a proper solution to the issue.

Environment is: Visual Studio 10.

edit:
IMP_EXP_DIRECTIVE_MACRO is set to:
"__declspec(dllexport)", when building foo.dll
"", when building bar

edit 2:
Problem is also gone, when added a preprocessor flag to compiling bar.lib that causes class __declspec(dllimport) Foo usage instead of class Foo.
Is that the right way to go?

Konrad
  • 355
  • 6
  • 18
  • is `IMP_EXP_DIRECTIVE_MACRO` defined the same in both compilation units? – Richard Critten Oct 10 '18 at 10:15
  • When building foo.lib it's set to __declspec(dllexport). When building bar.lib I believe it's set to nothing (""), but I'm having difficulties to verify the value of it. – Konrad Oct 10 '18 at 10:29
  • "implicitly inlined" does not describe reality very well. A C++ member function always has external linkage, even though the optimizer *might* consider generating it inline. #including the .h file in multiple translation units generates multiple definitions of the function. Which is fine, ODR matters to the compiler and not the linker. it is the job of the linker to ditch the duplicates and keep just one of them. The compiler puts them in the COMDAT section, allowing the linker to do this. So seeing it back in both .lib files doesn't help you diagnose the linker error. – Hans Passant Oct 10 '18 at 11:14
  • @HansPassant "ODR matters to the compiler and not the linker", so how come linker reports LNK2005 "already defined"? Isn't that error plainly about ODR? – Konrad Oct 10 '18 at 11:46
  • Well, sure, the compiler can only diagnose ODR violations within in a translation unit. After that it has to be the linker that pulls the emergency stop. Hard to imagine how this helps you get ahead. – Hans Passant Oct 10 '18 at 11:53
  • As the functions are not the same (one is dllexport the other is not). Then if you link both of them into the same program you have an ODR problem and COMDAT folding (at the link stage) will not work as the function are different. – Richard Critten Oct 10 '18 at 12:19
  • @RichardCritten do you suggest that functions declarations shouldn't be mixed in such fashion? How else can I use Foo in bar.lib? – Konrad Oct 11 '18 at 08:08

1 Answers1

2

The function is inline-able.

But if you take its address &Foo::bar, the compiler still needs a real address. That practically means there must be non-inlined code at the address pointed to. The consequence is that the compiler has to put a copy in every Translation Unit, and the linker will choose one of those - one per linker output. But the linker of Foo.lib cannot predict that there will also be a copy in Bar.lib and vice versa.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Can you please elaborate @MSalters. Your answer in this shape is not helpful, neither it has solved problem described in the question. Thanks. – Konrad Oct 10 '18 at 13:20
  • thanks for explaining. Two questions though - why is it different then for a case, when definitions are moved to .cpp - shouldn't it behave in the same way? Second one - you say "linker will choose", but it doesn't (!) - any suggestions how to make it choose one? – Konrad Oct 11 '18 at 07:55