0

Edited to provider a little more clarity. Apologies for confusing everyone.

This is under Windows.

I have a static library that implements a class using the pimpl idiom. The pimpl header is not only used by the consuming code but it also links against the static library. Yet, when I compile the consuming code (.exe), the linker complains about unresolved externals on the implementation class that the pimpl header supposedly hides.

How can this be possible?

// Foo.lib

// Foo.h

class FooImpl;

class Foo
{
    std::shared_ptr<FooImpl> pimpl_;    
public:
    Foo();
};

// Foo.cpp

#include "Foo.h"
#include "FooImpl.h"

Foo::Foo() : pimpl_(new FooImpl())
{
}

// This is how its used in Bar.exe
// Bar.exe links against Foo.lib

// Bar.h

#include "Foo.h"

class Bar
{
    Foo access_foo_;
};

// Bar.cpp

#include "Bar.h"

When I compile/link Bar.exe, the linker complains that it is unable to resolve FooImpl. I forget what it said exactly since I don't have access to my work PC now but that's the gist of it. The error didn't make sense to me because the objective of going the pimpl route was so that Bar.exe didn't have to worry about FooImpl.

The exact error goes like this:

1>Foo.lib(Foo.obj) : error LNK2019: unresolved external symbol "public: __thiscall FooImpl::FooImpl(void)" (??0FooImpl@@QAE@XZ) referenced in function "public: __thiscall Foo::Foo(void)" (??0Foo@@QAE@XZ)

ForeverLearning
  • 6,352
  • 3
  • 27
  • 33
  • 8
    You have an error in line 3; it should say "foo", not "bar". – Kerrek SB Sep 30 '11 at 21:33
  • 2
    You're not giving us much of a chance here - how about telling us what you've tried, or showing us some compiler output, or showing us what you included at the top of your files, or maybe showing the makefile if you used one? They we could probably help :) – John Humphreys Sep 30 '11 at 21:36

2 Answers2

2

When you create a static library, the linker doesn't try to resolve everything that is missing; it assumes you'll be linking it later to another library, or that the executable itself will contribute some missing function. You must have forgotten to include some crucial implementation file in the library project.

The other possibility is that the pimpl implementation class is a template class. Templates don't generate code immediately, the compiler waits until you try to use them with the template parameters filled in. Your implementation file must include an instantiation of the template with the parameters that will be supported by your library.


After seeing your edit, the problem is that the Foo::Foo constructor needs access to the FooImpl::FooImpl constructor but the linker doesn't know where to find it. When the linker puts together the library, it doesn't try to resolve all of the references at that time, because it might have dependencies on yet another library. The only time that everything needs to be resolved is when it's putting together the executable. So even though Bar doesn't need to know about FooImpl directly, it still has a dependency on it.

You can resolve this in one of two ways: either export FooImpl from the library along with Foo, or make sure that Foo has access to FooImpl at compile time by putting them both in Foo.cpp with FooImpl coming before Foo.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • I edited my question for more clarity. FWIW, my pimpl class is not a template. – ForeverLearning Sep 30 '11 at 23:07
  • @Dilip, you're falling prey to my first assertion - your Foo::Foo doesn't have access to FooImpl::FooImpl. Either make sure FooImpl::FooImpl is exported from the library, or put it into Foo.cpp ahead of Foo::Foo. – Mark Ransom Oct 01 '11 at 01:36
  • thanks very much! I think I now understand. I will try this out tomorrow at work. – ForeverLearning Oct 01 '11 at 02:17
  • Including FooImpl in Foo.cpp resolved the linker errors. I didn't want to export because it somehow felt counter-intuitive to do that from a static library – ForeverLearning Oct 03 '11 at 13:48
2

1>Foo.lib(Foo.obj) : error LNK2019: unresolved external symbol "public: __thiscall FooImpl::FooImpl(void)" (??0FooImpl@@QAE@XZ) referenced in function "public: __thiscall Foo::Foo(void)" (??0Foo@@QAE@XZ)

The linker is complaining you that it can not find the definition of FooImpl::FooImpl(). You are calling the constructor here -

Foo::Foo() : pimpl_(new FooImpl())
                    // ^^^^^^^^^ Invoking the default constructor.

You just provided the declaration of default constructor but not it's definition.

Mahesh
  • 34,573
  • 20
  • 89
  • 115
  • I don't understand. This error is not happening when I build Foo.lib. This happens when I build Bar.exe (that links against Foo.lib). Which is what confuses me. The main reason the Foo pimpl class exists is so that Bar.exe does not have to have a dependency on FooImpl, correct? – ForeverLearning Sep 30 '11 at 23:48
  • I may be mistakenly using the compiler firewall idiom to achieve interface-implementation separation. I supply Bar.exe with only Foo.h and allow Bar.exe to link against Foo.lib hoping that would obviate the need for Bar.exe to worry about FooImpl. I seem to have been mistaken there? – ForeverLearning Sep 30 '11 at 23:56
  • `"I don't understand. This error is not happening when I build Foo.lib."` Probably in the files you built, you were no where instantiating `FooImpl` that invokes default constructor. – Mahesh Oct 01 '11 at 00:00