#include <functional>
#include <vector>
#include <iostream>
#include <memory>

template < typename CB > struct event
{
    std::vector< std::function<CB> > call_back ;

    template < typename FN > event<CB>& operator+= ( FN fn )
    {
       call_back.emplace_back(fn) ;
       return *this ;
    }

};

struct test
{
    const std::string& name() const { return name_ ; }

    void name( const std::string& new_name )
    { name_ = new_name ; fire_name_changed() ; }

    event< void( const test&, const std::string& ) > name_changed ;

    template < typename FN, typename... ARGS > void subscribe( FN fn, ARGS&&... args )
    {
        using namespace std::placeholders ;
        name_changed.call_back.emplace_back( std::bind( fn, args..., _1, _2 ) ) ;
    }

    private:

         std::string name_ = "anonymous" ;

         void fire_name_changed()
         { for( const auto& f : name_changed.call_back ) f( *this, name() ) ; }
};

void a_function( const test& sender, const std::string& value )
{
    std::cout << "a_function: -\n    name of object at "
               << std::addressof(sender) << "  changed to: '" << value << "'\n\n" ;
}

void another_fun( const std::string& msg, const test& sender, const std::string& value )
{
    std::cout << "another_fun: -\n    msg: '" << msg
               << "'\n    sender: " << std::addressof(sender)
               << "\n    value: '" << value << "'\n\n" ;
}

int main()
{
    test t ;
    t.name_changed += []( const test& sender, const std::string& value )
                      {
                          std::cout << "lambda: -\n    name of object at "
                                     << std::addressof(sender)
                                     << "  changed to: '" << value << "'\n\n" ;
                      } ;

    t.name_changed += a_function ;

    t.subscribe( another_fun, "**** name of object is changed! ****" ) ;

    struct recvr
    {
        void handle_it( const test& sender, const std::string& value ) const
                      {
                          std::cout << "recvr::handle_it -\n    name of object at "
                                     << std::addressof(sender)
                                     << "  changed to: '" << value << "'\n\n" ;
                      }
    };

    recvr r ;
    t.subscribe( &recvr::handle_it, r ) ;

    t.name( "Cambalinho" ) ; // change the name to test it
}
