How do I solve an unresolved external when using C++ Builder packages?
- by David M
I'm experimenting with reconfiguring my application to make heaving use of packages. Both I and another developer running a similar experiment are running into a bit of trouble when linking using several different packages. We're probably both doing something wrong, but goodness knows what :)
The situation is this:
The first package, PackageA.bpl, contains C++ class FooA. The class is declared with the PACKAGE directive.
The second package, PackageB.bpl, contains a class inheriting from FooA, called FooB. It includes FooB.h, and the package is built using runtime packages, and links to PackageA by adding a reference to PackageA.bpi.
When building PackageB, it compiles fine but linking fails with a number of unresolved externals, the first few of which are:
[ILINK32 Error] Error: Unresolved external '__tpdsc__ FooA' referenced from C:\blah\FooB.OBJ
[ILINK32 Error] Error: Unresolved external 'FooA::' referenced from C:\blah\FooB.OBJ
[ILINK32 Error] Error: Unresolved external '__fastcall FooA::~FooA()' referenced from blah\FooB.OBJ
etc.
Running TDump on PackageA.bpl shows:
Exports from PackageA.bpl
14 exported name(s), 14 export addresse(s). Ordinal base is 1.
Sorted by Name:
RVA Ord. Hint Name
-------- ---- ---- ----
00002A0C 8 0000 __tpdsc__ FooA
00002AD8 10 0001 __linkproc__ FooA::Finalize
00002AC8 9 0002 __linkproc__ FooA::Initialize
00002E4C 12 0003 __linkproc__ PackageA::Finalize
00002E3C 11 0004 __linkproc__ PackageA::Initialize
00006510 14 0007 FooA::
00002860 5 0008 FooA::FooA(FooA&)
000027E4 4 0009 FooA::FooA()
00002770 3 000A __fastcall FooA::~FooA()
000028DC 6 000B __fastcall FooA::Method1() const
000028F4 7 000C __fastcall FooA::Method2() const
00001375 2 000D Finalize
00001368 1 000E Initialize
0000610C 13 000F ___CPPdebugHook
So the class definitely seems to be exported and available to link. I can see entries for the specific things ILink32 says it's looking for and not finding. Running TDump on the BPI file shows similar entries.
Other info
The class does descend from TObject, though originally before refactoring into packages it was a normal C++ class. (More detail below. It seems "safer" using VCL-style classes when trying to solve problems with a very Delphi-ish thing like this anyway. Changing this only changes the order of unresolved externals to first not find Method1 and Method2, then others.)
Declaration for FooA:
class PACKAGE FooA: public TObject {
public:
FooA();
virtual __fastcall ~FooA();
FooA(const FooA&);
virtual __fastcall long Method1() const;
virtual __fastcall long Method2() const;
};
and FooB:
class FooB: public FooA {
public:
FooB();
virtual __fastcall ~FooB();
... other methods...
};
All methods definitely are implemented in the .cpp files, so it's not not finding them because they don't exist! The .cpp files also contain #pragma package(smart_init) near the top, under the includes.
Questions that might help...
Are packages reliable using C++, or are they only useable with Delphi code?
Is linking to the first package by adding a reference to its BPI correct - is that how you're supposed to do it? I could use a LIB but it seems to make the second package much larger, and I suspect it's statically linking in the contents of the first.
Can we use the PACKAGE directive only on TObject-derived classes? There is no compiler warning using it on standard C++ classes.
Is splitting code into packages the best way to achieve the goal of isolating code and communicating through defined layers / interfaces? I've been investigating this path because it seems to be the C++Builder / Delphi Way, and if it worked it looks attractive. But are there better alternatives?
I'm very new to using packages and have only known about them through using components before. Any general words of advice would be great!
We're using C++Builder 2010. I've fabricated the class and method names in the above code examples, but other than that the details are exactly what we're seeing.
Cheers,
David