nuclear@0: /************************************************************************************ nuclear@0: nuclear@0: PublicHeader: Kernel nuclear@0: Filename : OVR_Observer.h nuclear@0: Content : Observer pattern nuclear@0: Created : June 20, 2014 nuclear@0: Author : Chris Taylor nuclear@0: nuclear@0: Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. nuclear@0: nuclear@0: Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); nuclear@0: you may not use the Oculus VR Rift SDK except in compliance with the License, nuclear@0: which is provided at the time of installation or download, or which nuclear@0: otherwise accompanies this software in either electronic or hard copy form. nuclear@0: nuclear@0: You may obtain a copy of the License at nuclear@0: nuclear@0: http://www.oculusvr.com/licenses/LICENSE-3.2 nuclear@0: nuclear@0: Unless required by applicable law or agreed to in writing, the Oculus VR SDK nuclear@0: distributed under the License is distributed on an "AS IS" BASIS, nuclear@0: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. nuclear@0: See the License for the specific language governing permissions and nuclear@0: limitations under the License. nuclear@0: nuclear@0: ************************************************************************************/ nuclear@0: nuclear@0: #ifndef OVR_Observer_h nuclear@0: #define OVR_Observer_h nuclear@0: nuclear@0: #include "OVR_Types.h" nuclear@0: #include "OVR_Atomic.h" nuclear@0: #include "OVR_RefCount.h" nuclear@0: #include "OVR_Delegates.h" nuclear@0: #include "OVR_Array.h" nuclear@0: #include "OVR_String.h" nuclear@0: #include "OVR_Hash.h" nuclear@0: nuclear@0: namespace OVR { nuclear@0: nuclear@0: template class Observer; nuclear@0: template class ObserverScope; nuclear@0: template class ObserverHash; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // Observer pattern nuclear@0: nuclear@0: // An Observer will observe a Subject. The Subject can emit callbacks that get nuclear@0: // serviced by the Observers. nuclear@0: nuclear@0: // The trickiest part of this is the shutdown code. nuclear@0: // To simplify shutdown, the Observer is a reference-counted object divorced nuclear@0: // from the handler that is called. To avoid misuse, the ObserverScope object nuclear@0: // is provided to ensure that the Shutdown() method is called when it goes out nuclear@0: // of scope. nuclear@0: nuclear@0: // The Observer<> class doubles as the subject class. nuclear@0: // To avoid misuse, assertions are added if a subject tries to observe, or if nuclear@0: // an observer tries to be watched. nuclear@0: nuclear@0: /* nuclear@0: Usage example: nuclear@0: nuclear@0: Say we want to invoke a handler with the signature: nuclear@0: nuclear@0: void MyHandler(int i, bool b); nuclear@0: nuclear@0: The corresponding delegate type is: nuclear@0: nuclear@0: typedef Delegate2 Handler; nuclear@0: nuclear@0: Note: The return value will be ignored for the Observer pattern. nuclear@0: nuclear@0: For this example there are two classes, one that emits events and another nuclear@0: that listens for events: nuclear@0: */ nuclear@0: nuclear@0: /* nuclear@0: Event emitter example: nuclear@0: nuclear@0: class MyEmitter nuclear@0: { nuclear@0: ObserverScope TheSubject; nuclear@0: nuclear@0: public: nuclear@0: void ClearAllListeners() nuclear@0: { nuclear@0: TheSubject.ReleaseAll(); nuclear@0: } nuclear@0: nuclear@0: void CallListeners(int x, bool y) nuclear@0: { nuclear@0: TheSubject->Call(x, y); nuclear@0: } nuclear@0: nuclear@0: Observer* GetSubject() nuclear@0: { nuclear@0: return TheSubject; nuclear@0: } nuclear@0: }; nuclear@0: */ nuclear@0: nuclear@0: /* nuclear@0: Event listener example: nuclear@0: nuclear@0: class MyListener nuclear@0: { nuclear@0: ObserverScope TheObserver; nuclear@0: nuclear@0: void OnEvent(int x, bool y) nuclear@0: { nuclear@0: // Handle event here nuclear@0: } nuclear@0: nuclear@0: public: nuclear@0: MyListener() nuclear@0: { nuclear@0: TheObserver.SetHandler( nuclear@0: Handler::FromMember(this) nuclear@0: ); nuclear@0: } nuclear@0: nuclear@0: void ClearListener() nuclear@0: { nuclear@0: TheObserver.ReleaseAll(); nuclear@0: } nuclear@0: nuclear@0: void ListenTo(Observer* emitter) nuclear@0: { nuclear@0: TheObserver->Observe(emitter); nuclear@0: } nuclear@0: }; nuclear@0: */ nuclear@0: nuclear@0: /* nuclear@0: Usage example: nuclear@0: nuclear@0: MyListener listener; nuclear@0: MyEmitter emitter; nuclear@0: nuclear@0: // To listen to an emitter, nuclear@0: listener.ListenTo(emitter.GetSubject()); nuclear@0: nuclear@0: // To call the listeners, nuclear@0: emitter.CallListeners(22, true); nuclear@0: */ nuclear@0: nuclear@0: template nuclear@0: class Observer : public RefCountBase< Observer > nuclear@0: { nuclear@0: friend class ObserverScope; nuclear@0: friend class ObserverHash; nuclear@0: nuclear@0: public: nuclear@0: typedef Observer ThisType; nuclear@0: typedef DelegateT Handler; nuclear@0: nuclear@0: protected: nuclear@0: bool IsShutdown; // Flag to indicate that the object went out of scope nuclear@0: mutable Lock TheLock; // Lock to synchronize calls and shutdown nuclear@0: Array< Ptr< ThisType > > References; // List of observed or observing objects nuclear@0: Handler TheHandler; // Observer-only: Handler for callbacks nuclear@0: nuclear@0: Observer() : nuclear@0: IsShutdown(false) nuclear@0: { nuclear@0: TheHandler.Invalidate(); nuclear@0: } nuclear@0: Observer(Handler handler) : nuclear@0: IsShutdown(false), nuclear@0: TheHandler(handler) nuclear@0: { nuclear@0: } nuclear@0: ~Observer() nuclear@0: { nuclear@0: OVR_ASSERT(References.GetSizeI() == 0); nuclear@0: } nuclear@0: nuclear@0: public: nuclear@0: void SetHandler(Handler handler) nuclear@0: { nuclear@0: OVR_ASSERT(References.GetSizeI() == 0); nuclear@0: TheHandler = handler; nuclear@0: } nuclear@0: nuclear@0: // Release references and prevent further actions nuclear@0: void Shutdown() nuclear@0: { nuclear@0: Lock::Locker locker(&TheLock); nuclear@0: IsShutdown = true; nuclear@0: References.ClearAndRelease(); nuclear@0: } nuclear@0: nuclear@0: // Get count of references held nuclear@0: int GetSizeI() const nuclear@0: { nuclear@0: Lock::Locker locker(&TheLock); nuclear@0: return References.GetSizeI(); nuclear@0: } nuclear@0: nuclear@0: // Observe a subject nuclear@0: bool Observe(ThisType *subject) nuclear@0: { nuclear@0: OVR_ASSERT(TheHandler.IsValid()); nuclear@0: nuclear@0: if (!subject) nuclear@0: { nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: Lock::Locker locker(&TheLock); nuclear@0: nuclear@0: if (IsShutdown) nuclear@0: { nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: if (!subject->SubjectAddObserver(this)) nuclear@0: { nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: References.PushBack(subject); nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: protected: nuclear@0: // Subject function: AddObserver() nuclear@0: // Returns true if the observer was added nuclear@0: bool SubjectAddObserver(ThisType* observer) nuclear@0: { nuclear@0: OVR_ASSERT(!TheHandler.IsValid()); nuclear@0: nuclear@0: if (!observer) nuclear@0: { nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: Lock::Locker locker(&TheLock); nuclear@0: nuclear@0: if (IsShutdown) nuclear@0: { nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: const int count = References.GetSizeI(); nuclear@0: for (int i = 0; i < count; ++i) nuclear@0: { nuclear@0: if (References[i] == observer) nuclear@0: { nuclear@0: // Already watched nuclear@0: return true; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: References.PushBack(observer); nuclear@0: nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: public: nuclear@0: // Subject function: Call() nuclear@0: #define OVR_OBSERVER_CALL_BODY(params) \ nuclear@0: bool callSuccess = false; \ nuclear@0: Lock::Locker locker(&TheLock); \ nuclear@0: int count = References.GetSizeI(); \ nuclear@0: for (int i = 0; i < count; ++i) \ nuclear@0: { \ nuclear@0: if (!References[i]->IsShutdown) \ nuclear@0: { \ nuclear@0: OVR_ASSERT(References[i]->TheHandler.IsValid()); \ nuclear@0: References[i]->TheHandler params; \ nuclear@0: callSuccess = true; \ nuclear@0: } \ nuclear@0: if (References[i]->IsShutdown) \ nuclear@0: { \ nuclear@0: References.RemoveAt(i); \ nuclear@0: --i; --count; \ nuclear@0: } \ nuclear@0: } \ nuclear@0: return callSuccess; nuclear@0: nuclear@0: // Call: Various parameter counts nuclear@0: // Returns true if a call was made nuclear@0: bool Call() nuclear@0: { nuclear@0: OVR_OBSERVER_CALL_BODY(()) nuclear@0: } nuclear@0: template nuclear@0: bool Call(Param1& p1) nuclear@0: { nuclear@0: OVR_OBSERVER_CALL_BODY((p1)) nuclear@0: } nuclear@0: template nuclear@0: bool Call(Param1* p1) nuclear@0: { nuclear@0: OVR_OBSERVER_CALL_BODY((p1)) nuclear@0: } nuclear@0: template nuclear@0: bool Call(Param1& p1, Param2& p2) nuclear@0: { nuclear@0: OVR_OBSERVER_CALL_BODY((p1, p2)) nuclear@0: } nuclear@0: template nuclear@0: bool Call(Param1* p1, Param2* p2) nuclear@0: { nuclear@0: OVR_OBSERVER_CALL_BODY((p1, p2)) nuclear@0: } nuclear@0: template nuclear@0: bool Call(Param1& p1, Param2& p2, Param3& p3) nuclear@0: { nuclear@0: OVR_OBSERVER_CALL_BODY((p1, p2, p3)) nuclear@0: } nuclear@0: template nuclear@0: bool Call(Param1* p1, Param2* p2, Param3* p3) nuclear@0: { nuclear@0: OVR_OBSERVER_CALL_BODY((p1, p2, p3)) nuclear@0: } nuclear@0: nuclear@0: #undef OVR_OBSERVER_CALL_BODY nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // ObserverScope nuclear@0: nuclear@0: // Scoped shutdown of the Observer object nuclear@0: template nuclear@0: class ObserverScope : public NewOverrideBase nuclear@0: { nuclear@0: Ptr< Observer > TheObserver; nuclear@0: DelegateT TheHandler; nuclear@0: nuclear@0: void Shutdown() nuclear@0: { nuclear@0: if (TheObserver) nuclear@0: { nuclear@0: TheObserver->Shutdown(); nuclear@0: TheObserver.Clear(); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: public: nuclear@0: ObserverScope() nuclear@0: { nuclear@0: TheObserver = *new Observer; nuclear@0: } nuclear@0: ~ObserverScope() nuclear@0: { nuclear@0: Shutdown(); nuclear@0: } nuclear@0: nuclear@0: // Release all references and recreate it nuclear@0: void ReleaseAll() nuclear@0: { nuclear@0: Shutdown(); nuclear@0: TheObserver = *new Observer; nuclear@0: if (TheHandler.IsValid()) nuclear@0: { nuclear@0: TheObserver->SetHandler(TheHandler); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: void SetHandler(DelegateT handler) nuclear@0: { nuclear@0: TheHandler = handler; nuclear@0: TheObserver->SetHandler(handler); nuclear@0: } nuclear@0: nuclear@0: Observer* GetPtr() nuclear@0: { nuclear@0: return TheObserver.GetPtr(); nuclear@0: } nuclear@0: Observer* operator->() nuclear@0: { nuclear@0: return TheObserver.GetPtr(); nuclear@0: } nuclear@0: const Observer* operator->() const nuclear@0: { nuclear@0: return TheObserver.GetPtr(); nuclear@0: } nuclear@0: operator Observer*() nuclear@0: { nuclear@0: return TheObserver.GetPtr(); nuclear@0: } nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // ObserverHash nuclear@0: nuclear@0: // A hash containing Observers nuclear@0: template nuclear@0: class ObserverHash : public NewOverrideBase nuclear@0: { nuclear@0: public: nuclear@0: ObserverHash() {} nuclear@0: ~ObserverHash() {Clear();} nuclear@0: void Clear() nuclear@0: { nuclear@0: Lock::Locker locker(&TheLock); nuclear@0: typename OVR::Hash< String, Ptr >, OVR::String::HashFunctor >::Iterator it = _Hash.Begin(); nuclear@0: for( it = _Hash.Begin(); it != _Hash.End(); ++it ) nuclear@0: { nuclear@0: Ptr > o = it->Second; nuclear@0: o->Shutdown(); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: Ptr > GetSubject(OVR::String key) nuclear@0: { nuclear@0: Lock::Locker locker(&TheLock); nuclear@0: Ptr > *o = _Hash.Get(key); nuclear@0: if (o) nuclear@0: return (*o); nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: // Add handler to new observer with implicit creation of subject. nuclear@0: void AddObserverToSubject(OVR::String key, Observer *observer) nuclear@0: { nuclear@0: Lock::Locker locker(&TheLock); nuclear@0: Ptr > *subjectPtr = _Hash.Get(key); nuclear@0: nuclear@0: if (subjectPtr==NULL) nuclear@0: { nuclear@0: Ptr > subject = *new Observer(); nuclear@0: _Hash.Add(key, subject); nuclear@0: observer->Observe(subject); nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: observer->Observe(*subjectPtr); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: void RemoveSubject(OVR::String key) nuclear@0: { nuclear@0: Lock::Locker locker(&TheLock); nuclear@0: Ptr > *subjectPtr = _Hash.Get(key); nuclear@0: if (subjectPtr!=NULL) nuclear@0: { nuclear@0: (*subjectPtr)->Shutdown(); nuclear@0: _Hash.Remove(key); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: protected: nuclear@0: OVR::Hash< OVR::String, Ptr >, OVR::String::HashFunctor > _Hash; nuclear@0: Lock TheLock; // Lock to synchronize calls and shutdown nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: } // namespace OVR nuclear@0: nuclear@0: #endif // OVR_Observer_h