I have a class called TTextStream in an existing project that inherits from TFileStream. It had a set of overloaded operator<< and operator>> function as member functions.
class PACKAGE TTextStream : public TFileStream
Most recommendations instead say that you should write external functions for those operators and make them friends of the class in question to be able to access private and protected memebers.
So as part of going through and cleaning up the code I did this.
And example of a function added
TTextStream& __fastcall operator >>(TTextStream& ts, AnsiString& s);
The implementation is then in the .cpp as usual.
Then within the TTextStream class I added
friend TTextStream& __fastcall operator >>(TTextStream& ts, AnsiString& s);
The project produces a C++ Builder package (BPL), let's call it Foo.bpl. It compiled fine and I then went on to compile the next BPL package, Bar.bpl, that uses functionality from Foo.bpl.
All these BPL's are outputed to the "default" folder, C:\Users\Public\Documents\Embarcadero\Studio\20.0\BLP
Foo.bpi is added under Requires in the Bar.bpl project.
Upon building I got
[ilink32 Error] Error: Unresolved external '__fastcall operator >>(TTextStream&, System::AnsiStringT<0>&)' referenced from BLABLABLA.OBJ
If I tried to link statically with Foo.lib instead it works fine. After some digging I come to the conclusion that the function hasn't been exported correctly to Foo.bpl.
As I understand it I have to use the PACKAGE macro for this so I add this to the definition in the .h file and the implementation in the .cpp file.
PACKAGE TTextStream& __fastcall operator <<(TTextStream& ts, const AnsiString& s);
I recompile Foo.bpl, I then compile Bar.bpl and it now compiles and links fine. But Bar.bpl is a runtime and design time package and when I go to install it in the IDE a Windows message dialog pops up with basically the same message as before.
The procedure entry point @$blsh$qr11TTextStreamrx27System@%AnsiStringT$us$i0$% could not be located in the dynamic library C:\Users\Public\Documents\Embarcadero\Studio\20.0\BPL\Dcl\Bar.bpl
So it seems to complain about not finding a function, that seems to be exported in Foo.bpl in Bar.bpl?
Not quite sure what I'm missing. TDUMP seems to show it actually being exported.
C:\Users\Public\Documents\Embarcadero\Studio\20.0\Bpl>tdump -oiEXTDEF Foo.bpl | grep TTextStream
File STDIN:
00050668 211 0000 __fastcall operator <<(TTextStream&, const char *)
000505D8 210 0001 __fastcall operator <<(TTextStream&, System::WideString&)
0005057C 209 0002 __fastcall operator <<(TTextStream&, System::AnsiStringT<0>&)
00051350 213 0003 __fastcall operator >>(TTextStream&, System::WideString&)
000506B0 212 0004 __fastcall operator >>(TTextStream&, System::AnsiStringT<0>&)
00051D6C 218 0009 __tpdsc__ TTextStream
00051D3C 217 00DB TTextStream::
00051A18 216 00DC TTextStream::operator =(TTextStream&)
00050150 207 00DD __fastcall TTextStream::TTextStream(System::AnsiStringT<0>&, unsigned short, TEOLMode)
0005157C 215 00DE TTextStream::TTextStream(TTextStream&)
000514F4 214 00DF __fastcall TTextStream::~TTextStream()
0005051C 208 00E0 __fastcall TTextStream::WriteEOL()
or without de-mangling
EXPORT ord:0211='@$blsh$qr11TTextStreampxc'
EXPORT ord:0210='@$blsh$qr11TTextStreamrx17System@WideString'
EXPORT ord:0209='@$blsh$qr11TTextStreamrx27System@%AnsiStringT$us$i0$%'
EXPORT ord:0213='@$brsh$qr11TTextStreamr17System@WideString'
EXPORT ord:0212='@$brsh$qr11TTextStreamr27System@%AnsiStringT$us$i0$%'
EXPORT ord:0218='@$xp$11TTextStream'
EXPORT ord:0050='@TLangFormNode@SaveToASCII$qqrp11TTextStreamui'
EXPORT ord:0217='@TTextStream@'
EXPORT ord:0216='@TTextStream@$basg$qrx11TTextStream'
EXPORT ord:0207='@TTextStream@$bctr$qqrrx27System@%AnsiStringT$us$i0$%us8TEOLMode'
EXPORT ord:0215='@TTextStream@$bctr$qrx11TTextStream'
EXPORT ord:0214='@TTextStream@$bdtr$qqrv'
EXPORT ord:0208='@TTextStream@WriteEOL$qqrv'
I have also tested with other calling conventions to rule that out.
As a secondary test I also created a small test case.
A project producing a bpl with a Unit (functions.h/functions.cpp) added with a single function
#ifndef __MYFUNCS__
#define __MYFUNCS__
#pragma package(smart_init)
PACKAGE int add(int a, int b)
#endif
that just adds the two numbers together, with the implementation in the .cpp file. Compile and linkk the BPL.
Then I created a console app that required the bpl in question, included the header file with the function definition (functions.h), compile and it fails linking with the same error. So I'm probably missing some minor obvious thing...
All this is done using RAD Studio 10.3.2 and not using the classic compiler.
After running into this a few times I finally found a way that seems to "fix" it. Several BPL projects some of which contain IDE components refused to install with the message "procedure entry point" when I for instance changed a namespace or something. Functions were exported correctly to the BPL and what finally made it able to install the new version of the component BPL was
Installing after this gave the error, instead
I have no idea why this works but it has worked fine for 5 projects now. I'm guessing some cache or something is messing up for me. Or perhaps the old BPL that is uninstalled first doesn't actually get removed entirely.
If anyone has any idea what is going on I would be happy for the info.