Kada Delphi biblioteka dobije build konfiguraciju bez vizuelnog okvira, zameničke klase su mesto gde žive greške. Ne platforma, ne kompajler: zamene. PDFlibPas ima grafički sloj koji obezbeđuje bitmap, canvas, font, metafile i štampačke ekvivalente za build-ove bez VCL-a, i preseljenje na Free Pascal iznelo je na videlo svaki režim otkazivanja koji zamena može imati. Razvrstavaju se uredno po trošku dijagnoze, i redosled je suprotan onome što intuicija sugeriše
Zamena koja baca izuzetak jeftina je za pronalaženje; izuzetak imenuje metodu. Zamena koja vraća prazne podatke je skupa, jer se otkazivanje pojavljuje nekoliko slojeva od svog uzroka. Zamena koja vraća uspeh najgora je od svih, jer je povratni kod valjan, kod greške je nula, nikakav izuzetak se ne diže, i jedini dokaz da je nešto pošlo naopako je u bajtovima koji su izašli
Oblik tri: valjan identifikator slike nad praznim XObject-om
Vektorski metafile konvertor bio je prazno telo procedure u non-VCL konfiguraciji. Sve iznad nastavilo je raditi. Ulazne tačke EMF uvoza i ulazna tačka hvatanja canvas-a otrčale su do kraja i vratile legalan identifikator slike, koji je pozivalac zatim postavio na stranicu. Ono što je sletelo u datoteku bio je form XObject sa dužinom sadržaja nula. Stranica se prikazala bela
Ništa nije javilo problem, i to uključuje sopstveni demonstracioni program biblioteke za ovu funkciju, koji je nacrtao praznu stranicu i nije primetio. Nije bilo neuspešne povratne vrednosti za proveru, jer je sekvenca poziva iskreno sva uspela; jedino što je bilo pogrešno bila je veličina proizvedenog toka. Dijagnostikovanje ove klase defekta znači postavljati drugo pitanje: ne "da li je poziv otkazao" nego "da li je artefakt uverljiv". Form XObject dužine nula, slika od nula piksela, stranica od nula bajtova sadržaja, to su tvrđenja koja ga hvataju
Popravka ima dve polovine i druga polovina lako se zaboravi. Prvo, učinite da prazna implementacija baca izuzetak, pa otkazivanje ima kanal uopšte. Drugo, pretvorite taj izuzetak u null rezultat na fabrici slike i dodajte null provere na dva mesta koja konzumiraju identifikator slike, jer inače "čisto otkazivanje" pretvara se pravo u pristupno kršenje dok stablo stranica dereferencira ništa. Stub koji baca jeste unapređenje samo ako su pozivaoci bili spremni za otkazivanje koje ranije nikad nisu mogli primiti
Oblik dva: prazni podaci, tri sloja od pada
Metafile canvas zamena nije popunila svoje fizičke dimenzije. Ta vrednost deli se u izračun geometrije stranice, pa je izračun proizveo nulu, pa je izračun okvirnog okvira delio nulom. Gol rukovaoc izuzetkom progutao je to, fabrika slike vratila je null rezultat, i pristupno kršenje konačno se desilo u stablu stranica kada je null iskorišćen. Tri sloja između uzroka i simptoma, sa rukovaocem izuzetkom u sredini koji briše dokaze
Ista jedinica imala je još dva primerka obrasca. Klasa fonta imala je prazna tela Assign i konstruktora, što je važnije nego što izgleda jer je svojstvo fonta canvas-a samo za čitanje: dodeljivanje u njega jedini je način da se dostavi font, pa prazna implementacija čini izbor fonta tiho nedejstvujućim i tekst izlazi u onome što je bilo podrazumevano. A vrednost piksela po inču od nula učinila je da svaki pozivalac koji dimenzioniše canvas iz metriki fonta proizvede canvas nula puta nula, što daje praznu stranicu i povratak uspeha
// Oblik koji treba tražiti u zameničkoj jedinici: metoda koja ni
// ne baca izuzetak ni ne radi ništa. Obe se kompajliraju i obe
// proizvode "uspeh" bez izlaza
procedure TMetafileCanvasStandIn.Create(...);
begin
// bez inherited poziva, bez inicijalizacije polja
end;
function TBitmapStandIn.LoadFromStream(Stream: TStream): Boolean;
begin
Result := True; // a bitmapa je i dalje prazna
end;
Široka struktura koja čuva samo prvi znak
Ovaj uopšte nije problem zamene, ali pripada istom katalogu jer je simptom jednako daleko od uzroka. Struktura enumeracije štampača deklarisana je sa svih dvanaest svojih članova nizova znakova tipizovanih kao pokazivači na jednobajtne znakove, dok je funkcija koja je puni varijanta enumeracionog API-ja sa širokim znakovima
Veličine pokazivača identične su, pa je raspored strukture ispravan i ništa ne pada. Ono što se umesto toga dešava jeste da čitanje UTF-16 niza znakova kao jednobajtnog niza staje na prvom nultom bajtu, koji je za svako ASCII ime štampača gornja polovina drugog znaka. Svako ime štampača vratilo se kao tačno jedan znak. Nizvodno, validacija imena otkazala je, stvaranje štampača otkazalo je i štampanje otkazalo je za svaki stvarni štampač na mašini, i nijedan od tih simptoma ne ukazuje na deklaraciju strukture
// Pogrešno: ispravna veličina, pogrešan tip elementa. Nema greške
// kompajliranja, nema pada, svaki niz znakova skraćen na jedan znak
type
TPrinterInfo2Wrong = record
pServerName: PAnsiChar;
pPrinterName: PAnsiChar;
// ... još deset
end;
// Ispravno: *W struktura ima široke članove od početka do kraja
type
TPrinterInfo2W = record
pServerName: PWideChar;
pPrinterName: PWideChar;
// ... još deset
end;
Pravilo koje iz toga izlazi je mehaničko i vredi ga primenjivati bez razmišljanja: za svaku Win32 strukturu čije se ime završava na W, proverite da je svaki član niza znakova široka varijanta, polje po polje. Mešanje ANSI i širokog sveta ne proizvodi ni dijagnostiku kompajlera ni pad, samo tiho skraćivanje, i isto važi obrnuto za ANSI varijante
Gol rukovaoc izuzetkom je pravi protivnik
Svako od ovih ispitivanja usporeno je istom konstrukcijom: rukovaoc koji hvata sve i pretvara ga u false povratnu vrednost. To je razumna stvar pisati oko dekodera slika, jer oštećena slika ne treba da obori dokumentni posao. To je takođe uređaj za brisanje jedinog dela informacije koji vam treba
Praktičan odgovor je učiniti rukovaoca privremeno bučnim. Izvojštavanje klase izuzetka, poruke i backtrace-a iznutra golog rukovaoca, pod debug uslovnim, pretvara neobjašnjiv null povratak u imenovani izuzetak sa lokacijom. U dva od tri gore navedena slučaja taj jedan korak okončao je ispitivanje, jer je izuzetak bio deljenje nulom ili pristupno kršenje u metodi zamene čije je ime reklo sve
Popis za usvajanje zameničkog puta
Četiri stavke, u redosledu po kojem se isplata javlja. Pre pozivanja u zameničku klasu, pročitajte metode koje nameravate koristiti i potvrdite da svaka ima pravo telo; prazno telo nije detalj implementacije, to je nedostajuća funkcija. Dajte prednost zamenama koje bacaju izuzetak nad zamenama koje vraćaju neutralne vrednosti, i uparite to sa null proverama na mestima gde fabrika sada legitimno može vratiti ništa. Verifikujte funkciju ispitivanjem artefakta, ne povratnog koda, pošto je ceo režim otkazivanja ovde čist povratni kod nad praznim artefaktom; rastavljanje na nivou bajtova onoga što dokument stvarno sadrži najbrži je način da se to vidi, i članak o reviziji veličine datoteke pokriva taj alat. A kada funkcija nema održivu zameničku implementaciju, usmerite pogođene primerke na put koji radi i recite zašto u komentaru, umesto ostavljanja demonstracije koja tiho proizvodi prazan izlaz
Šira tačka primenjuje se daleko van jedne biblioteke. Svaka baza koda sa uslovnom drugom implementacijom, mock slojem, headless režimom, platformskim shim-om, izložena je obliku tri. Razlog što se tako dobro krije je što je svaka kapija kvaliteta na koju se tim obično oslanja, povratni kodovi, kodovi greške, izuzeci, izlazni statusi, kanal statusa, i oblik tri sve ih drži čistima. Samo izlaz ga izdaje. To je takođe razlog iza provere artefakata umesto statusa pri rukovanju nepoverljivim ulazom, opisano u članku o parsiranju nepoverljivog PDF-a, i iza poređenja renderovanog izlaza kroz motore umesto verovanja jednom, opisano u renderovanju sa više motora
PDFlibPas je nativna Object Pascal PDF biblioteka za Delphi, C++Builder i Free Pascal, i njegova non-VCL konfiguracija je ono što čini headless i cross-toolchain build-ove mogućim; trenutna pokrivenost konfiguracija navedena je na stranici proizvoda losLab PDF Developer Library