Kompilacja warunkowa – wymuszenie zdefiniowania tylko jednego symbolu

0

Sytuacja wygląda tak – mamy zestaw trzech symboli, o zasięgu globalnym (dotyczących całego projektu). Kompilacja ma zostać przerwana w momencie, gdy żaden z trzech symboli nie jest zdefiniowany, lub jeśli zdefiniowanych jest więcej niż jeden. Dla przykładu, nazwijmy te symbole FOO, BAR, BAZ.

Całość można załatwić kupką $DEFINED-ów, np. w poniższy sposób:

{$IF NOT DEFINED(FOO) AND NOT DEFINED(BAR) AND NOT DEFINED(BAZ)}
  {$FATAL needed symbol is not defined}
{$ELSEIF (DEFINED(FOO) AND DEFINED(BAR)) OR (DEFINED(FOO) AND DEFINED(BAZ)) OR (DEFINED(BAR) AND DEFINED(BAZ))}
  {$FATAL too many symbols defined}
{$ENDIF}

Pierwsza linijka warunków sprawdza czy którykolwiek został zdefiniowany, druga czy nie jest ich co najmniej dwa. Całość działa prawidłowo – poprawnie wykrywa brak symboli lub ich nadmiar. Problem w tym, że strasznie tego dużo – niezbyt to czytelne (choć użyłem trzyliterowych symboli, w oryginale składają się z liter kilkunastu) i łatwo się pomylić przy wypisywaniu wymaganych permutacji.

A co jeśli miałbym tych symboli pięć? Albo więcej? Trzeba by cały ekran zawalić tymi $DEFINED-ami, co było by kompletnie nieczytelne… Czy jest jakiś lepszy (przede wszystkim którszy) sposób na wykonanie takiej walidacji?


Szkoda, że $DEFINED nie pozwala na grupowanie symboli… Mogłoby być przyjemnie:

{$IF NOT DEFINED(FOO,BAR,BAZ)}
  {$FATAL needed symbol is not defined}
{$ELSEIF DEFINED(FOO,BAR) OR DEFINED(FOO,BAZ) OR DEFINED(BAR,BAZ)}
  {$FATAL too many symbols defined}
{$ENDIF}

Niestety to nie przejdzie. :/

0

Kompilacja ma zostać przerwana w momencie, gdy żaden z trzech symboli nie jest zdefiniowany, lub jeśli zdefiniowanych jest więcej niż jeden.
Dla przykładu, nazwijmy te symbole FOO, BAR, BAZ.

Ale po co chcesz to walidować? Opisz w instrukcji, jak prawidłowo zbudować projekt. Jak ktoś sobie zdefiniuje źle i mu coś nie będzie działać, to już jego problem.

0

Tzn. bez tych symboli projekt nie zostanie poprawnie zbudowany – wyskoczą jakieś randomowe błędy, np. o niezdefiniowanych identyfikatorach. Jeśli będzie ich więcej to kompilacja może zostać wykonana, ale też nie musi – znów mogą polecieć randomowe błędy (z punktu widzenia kompilującego).

Tego typu błędy (np. o brakujących stałych czy nieznanych polach rekordów) nic nie mówią o tym co źle się robi. Uznałem więc, że dobrze będzie wrzucić odpowiednie zabezpieczenie do głównego pliku projektu, które poinformuje kompilującego o tym, co konkretnie zepsuł i jak to naprawić. Projekt nawet dobrze nie zacznie być kompilowany, jak już zostanie ten proces przerwany. A skoro są do tego specjalne dyrektywy (jak właśnie $FATAL), to czemu by z nich nie skorzystać? Robię to raz, w jednym miejscu i zostawiam – w razie błędów sytuacja będzie klarowna.

Zadałem tutaj pytanie po to aby dowiedzieć się, czy jakiejś wariacji dyrektyw nie znam, czy po prostu krótszy zapis nie istnieje i takie potworki to jedyne co można zrobić. Sam fakt dodania takiego zabezpieczenia uważam za dobrą rzecz – w końcu pozwoli zaoszczędzić czas i włosów na głowie. ;)

3

Co prawda nie pisałem w Pascalu od jakichś dwóch lat, ale let's give it a try, tak na logikę:

{$DEFINE TEST_A}
{$DEFINE TEST_B}
// {$DEFINE TEST_C}
 
{$IF DEFINED(TEST_A) + DEFINED(TEST_B) + DEFINED(TEST_C) <> 1}
	{$FATAL SORRY, MATE!}
{$ENDIF}

https://ideone.com/beJEc8
https://ideone.com/67COm1
Najciemniej pod latarnią ;-)

0

Ja mam zrobione coś w tym stylu:

const
{$IFDEF FOO}
  MOJSYMBOL = 'FOO';
{$ENDIF}
{$IFDEF BAR}
  MOJSYMBOL = 'BAR';
{$ENDIF}
{$IFDEF BAZ}
  MOJSYMBOL = 'BAZ';
{$ENDIF}
  SYMBOL_TEST = MOJSYMBOL;

Nie robiłem tego na potrzeby walidacji ale do kompilowania różnych wersji aplikacji, konkretnie dodatkowych członów nazwy programu. Nie ma tutaj problemu z kombinowaniem odnośnie permutacji zestawów, długich nazw symboli itp. Wydaje mi się, że rozwiązanie jest w miarę proste. Musi być zadeklarowany chociaż jeden z symboli a nie może być równocześnie więcej niż jeden. Kompilator wyrzuci błąd, o braku zmiennej lub duplikacie. Można by było tutaj jeszcze pokombinować odnośnie komunikatu kompilatora lub samej nazwy zmiennej, która mogłaby sugerować typ błędu.

0
Patryk27 napisał(a):

Co prawda nie pisałem w Pascalu od jakichś dwóch lat, ale let's give it a try, tak na logikę:

Tyle że to nie jest logiczne, bo dodajesz boole – ale kompiluje się prawidłowo… Szkoda że w dokumentacji nie ma nic na ten temat, bo bym pytać nie musiał. :|

Dzięki za sugestię – to wystarczająco upraszcza sprawę.

Clarc napisał(a):

Nie robiłem tego na potrzeby walidacji ale do kompilowania różnych wersji aplikacji, konkretnie dodatkowych członów nazwy programu.

Trafiłeś w sedno – potrzebuję tego do sprawdzenia, czy wersja jest poprawnie określona.

Tyle że użycie globalnej stałej ani żadnego innego podobnego elementu lub konstrukcji nie wchodzi w grę, bo mowa tutaj o wykluczaniu pewnych fragmentów kodu oraz całych modułów (i formularzy), linkowania lub nie zasobów, ucinania pól w strukturach itd. Dzięki temu do pliku wykonywalnego nie zostaje wkompilowane to, co używane w danej wersji nigdy nie będzie.


Problem rozwiązany. Dzięki za odzew, a Tobie @Patryk27 za podsunięcie rozwiązania.

Następna ciekawostka poznana i szkoda, że nie w postaci wpisu na blogu, a pytania na forum. ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1