user249375
user249375

Reputation:

Observer pattern in c++

I am having a bit of trouble with observer pattern I have a beverage class that uses decorator fine. I am trying to implement observer to let an observer( say a cell phone/text message) in this case know when there order is done. i wont include the beverage/decorator classes since they work fine.

In main i was going to do something like this:

Subject mySubject = new Subject();
Observer myObserver1 = new Observer1();
Observer myObserver2 = new Observer2();

// register observers
mySubject.Register(myObserver1);
mySubject.Register(myObserver2);

mySubject.Notify("message 1");
mySubject.Notify("message 2");

I have a subject class, observer, phone behavior class and a cellhone1,cellphone2 classes..

Here is the subject class

#ifndef _SUBJECT_
#define _SUBJECT_

//#include "Starbuzz.h"
//#include "Starbuzz2.h"
#include "Observer.h"
#include <list>

namespace CoffeeHouse {
namespace Observers {


class Subject  {


private:
std::list< Observer* > observers;

public:
Subject();
~Subject();

void Subject::Register(Observer observer)
{

//if (!observers.(observer))
//{

observers.insert(observer);
}
//}

//void Unregister(Observer observer)
//{
// if observer is in the list, remove
//if (observers.Contains(observer))
//{
//observers.Remove(observer);
//}
//}

void Notify(std::string message)
{
//need loop
Observer observer;
observer.update(message);


}
//}
//}

//void Subject::registerObserver( Observer* o ) { assert( o );
    //_observers.push_front(o);
//}
//void Subject::removeObserver( Observer* o ) { assert( o );
//  _observers.remove(o);
//}
//void Subject::notifyObservers() const {
    //for( std::list< Observer* >::iterator iterator = _observers.begin();   _observers.end() != iterator; ++iterator ) {
        //Observer* observer = *iterator;
        //observer->update(message);
    //}
//}
};
} // namespace Observer
} 


#endif

Here is the observer class

#ifndef _OBSERVER_
#define _OBSERVER_

#include <string>

