Ko vzdržujete komponento VCL, napisano v Delphiju, ki pa jo uporabljajo uporabniki tako Delphija kot C++Builderja, hitro ugotovite, da oba sistema za gradnjo (build systems) povsem drugače obravnavata odvisnosti. Enota, ki se popolnoma brez težav prevede v Delphiju, lahko povzroči katastrofalne napake povezovalnika (linker errors) v C++Builderju. To neskladje je pogosta past za avtorje komponent, zlasti pri dodajanju novih notranjih enot v obstoječo knjižnico
Tukaj je podrobna razčlenitev, zakaj se to dogaja – z uporabo primerov iz resničnega sveta iz komponente HotXLS – in kako zavarovati postopek navzkrižnega prevajanja z uporabo eksplicitnih vključitev, {$HPPEMIT} in povezovanjem s pragmami
Past: Implicitno prevajanje v primerjavi z eksplicitno vključitvijo
Predpostavimo, da ustvarite novo enoto Delphi, lxXlsSummary.pas, za obravnavo metapodatkov dokumenta, in jo vključite v svojo glavno razčlenjevalno enoto, lxRead.pas, z uporabo uses. V IDE-ju Delphi pritisnete "Compile", gradnja je zelena in posodobitev izdate
Naslednji dan vaši uporabniki C++Builderja poročajo o napaki med fazo povezovanja: Unresolved external 'XlsReadSummaryInformation' referenced from lxRead.obj
Način Delphija (dcc32)
Ko prevajalnik Delphi obdela paket (.dpk), pregleda enote, ki so eksplicitno navedene v klavzuli contains. Če ena od teh enot z uses uporabi zunanjo enoto (kot je lxXlsSummary.pas), ki ni na seznamu contains, prevajalnik Delphi izvede implicitno statično povezovanje. Preprosto najde datoteko .pas v iskalni poti, jo prevede v .dcu in jo zapeče v končni .bpl. Gradnja je uspešna in popolnoma prikrije opustitev
Način C++Builderja (MSBuild / .cbproj)
Sistem za gradnjo v C++Builderju je precej strožji. Generira objektne datoteke C++ (.obj) in glave (.hpp) samo za tiste enote Delphi, ki so eksplicitno navedene v skupini postavk <DelphiCompile> v datoteki .cbproj. Ker lxXlsSummary.pas ni bil nikoli eksplicitno registriran v projektni datoteki, lxXlsSummary.obj ne bo ustvarjen. Ko poskuša povezovalnik razrešiti klice, ki jih naredi lxRead.obj, simboli manjkajo, kar vodi do napake z nerazrešeno zunanjo referenco (unresolved external)
Razreševanje zunanjih referenc s Pragma Link in HPPEMIT
Če želite zagotoviti, da je enota pravilno povezana v C++, ne da bi uporabnika prisilili, da ročno doda datoteko .obj v svoj projekt, lahko uporabite direktivo Delphi {$HPPEMIT}. Ta prevajalniku Delphi ukaže, naj v generirano datoteko glave .hpp vbrizga specifično direktivo C++ #pragma link
unit lxXlsSummary;
interface
{$IFDEF WINDOWS}
// Inject a pragma link into the generated C++ header file
// This forces the C++ linker to include the corresponding .obj file
{$HPPEMIT '#pragma link "lxXlsSummary.obj"'}
{$ENDIF}
uses
SysUtils, Classes;
type
TXlsSummaryInfo = class(TObject)
public
Title: string;
Author: string;
CreateTime: TDateTime;
end;
function XlsReadSummaryInformation(const FileName: string): TXlsSummaryInfo;
implementation
function XlsReadSummaryInformation(const FileName: string): TXlsSummaryInfo;
begin
Result := TXlsSummaryInfo.Create;
// Metadata extraction logic here
end;
end.
Ko C++Builder vključi lxXlsSummary.hpp, prevajalnik naleti na #pragma link in samodejno naroči povezovalniku (ILINK32/ILINK64), naj razreši simbole iz lxXlsSummary.obj
Zlato pravilo za vzdrževanje komponent
Da bi se izognili popolni okvari gradenj v C++Builderju, morate uvesti strogo politiko registracije. Kadarkoli je v vašo knjižnico dodana nova Pascalova enota, mora biti eksplicitno registrirana hkrati v vseh treh vrstah projektnih datotek
1. Posodobitev projekta C++Builder (.cbproj / .bpk)
Odprite datoteko .cbproj v urejevalniku besedila in dodajte novo enoto na seznam za prevajanje, pri čemer poskrbite, da boste podali edinstven vrstni red gradnje. Če uporabljate starejše različice C++Builderja z datotekami `.bpk`, poskrbite, da je dodana oznaka `
<DelphiCompile Include="lxXlsSummary.pas">
<BuildOrder>101</BuildOrder>
</DelphiCompile>
2. Posodobitev paketa Delphi (.dpk)
Dodajte enoto v eksplicitno klavzulo contains. To zagotavlja, da se prevajalniku Delphi ni treba zanašati na implicitno povezovanje, ki tako ali tako na splošno velja za slabo prakso
package HotXLS;
{$R *.res}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
requires
rtl,
vcl;
contains
lxRead in 'lxRead.pas',
lxXlsSummary in 'lxXlsSummary.pas';
end.
Validacija nenehne integracije (CI/CD)
Končna obramba pred to pastjo je preverjanje CI/CD. Nikoli se ne zanašajte zgolj na uspešno gradnjo v Delphiju, preden izdate komponento za dva jezika. Vaše skripte za gradnjo morajo priklicati MSBuild ali orodja ukazne vrstice bcc32c za projekte C++Builder (npr. build-Win32-Lib-CB.cmd) in izvesti celotno povezovanje preizkusnih različic C++ in celotnih predstavitvenih aplikacij (demos). Šele ko je povezovalnik C++ uspešen, ste lahko prepričani, da so vse enote Delphi pravilno registrirane in izpostavljajo svoje simbole izvajanju C++
Opomba: Združljivost navzkrižno platformnih prevajalnikov se strogo ohranja med izdajami Delphi in C++Builder komponente HotXLS VCL Component