//autor: Adam Magrzyk G1-TIZ nr albumu: 200798
//
//      Praca domowa z ZPR
//      Temat: boost::function
//      Autor: Adam Magrzyk
//      Grupa dziekanska/specjalnosc: G1-TIZ
//

//W kontekście boost::function zostało omówione:
//wywoływanie funkcji
//wywoływanie metod
//wywoływanie obiektów funkcyjnych
//realizacja wywołań zwrotnych


#include <iostream>
#include <vector>
//włączenie pliku nagłówkowego
#include <boost/function.hpp>


//funkcja składowana
bool simple_function(int i, int j)
{
    std::cout<<"wywolanie funkcji skladowanej, argumenty: i = "<<i<<" j = "<<j<<std::endl;
    return i>1;
}
        
class simple_class
{
  public:
    //metoda składowana
    bool simple_function(int i, int j)
    {
      std::cout<<"wywolanie metody skladowanej, argumenty: i = "<<i<<" j = "<<j<<std::endl;
      return i>1;
    }
};

//obiekt funkcyjny z pamięcią stanu
class keeping_state
{
  public:
    keeping_state() : m_total(0) {}

    int operator()(int i)
    {
      m_total += i;
      return m_total;
    }

    int getTotal()
    {
      return m_total;
    }

  private:
    int m_total;
};

//funkcja do wywołania zwrotnego
void callback_function(int i)
{
  std::cout<<"(callback_function) Zauwazono zmiane wartosci. Nowa wartosc "<<i<<std::endl;
}

//funkcja do wywołania zwrotnego
void callback_function1(int i)
{
  std::cout<<"(callback_function1) Zauwazono zmiane wartosci. Nowa wartosc "<<i<<std::endl;
}


class notifier
{
  public:
    //dodawanie wywołania zwrotnego
    template <typename T> void add_observer(T t)
    {
      m_vec.push_back(function_type(t));
    }

    void change_value(int i)
    {
      m_value = i;
      //wywołanie zarejestrowanych wywołań zwrotnych z parametrem m_value
      for(std::size_t i = 0; i < m_vec.size(); ++i)
      {
	m_vec[i](m_value);
      }
    }

  private:
    typedef boost::function<void (int)> function_type;
    //kolekcja zarejestrowanych wywołań zwrotnych
    std::vector<function_type> m_vec;
    int m_value;
};


int main(int argc, char** argv)
{
  //***********************************************************************************
  //function dla funkcji przyjmującej dwa argumenty typu int i zwracającej bool
  //deklaracja obiektu boost::function według tzw. składni zalecanej:
  //typ zwracany jako pierwszy argument szablonu
  //list argumentów wywołania podawana w nawiasach i rozdzielona przecinkami
  boost::function<bool (int, int)> f1;
  //przypisanie do obiektu typu function adresu funkcji
  f1=&simple_function;
  //wywołanie funkcji
  f1(1, 1);

  //deklaracja obiektu boost::function według tzw. składni zgodnej:
  //do nazwy klasy function dodawany jest przyrostek informujący o liczbie argumentów przyjmowanych w wywołaniu
  //typ zwracany jako pierwszy argument szablonu
  //pozostałe argumenty szablonu to lista argumentów wywołania funkcji lub obiektu funkcyjnego
  boost::function2<bool, short, short> f2;
  //funkcja z argumentami dającymi się niejawnie konwertować do typu argumentów użytych w konstruktorze obiektu boost::function
  f2=&simple_function;
  //wywołanie funkcji
  f2((short)2, (short)2);
  //***********************************************************************************


  //***********************************************************************************
  //function dla metody przyjmującej dwa argumenty typu int i zwracającej bool
  //obiekt klasy przekazywany przez wskażnik - istnieje
  //możliwość przekazywania obiektu klasy przez wartość
  boost::function<bool(simple_class*, int ,int)> f3;
  simple_class s;

  ////////////////////////////////////////////////////////////////////////////////////
  //f3(&s, 3, 3); wywołanie pustego obiektu function - wyjątek boost::bad_function_call
  ////////////////////////////////////////////////////////////////////////////////////

  //przypisanie do obiektu typu function adresu metody
  f3=&simple_class::simple_function;
  //wywołanie funkcji na rzecz konkretnego obiektu klasy simple_class
  f3(&s, 3, 3);
  //***********************************************************************************

  //***********************************************************************************
  //wywołanie przez function obiektu funkcyjnego - prosty sposób na zachowanie pamięci stanu
  //Uwaga: domyślne zachowanie boost::function polega na kopiowaniu obiektu funkcyjnego - 
  //f4 i f5 w przykładzie zawierają własne kopie obiektu ks
  keeping_state ks;
  boost::function<int (int)> f4;
  //do f3 kopiowany jest obiekt ks
  f4 = ks;
  boost::function<int (int)> f5;
  //do f4 kopiowany jest obiekt ks
  f5 = ks;
  
  std::cout<<"Dodajemy 10. Biezaca suma: "<<f4(10)<<std::endl;
  std::cout<<"Dodajemy 10. Biezaca suma: "<<f5(10)<<std::endl;
  std::cout<<"Koncowy stan obiektu: "<<ks.getTotal()<<std::endl;

  //Wyjście programu:
  //Dodajemy 10. Biezaca suma: 10
  //Dodajemy 10. Biezaca suma: 10
  //Koncowy stan obiektu: 0
  //***********************************************************************************

  //***********************************************************************************
  //Alternatywa w postaci kopiowania do boost::function obiektu klasy boost::reference_wrapper
  //reprezentującego referencję oryginalnego obiektu funkcyjnego
  //boost::ref zwraca obiekt klasy boost::reference_wrapper przechowujący referencję obiektu funkcyjnego
  keeping_state ks1;
  boost::function<int (int)> f6;
  //do f6 kopiowana jest referencja na obiekt ks1
  f6 = boost::ref(ks1);
  boost::function<int (int)> f7;
  //do f7 kopiowana jest referencja na obiekt ks1
  f7 = boost::ref(ks1);
  
  std::cout<<"Dodajemy 10. Biezaca suma: "<<f5(10)<<std::endl;
  std::cout<<"Dodajemy 10. Biezaca suma: "<<f6(10)<<std::endl;
  std::cout<<"Koncowy stan obiektu: "<<ks1.getTotal()<<std::endl;

  //Wyjście programu:
  //Dodajemy 10. Biezaca suma: 10
  //Dodajemy 10. Biezaca suma: 10
  //Koncowy stan obiektu: 20
  //***********************************************************************************

  //***********************************************************************************
  //Wykorzystanie boost::function do realizacji mechanizmu wywołań zwrotnych
  notifier n;
  n.add_observer(&callback_function);
  n.add_observer(&callback_function1);

  n.change_value(31);
  //***********************************************************************************

  return (0);
}