namespace CoffeeHouse {
namespace Observers {

class Subject;

class Observer {

//public: virtual ~Observer() = 0; 

public: virtual void Update(std::string message) = 0;
};

here is the phone behavior class

#ifndef _PHONEBEHAVIOR_
#define _PHONEBEHAVIOR_

namespace CoffeeHouse {
namespace Observer {

class PhoneBehavior {
public: virtual void Update() const = 0;
};
protected: virtual ~PhoneBehavior() = 0 {

};


};

} // namespace Observer
} //

here is cellphone 1

#ifndef _CELLPHONE1_
#define _CELLPHONE1_

namespace CoffeeHouse {
namespace Observer {
include<iostream>
class CellPhone1: public Observer, public PhoneBehavior {
public: 
    CellPhone1();
    ~CellPhone1();
virtual void Update(std::string message)
 {
   std::cout << "CellPhone1: " << message;
}
};

} // namespace Observer
} //

#endif

here re the errors i am getting..

error C2259: 'CoffeeHouse::Observers::Observer' : cannot instantiate abstract class
1>        due to following members:
1>        'void CoffeeHouse::Observers::Observer::update(std::string)' : is abstract
see declaration of 'CoffeeHouse::Observers::Observer::update'
error C2661: 'std::list<_Ty>::insert' : no overloaded function takes 1 arguments
with[_Ty=CoffeeHouse::Observers::Observer *

error C2259: 'CoffeeHouse::Observers::Observer' : cannot instantiate abstract class
due to following members:
'void CoffeeHouse::Observers::Observer::update(std::string)' : is abstract
observer.h(15) : see declaration of 'CoffeeHouse::Observers::Observer::update

When i click on error "cannot instantiate abstract class" it takes me to:

void Subject::Register(Observer observer)

I understand that Abstract classes are made such that they cannot be instantiated!

How could i do an update then?? any suggestions of a better way?

I appreciate any help!

Upvotes: 0

Views: 1620

Answers (3)

amightywind
amightywind

Reputation: 31

You can make anything observable with this:

https://simmesimme.github.io/tutorials/2015/09/20/signal-slot

#ifndef SIGNAL_H
#define SIGNAL_H

#include <functional>
#include <map>

//! A signal object may call multiple slots with the same
//! signature. You can connect functions to the signal which will be
//! called when the emit() method on the signal object is invoked. Any
//! argument passed to emit() will be passed to the given functions.
template <typename... Args>
class signal
{
public:
  //! Default constructor.
  signal(): m_slots(), m_current_id(0) {}

  //! Copy constructor. (deleted)
  signal(signal const& other) = delete;

  //! Assignment operator. (deleted)
  signal& operator=(signal const& other) = delete;

  //! Connects a member function to this signal.
  //!
  //! @code{.cpp}
  //! signal<> s;
  //! int id = s.connect_member(&myclass, MyClass::method);
  //! @endcode
  //!
  //! @param[in] instance Pointer to the instance to connect.
  //! @param[in] func Pointer to the member func to connect.
  //! @return id of object connected, to be used to disconnect.
  template <typename T>
  int connect_member(T *inst, void (T::*func)(Args...))
  {
    return connect([=](Args... args) {(inst->*func)(args...);});
  }

  //! Connects a const member function to this signal.
  //!
  //! @code{.cpp}
  //! signal<> s;
  //! int id = s.connect_member(&myclass, MyClass::method);
  //! @endcode
  //!
  //! @param[in] instance Pointer to the instance to connect.
  //! @param[in] func Pointer to the member func to connect.
  //! @return id of object connected, to be used to disconnect.
  template <typename T>
  int connect_member(T *inst, void (T::*func)(Args...) const)
  {
    return connect([=](Args... args) { (inst->*func)(args...);});
  }

  //! Connects a std::function function wrapper to this signal.
  //!
  //! This method can be used to adapt many kinds of callable objects
  //! to a signal.
  //!
  //! @code{.cpp}
  //! signal<> s;
  //! std::function<void()> f = std::bind(&signal_utest::test0, this);
  //! int id = s.connect(f);
  //! @endcode
  //!
  //! @param[in] slot Function object invoked on emit().
  //! @return id of object connected, to be used to disconnect.
  int connect(std::function<void(Args...)> const& slot) const
  {
    m_slots.insert(std::make_pair(++m_current_id, slot));
    return m_current_id;
  }

  //! Disconnects a previously connected function.
  //!
  //! @param[in] id Id returned by connect function.
  void disconnect(int id) const
  {
    m_slots.erase(id);
  }

  // Disconnects all previously connected functions.
  void disconnect_all() const
  {
    m_slots.clear();
  }

  //! Calls all connected functions.
  void emit(Args... p)
  {
    for (auto it : m_slots)
      it.second(p...);
  }

private:
  mutable std::map<int, std::function<void(Args...)>> m_slots;
  mutable int m_current_id;
};

#endif /* SIGNAL_H */

with tests:

  signal<> s0;
  int id0 = -1;
  UTEST_CHECK(id0 != 1);
  id0 = s0.connect_member(this, &signal_utest::test0);
  UTEST_CHECK(id0 == 1);
  UTEST_CHECK(get_test0() == 0);
  s0.emit();
  UTEST_CHECK(get_test0() == 1);

  signal<int> s1;
  UTEST_CHECK(s1.connect_member(this, &signal_utest::test1) == 1);
  UTEST_CHECK(test1() == 0);
  s1.emit(1);
  UTEST_CHECK(test1() == 1);

  signal<int, int> s2;
  UTEST_CHECK(s2.connect_member(this, &signal_utest::test2) == 1);
  UTEST_CHECK(test2() == 0);
  s2.emit(1, 1);
  UTEST_CHECK(test2() == 2);

Upvotes: 0

Josh Matthews
Josh Matthews

Reputation: 13026

One error is that Subject::Register is currently taking an Observer object, instead of a pointer to an Observer. This means that you're trying to instantiate an abstract object, which is illegal.

Upvotes: 1

Rob S.
Rob S.

Reputation: 3609

Problems I see:

  • You use a namespace and a class called Observer in PhoneBehavior and CellPhone1 class source files.
  • In CellPhone1 class source file you don't #include <string> or use the std namespace for the string in void Update(string message)
  • In PhoneBehavior class source file virtual void update() should have probably have a capital "U" to stay consistent with your coding style and because that's how it's called in the CellPhone1 class source file.
  • In CellPhone1 class source file you have Cout <<"CellPhone1:" + message);. It should be std::cout << "CellPhone1: " << message;
  • In CellPhone1 class source file you need to #include <iostream>
  • You have too many closing braces in the PhoneBehavior class source file. And only the closing brace corresponding to the opening brace of your class PhoneBehavior should have a semicolon after it.

Edit: When I say "class source file" I'm assuming your project setup with CellPhone1.cpp , PhoneBehavior.cpp files based on the way you presented your question.

Upvotes: 1

Related Questions