Technical Article

Dinamičko stvaranje i brisanje instance HotPDF komponente u C++Builderu

Postavljanje komponente THotPDF na formu u fazi dizajna je u redu za brzi prototip, ali povezuje vijek trajanja komponente s vijekom trajanja forme, što produkcijski kod rijetko želi. Generator izvještaja koji se pokreće jednom na klik gumba, nit usluge (service thread) koja vrši noćni izvoz u serijama, pomoćna klasa koja uopće nema formu: u svakoj od tih situacija želite da komponenta postoji točno tijekom trajanja jednog PDF zadatka i potom nestane. To znači alokaciju u vrijeme izvođenja (runtime), a to mijenja dvije stvari koje vrijedi razumjeti prije pisanja prvog retka koda: tko je vlasnik objekta i kako se odvija čišćenje kada nešto pođe po zlu.

Semantika vlasnika (Owner semantics) u VCL-u

Svaki konstruktor VCL komponente prima parametar Owner tipa TComponent*. Prosljeđivanje this (forme) registrira novi objekt u popisu komponenti u vlasništvu forme, pa ako se forma uništi dok je komponenta još živa, VCL je automatski oslobađa. Prosljeđivanje nullptr znači da nema vlasnika: preuzimate isključivu odgovornost za pokazivač i ništa ga neće očistiti umjesto vas ako iznimka odvrati stog prije vašeg izričitog poziva delete.

Za jednokratni izvoz koji se završava unutar jedne funkcije radi bilo koji izbor, ali ta dva pristupa imaju različite načine zakazivanja. Uz this kao vlasnika, curenje memorije je nemoguće dok god se forma na kraju zatvori; s nullptr, pokazivač mora doći do __finally bloka. U praksi, uzorak nullptr plus __finally je malo čišći za kratkotrajne objekte jer čini granicu životnog vijeka vidljivom na prvi pogled i izbjegava da forma nakuplja objekte u vlasništvu koji su trebali biti privremeni.

Struktura sigurna od iznimki (Exception-safe)

Generiranje PDF-a može zakazati iz razloga koji nemaju nikakve veze s API-jem: izlazni direktorij je samo za čitanje, datoteka fonta nedostaje, tok podataka se prerano prazni ili podaci koje je dostavio pozivatelj prelaze ograničenje duljine. Bez obzira na uzrok, put čišćenja se mora izvršiti. Idiomatski način za jamstvo toga u C++Builderu je try/__finally:

#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#pragma package(smart_init)
#pragma link "HPDFDoc"
#pragma resource "*.dfm"

TForm1 *Form1;

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    THotPDF* Pdf = new THotPDF(nullptr);
    try
    {
        Pdf->FileName = "output.pdf";
        Pdf->Compression = cmFlateDecode;
        Pdf->FontEmbedding = true;
        Pdf->BeginDoc();
        Pdf->CurrentPage->SetFont("Arial", TFontStyles(), 12);
        Pdf->CurrentPage->TextOut(72, 720, 0, L"Hello from C++Builder");
        Pdf->EndDoc();
    }
    __finally
    {
        delete Pdf;
    }
}

Nekoliko stvari u tom popisu vrijedi istaknuti. Vlasnik je nullptr, što životni vijek čini eksplicitnim. Svojstva Compression i FontEmbedding postavljaju se prije BeginDoc: obje su opcije na razini dokumenta koje HotPDF potvrđuje kada se dokument otvori, a njihovo dodjeljivanje nakon toga nema utjecaja. Metoda TextOut prima koordinate u točkama mjerene od donjeg lijevog kuta stranice, pri čemu Y raste prema gore; par 72, 720 postavlja tekst blizu gornjeg lijevog kuta stranice veličine Letter s lijevom marginom od jednog inča. Poziv delete Pdf u bloku __finally izvodi se bez obzira na to jesu li BeginDoc, crtanje ili EndDoc pokrenuli iznimku.

Izbjegavajte pozivanje bilo koje metode na objektu Pdf nakon brisanja. Ako je pokazivač pohranjen u varijablu članicu, postavite ga na nullptr odmah nakon brisanja kako bi svaki slučajni kasniji pristup proizveo čisti pad programa umjesto tihe korupcije.

Konfiguracija projekta

C++Builder locira THotPDF kombinacijom putanja uključivanja (include paths), putanja biblioteka i pragma direktive. Generirano zaglavlje živi uz datoteku HPDFDoc.pas u direktoriju izvornog koda HotPDF-a; dodajte taj direktorij u Project > Options > C++ Compiler > Include path. Direktiva #pragma link "HPDFDoc" govori povezivaču (linker) da povuče prevedenu jedinicu bez ručnog navođenja u datoteci projekta. Ako koristite paket za vrijeme izvođenja (runtime package) umjesto statičkog povezivanja, najprije instalirajte HotPDF pakete za dizajn i vrijeme izvođenja; pragma i dalje vrijedi.

Naziv jedinice HPDFDoc ostavite nepromijenjenim. C++Builder izvodi naziv datoteke zaglavlja iz naziva Pascal jedinice, pa preimenovanje datoteke ili korištenje aliasa putanje u pragmi tiho prekida pretraživanje.

Opseg i poslovi s više dokumenata (Multi-document jobs)

Za jednokratni izvoz pokrenut korisničkom akcijom, lokalna varijabla ograničena na rukovatelj gumba je ispravan odgovor: stvara se, koristi i uništava unutar jednog okvira poziva, a namjera je očigledna svakome tko kasnije čita kod. Alternativa u fazi dizajna opravdana je kada ista forma upravlja kontinuiranim radnim tijekom, kao što je ploča s pretpregledom ispisa koja ponovno gradi dokument kad god korisnik promijeni postavku; u tom slučaju, održavanje komponente na životu i višekratno pozivanje BeginDoc/EndDoc manje je ometajuće od opetovane alokacije i oslobađanja objekata na gomili.

Za serijske poslove koji proizvode mnogo dokumenata u nizu, postavljanje jedne instance THotPDF po dokumentu vrijedi režijskih troškova alokacije. Stanje se ne prenosi između dokumenata ako nema objekta koji bi ga nosio, a to uklanja jednu klasu povremenih bugova koje inače morate debugirati. Alocirajte, generirajte, obrišite, ponovite.

Jedno svojstvo koje se pojavljuje u nekoliko HotPDF demo primjera je AutoLaunch, koje otvara generiranu datoteku u sistemskom PDF pregledniku odmah nakon EndDoc. Korisno je dok pišete prvu skicu rasporeda. U produkciji ga preskočite: eksplicitno otvorite izlaznu putanju, potvrdite da datoteka postoji i da ima veličinu veću od nule, zabilježite rezultat i prepustite pozivnom radnom tijeku odluku o tome je li preglednik potreban. U serijskom poslu, AutoLaunch pokreće po jedan prozor preglednika za svaki dokument i na nekim će sustavima blokirati proces čekajući da se preglednik zatvori.

Komponenta THotPDF i svi ovdje prikazani pozivi za crtanje dio su komponente HotPDF Component za Delphi i C++Builder.