Tehnični članak

Komponente Delphi Excel, združljive s C++Builder

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