Zaawansowane CPP/Ćwiczenia 9: Szablony wyrażeń

From Studia Informatyczne

Ćwiczenie 1

Zaimplementuj szablony funkcji table1, table2 i table3, generujące tabele prawdy dla jednej, dwu lub trzech zmiennych logicznych. Argumentem tych funkcji ma być funkcja przyjmująca, w zależności od funkcji, od 1 do 3 argumentów typu bool, np.:

 bool And(bool q,bool r) {return q && r;}
 table2(And) ;

powinno wygenerować :

--------------
| 0 | 0 || 0 |    
| 0 | 1 || 0 |    
| 1 | 0 || 0 |    
| 1 | 1 || 1 |    
--------------

Podobnie dla table1 i table3.

Rozwiązanie

Patrz plik table.h.

Ćwiczenie 2

Napisz szablony wyrażeń, które będzie można użyć z powyższymi szablonami. Zaimplementuj trzy klasy First, Second i Third, oznaczające odpowiednio pierwszy, drugi i trzeci argument funkcji logicznej. Zaimplementuj operatory &&(i), ||(lub), !(zaprzeczenie), >> (implikacja) i ==(równoważność) oraz możliwość korzystania ze stałych logicznych True i False. Np.:

First   q;
Second  r;
table2(q >> r);

powinno wygenerować:

--------------
| 0 | 0 || 1 |    
| 0 | 1 || 1 |    
| 1 | 0 || 0 |    
| 1 | 1 || 1 |    
--------------

a

table1(True == q);

powinno wygenerować:

---------
| 0 | 0 |    
| 1 | 1 |    
---------

Rozwiązanie

Rozwiązanie jest całkowicie analogiczne do szblonów wyrażeń podanych na wykładzie. Zaczynamy od klas reprezentujących odpowiednio pierwszy, drugi i trzeci argument wyrażenia logicznego:

struct First {
   bool operator()(bool q) {return q;};
   bool operator()(bool q, bool r) {return q;};
   bool operator()(bool q, bool r, bool t) {return q;};
};
struct Second {
   bool operator()(bool q, bool r) {return r;};
   bool operator()(bool q, bool r, bool t) {return r;};
};
struct Third {
   bool operator()(bool q, bool r, bool t) {return t;};
};

Następnie definiujemy klasę stałych logicznych:

class Constant {
   bool _val;
public
   Constant(bool q):_val(q) {};
   bool operator()(bool q) {return _val;};
   bool operator()(bool q, bool r) {return _val;};
   bool operator()(bool q, bool r, bool s) {return _val;};
};

i dwie globalne stałe:

namespace Logic {
Constant True(true);
Constant False(false);
}

Używamy przestrzeni nazw Logic aby zmniejszyć ryzyko konfliktu nazw.

Klasa And reprezentuje koniunkcję dwu wyrażeń logicznych:

template<typename L,typename R> class And {
   L _lhs;
   R _rhs;
public:
   And(L l,R r):_lhs(l),_rhs(r) {};
   bool operator()(bool q) {return _lhs(q) && _rhs(q);}
   bool operator()(bool q, bool r) {return _lhs(q,r) && _rhs(q,r);}
   bool operator()(bool q, bool r, bool s) {return _lhs(q,r,s) && _rhs(q,r,s);}
};

Obiekty tej klasy są generowane przez operator:

template<typename L,typename R> And<L,R> operator&&(L l,R r)
   {return And<L,R>(l,r);}

Jeśli będziemy korzystać tylko z naszych stałych logicznych Logic::True i Logic::False, to niepotrzebne są dodatkowe przeciążenia operatora.

Pozostałe operatory implementujemy analogicznie. Całość kodu jest podana w pliku logic.h.

Ćwiczenie 3

Zaimplementuj szablon funkcji, który będzie przyjmował jako jeden z parametrów szablonu ilość zmiennych logicznych i będzie łączył działanie funkcji table1, table2 i table3. Np.:

table<2>(And);

powinno być równoważne wywołaniu:

table2(And);

Podpowiedź

Wykorzystaj pomocniczy szablon klasy aby móc skorzystać ze specjalizacji częściowej.

Rozwiązanie

Patrz plik table.h.