Zaawansowane CPP/Ćwiczenia 11: Funktory

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania

Ćwiczenie 1

Zaimplementuj adapter compose_f_gx_hy realizujący złożenie dwuargumentowe f(g(x),h(y)).

Rozwiązanie

Ćwiczenie 2

Korzystając z klasy functor_traits zaimplementuj adpter bind1st, który bedzie działał zarówno dla funktorów jedno-, jak i dwuargumentowych.

Rozwiązanie

Ćwiczenie 3

Zaimplementuj funktor implementujący, składanie funkcji poprzez wykonywanie ich po kolei np.:

macro(f1,f2)(x)

powinno wykonać

f1(x);f2(x);

Wartości zwracane przez te funkcje są ignorowane. Funkcja macro powinna zwracać funktor odpowiedniego typu (posiadający odpowiednie typy stowarzyszone) tak, aby możliwe było dalsze składanie np.:

macro(macro(f1,f2),f3)(x)

powinno wywołać:

f1(x);f2(x);f3(x);

Ćwiczenie 4

Zmodyfikuj powyższy szablon tak aby można było mieszać funkcje o różnej liczbie agrgumentów np.:

int f();
void g(double);
void h(double,int);
macro(f,g)(x);

powinno wywołać

f();g(x)}

a

macro(g,h)(3.14,0);

powinno wywołać

g(3.14);h(3.14,0)
Podpowiedź









Rozwiązanie 3

Zobacz plik {mod10/exercises/macro.h}macro.h.

Rozwiązanie 4

Zaczynamy od implemenatcji adaptera {call}. W tym celu defiuniujemy szablon:

template<typename F,typename A1,typename A2> struct call_t2: public std::binary_function<A1, A2, typename functor_traits<F>::result_type> {

 typedef typename functor_traits<F>::result_type result_type;
 typedef typename functor_traits<F>::arg1_type arg1_type;
 typedef typename functor_traits<F>::arg2_type arg2_type;
 F _f;

public:
call_t2(F f):_f(f) {};

który wyposażamy w trzy funkcje:

 result_type call(A1 a1,A2 a2, 

generator<result_type>) {

   return _f();
 };
 result_type call(A1 a1,A2 a2, 

std::unary_function<arg1_type,result_type>) {

   return _f(a1);
 };
 result_type call(A1 a1,A2 a2, 

std::binary_function<arg1_type,arg2_type,result_type>) {

   return _f(a1,a2);
 };

Ostatni argument służy do tylko do przeciążenia funkcji wykorzystanego w operatorze nawiasów:

 result_type operator()(A1 a1,A2 a2) {
   return call(a1,a2,typename functor_traits<F>::f_type());
 }; 

Jak zwykle dodajemy funkcję:

template<typename A1,typename A2,typename F> call_t2<F,A1,A2> call(F f) { return call_t2<F,A1,A2>(f);}

Podobnie definiujemy jednoargumentową wersję tego szablonu. {call_t1} i przeciążoną funkcję:

template<typename A1,typename F> call_t1<F,A1> call(F f) { return call_t1<F,A1>(f);}

Całość kodu znajduje się w pliku {mod10/exercises/call.h}call.h.

Implementując adapter {macro} musimy umieć poznać która z dwu przekazanych funkcji ma wiecej argumentów. Używamy w tym celu szablonu {If_then_else}:

template<typename F1,typename F2> struct macro_type {

 typedef typename If_then_else<
   (size_t)functor_traits<F1>::n_args 
      > =        
   (size_t)functor_traits<F2>::n_args ,
   typename functor_traits<F1>::f_type,
   typename functor_traits<F2>::f_type>::Result  m_type;

};

Korzystając z {macro_type} i {call} możemy zaimplementować szablon:

template<typename F1,typename F2> class macro_t : public macro_type<F1,F2>::m_type {

public:
 typedef void result_type ;

Proszę zwrócić uwagę, że dziedzicząc z { macro_type<F1,F2>::m_type} defiuniujemy poprawne typy argumentów fuktora, ale niekoniecznie dobry typ wartości zwracanej. Dlatego redefinujemy go potem na {void}. Całość kodu znajduje się w pliku {mod10/exercises/mixed_macro.h}mixedmacro.h.