newsmemory-ios-sdk/Frameworks/RCT-Folly.xcframework/ios-arm64/Headers/folly/ObserverContainer.h

1027 lines
34 KiB
C++

/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <bitset>
#include <vector>
#include <glog/logging.h>
#include <folly/ConstructorCallbackList.h>
#include <folly/Function.h>
#include <folly/Optional.h>
#include <folly/ScopeGuard.h>
#include <folly/io/async/DestructorCheck.h>
#include <folly/small_vector.h>
/**
* Tooling that makes it easier to design observable objects and observers.
*/
namespace folly {
/**
* Interface for store of pointers to observers.
*/
template <typename Observer>
class ObserverContainerStoreBase {
public:
using observer_type = Observer;
// ObserverContainerStore stores shared_ptr<Observer> objects.
//
// To support observer objects that are NOT managed by a shared_ptr, the
// encapsulating ObserverContainer wraps unmanaged pointers inside of
// shared_ptrs, but sets an empty deleter for the shared_ptr, so that the
// pointer remains unmanaged.
//
// As a result, it is possible for the shared_ptrs maintained by the store to
// be "unmanaged". The type alias `MaybeManagedObserverPointer` is used to
// ensure that this detail is apparent in other parts of the container code.
using MaybeManagedObserverPointer = std::shared_ptr<Observer>;
virtual ~ObserverContainerStoreBase() = default;
/**
* Add an observer pointer to the store.
*
* @param observer Observer to add.
* @return Whether observer was added (not already present).
*/
virtual bool add(MaybeManagedObserverPointer observer) = 0;
/**
* Remove an observer pointer from the store.
*
* @param observer Observer to remove.
* @return Whether observer found and removed from store.
*/
virtual bool remove(MaybeManagedObserverPointer observer) = 0;
/**
* Get number of observers in store.
*
* If called while the store is being iterated, the returned value may not
* reflect changes that occurred (e.g., observers added or removed) during
* iteration.
*
* @return Number of observers in store.
*/
virtual size_t size() const = 0;
/**
* Policy that determines how invokeForEachObserver handles mutations.
*/
enum class InvokeWhileIteratingPolicy {
InvokeAdded, // if observer added, invoke fn for it
DoNotInvokeAdded, // if observer added, do not invoke fn for it
CheckNoChange, // observers must not be added or removed during iteration
CheckNoAdded // observers must not be added during iteration
};
/**
* Invoke function for each observer in the store.
*
* @param fn Function to call for each observer in store.
* @param policy InvokeWhileIteratingPolicy policy.
*/
virtual void invokeForEachObserver(
folly::Function<void(MaybeManagedObserverPointer&)>&& fn,
const InvokeWhileIteratingPolicy policy) = 0;
/**
* Invoke function for each observer in the store.
*
* @param fn Function to call for each observer in store.
* @param policy InvokeWhileIteratingPolicy policy.
*/
virtual void invokeForEachObserver(
folly::Function<void(Observer*)>&& fn,
const InvokeWhileIteratingPolicy policy) {
invokeForEachObserver(
[fnL = std::move(fn)](MaybeManagedObserverPointer& observer) mutable {
fnL(observer.get());
},
policy);
}
};
/**
* Policy for ObserverContainerStore.
*
* Defines the udnerlying container type and the default size.
*/
template <unsigned int ReserveElements = 2>
struct ObserverContainerStorePolicyDefault {
template <typename Observer>
using container = std::conditional_t<
!kIsMobile,
folly::small_vector<Observer, ReserveElements>,
std::vector<Observer>>;
const static unsigned int reserve_elements = ReserveElements;
};
/**
* Policy-based implementation of ObserverContainerStoreBase.
*/
template <
typename Observer,
typename Policy = ObserverContainerStorePolicyDefault<>>
class ObserverContainerStore : public ObserverContainerStoreBase<Observer> {
public:
using Base = ObserverContainerStoreBase<Observer>;
using InvokeWhileIteratingPolicy = typename Base::InvokeWhileIteratingPolicy;
/**
* Construct a new store, reserving as configured.
*/
ObserverContainerStore() { observers_.reserve(Policy::reserve_elements); }
/**
* Add an observer pointer to the store.
*
* @param observer Observer to add.
* @return Whether observer was added (not already present).
*/
bool add(std::shared_ptr<Observer> observer) override {
// attempts to add the same observer multiple times are rejected
if (std::find(observers_.begin(), observers_.end(), observer) !=
observers_.end()) {
return false;
}
if (iterating_) {
CHECK(maybeCurrentIterationPolicy_.has_value());
const auto& policy = maybeCurrentIterationPolicy_.value();
switch (policy) {
case InvokeWhileIteratingPolicy::InvokeAdded:
case InvokeWhileIteratingPolicy::DoNotInvokeAdded:
break;
case InvokeWhileIteratingPolicy::CheckNoChange:
folly::terminate_with<std::runtime_error>(
"Cannot add observers while iterating "
"per current iteration policy (CheckNoChange)");
break;
case InvokeWhileIteratingPolicy::CheckNoAdded:
folly::terminate_with<std::runtime_error>(
"Cannot add observers while iterating "
"per current iteration policy (CheckNoAdded)");
break;
}
}
observers_.insert(observers_.end(), observer);
return true;
}
/**
* Remove an observer pointer from the store.
*
* @param observer Observer to remove.
* @return Whether observer found and removed from store.
*/
bool remove(std::shared_ptr<Observer> observer) override {
const auto it = std::find(observers_.begin(), observers_.end(), observer);
if (it == observers_.end()) {
return false;
}
// if store is currently being iterated, set this element to nullptr and it
// will be cleaned up after iteration is completed, else erase immediately.
if (iterating_) {
CHECK(maybeCurrentIterationPolicy_.has_value());
const auto& policy = maybeCurrentIterationPolicy_.value();
switch (policy) {
case InvokeWhileIteratingPolicy::InvokeAdded:
case InvokeWhileIteratingPolicy::DoNotInvokeAdded:
break;
case InvokeWhileIteratingPolicy::CheckNoChange:
folly::terminate_with<std::runtime_error>(
"Cannot remove observers while iterating "
"per current iteration policy (CheckNoChange)");
break;
case InvokeWhileIteratingPolicy::CheckNoAdded:
break;
}
*it = nullptr;
removalDuringIteration_ = true;
} else {
observers_.erase(it);
}
return true;
}
/**
* Get number of observers in store.
*
* If called while the store is being iterated, the returned value may not
* reflect changes that occurred (e.g., observers added or removed) during
* iteration.
*
* @return Number of observers in store.
*/
size_t size() const override { return observers_.size(); }
/**
* Invoke function for each observer in the store.
*
* @param fn Function to call for each observer in store.
* @param policy InvokeWhileIteratingPolicy policy.
*/
void invokeForEachObserver(
folly::Function<void(typename Base::MaybeManagedObserverPointer&)>&& fn,
const typename Base::InvokeWhileIteratingPolicy policy) noexcept
override {
CHECK(!iterating_)
<< "Nested iteration of ObserverContainer is prohibited.";
CHECK(!maybeCurrentIterationPolicy_.has_value())
<< "Nested iteration of ObserverContainer is prohibited.";
iterating_ = true;
maybeCurrentIterationPolicy_ = policy;
SCOPE_EXIT {
if (removalDuringIteration_) {
// observers removed while we were iterating through container;
// remove elements for which the element value is null
observers_.erase(
std::remove_if(
observers_.begin(),
observers_.end(),
[](const auto& elem) { return elem == nullptr; }),
observers_.end());
}
iterating_ = false;
maybeCurrentIterationPolicy_ = folly::none;
removalDuringIteration_ = false;
};
const auto numObserversAtStart = observers_.size();
// iterate through the list using indexes, not iterators, so that the list
// can mutate during iteration...
for (typename container_type::size_type idx = 0;
// observers_.size() cannot decrease during iteration, so it should be
// insignificantly faster to check the single size in the common case.
idx < numObserversAtStart ||
(idx < observers_.size() &&
policy == InvokeWhileIteratingPolicy::InvokeAdded);
idx++) {
auto& observer = observers_.at(idx);
if (!observer) { // empty space in list caused by incomplete removal
continue;
}
fn(observer);
}
}
using Base::invokeForEachObserver;
private:
using container_type =
typename Policy::template container<std::shared_ptr<Observer>>;
// The actual list of observers.
container_type observers_;
// Whether we are actively iterating through the list of observers.
bool iterating_{false};
// If we are actively iterating, the corresponding InvokeWhileIteratingPolicy.
folly::Optional<InvokeWhileIteratingPolicy> maybeCurrentIterationPolicy_;
// Whether a removal or addition occurred while we iterating through the list.
bool removalDuringIteration_{false};
};
/**
* Policy for ObserverContainerBase.
*
* @tparam EventEnum Enum of events that observers can subscribe to.
* Each event must have a unique integer value greater
* than zero.
*
* @tparam BitsetSize Size of bitset, must be greater than or equal to the
* number of events in EventEnum.
*/
template <typename EventEnum, size_t BitsetSize>
struct ObserverContainerBasePolicyDefault {
static constexpr size_t bitset_size() { return BitsetSize; }
using event_enum = EventEnum;
};
/**
* Base ObserverContainer and definition of Observers.
*/
template <
typename ObserverInterface,
typename Observed,
typename ContainerPolicy>
class ObserverContainerBase {
public:
using interface_type = ObserverInterface;
using observed_type = Observed;
using policy_type = ContainerPolicy;
using EventEnum = typename ContainerPolicy::event_enum;
using EventEnumIntT = std::underlying_type_t<EventEnum>;
virtual ~ObserverContainerBase() = default;
/**
* EventSet is used to keep track of the observer events that are enabled.
*/
class ObserverEventSet {
public:
ObserverEventSet() : bitset_(0) {}
/**
* Enables all events.
*/
void enableAllEvents() { bitset_.set(); }
/**
* Enables the events passed in the initializer list.
*
* @param eventsEnums Events to enable.
*/
template <typename... EventEnums>
void enable(EventEnums... eventEnums) {
for (auto&& event : {eventEnums...}) {
const auto eventAsInt = static_cast<EventEnumIntT>(event);
bitset_.set(eventAsInt);
}
}
/**
* Returns whether the event passed in is enabled.
*
* @param event Event to check.
* @return Whether the passed event is enabled.
*/
bool isEnabled(const EventEnum event) const {
const auto eventAsInt = static_cast<EventEnumIntT>(event);
return bitset_.test(eventAsInt);
}
/**
* Builder that makes it easier to pass EventSet to Observer constructor.
*/
class Builder {
public:
explicit Builder() = default;
/**
* Enables all events.
*/
Builder&& enableAllEvents() {
set_.enableAllEvents();
return std::move(*this);
}
/**
* Enables the events passed in the intiailizer list.
*
* @param events Events to enable.
*/
template <typename... EventEnums>
Builder&& enable(EventEnums... eventEnums) {
set_.enable(eventEnums...);
return std::move(*this);
}
/**
* Returns the EventSet that has been built.
*/
ObserverEventSet build() && { return set_; }
private:
ObserverEventSet set_;
};
private:
std::bitset<ContainerPolicy::bitset_size()> bitset_{0};
};
/**
* Observer base interface.
*
* This interface includes the events exposed by the subject's observer
* interface and the set of events that are provided by the ObserverContainer
* (attached/detached/moved/destoyed). It also defines how observers subscribe
* to specific events made available by the subject.
*/
class ObserverBase : public ObserverInterface, public DestructorCheck {
public:
using observed_type = Observed;
using interface_type = ObserverInterface;
using EventSet = ObserverEventSet;
using EventSetBuilder = typename ObserverEventSet::Builder;
~ObserverBase() override = default;
/**
* Construct a new observer with no event subscriptions.
*/
ObserverBase() {}
/**
* Construct a new observer subscribed to events in the passed EventSet.
*/
explicit ObserverBase(EventSet eventSet) : eventSet_(eventSet) {}
/**
* Base class that can be used to pass context about move operation.
*/
class MoveContext {};
/**
* Base class that can be used to pass context about why object destroyed.
*/
class DestroyContext {};
/**
* Invoked when this observer is attached to an object.
*
* @param obj Object that observer is now attached to.
*/
virtual void attached(Observed* /* obj */) noexcept {}
/**
* Invoked if this observer is detached from an object.
*
* @param obj Object that observer is no longer attached to.
*/
virtual void detached(Observed* /* obj */) noexcept {}
/**
* Invoked when an observed object's destructor is invoked.
*
* Destruction of the observed object implicitly implies detached, and thus
* detached will not be called if an object is destroyed.
*
* @param obj Object being destroyed.
* @param ctx Additional info about what triggered destruction.
* Not available unless provided by the implementation;
* if not supported it is a nullptr.
*/
virtual void destroyed(
Observed* /* obj */, DestroyContext* /* ctx */) noexcept {}
/**
* Invoked when object being observed changes due to move construction.
*
* @param oldObj Object previously being observed.
* @param newObj Object now being observed.
* @param ctx Additional info about what triggered the move.
* Not available unless provided by the implementation;
* if not supported it is a nullptr.
*/
virtual void moved(
Observed* /* oldObj */,
Observed* /* newObj */,
MoveContext* /* ctx */) noexcept {}
/**
* Proxy function used to invoke a method defined in the observer interface.
*
* Can be overridden to enable composition of observers, including event bus
* architectures in which multiple handlers act on an event.
*
* Implementations can remove themselves and add/remove other observers from
* the container when handling this call. If new observers are added to the
* container, invokeInterfaceMethod will be called on those new observers
* as well. If you want to avoid this in your observer implementation, delay
* mutation of the container until postInvokeInterfaceMethod is called.
*
* @param obj Object associated with observer event.
* @param fn Function that will invoke the method associated with
* an observer event, passing any event context.
* @param maybeEvent The event enum associated with the invocation.
*/
virtual void invokeInterfaceMethod(
Observed* obj,
folly::Function<void(ObserverBase*, Observed*)>& fn,
folly::Optional<EventEnum> /* maybeEvent */) noexcept {
fn(this, obj);
}
/**
* Invoked after invokeInterfaceMethod has completed for all observers.
*
* Can be used to delay mutation of the container after processing of an
* event has completed. Implementations can remove themselves and add/remove
* other observers from the container when handling this call. However, this
* function will only be called for the set of observers in the container
* when the preceding call to invokeInterfaceMethod finished.
*
* @param obj Object associated with observer event.
*/
virtual void postInvokeInterfaceMethod(Observed* /* obj */) noexcept {}
/**
* Returns the EventSet containing the events the observer wants.
*/
const EventSet& getEventSet() const noexcept { return eventSet_; }
private:
const EventSet eventSet_;
};
/**
* Observer interface.
*
* The interface between an observer container and observers in the container.
*
* This interface includes methods that are called upon relevant changes to
* the observer's status in a container (added/removedFromObserverContainer).
*
* An observer must not be destroyed while it is in a container. This can be
* accomplished by removing the observer from the container on its destruction
* or delaying destruction.
*
* Typical use cases should not attempt to implement this interface and should
* instead use a specialization such as ManagedObserver.
*/
class Observer : public ObserverBase {
public:
using observed_type = Observed;
using interface_type = ObserverInterface;
using EventSet = ObserverEventSet;
using EventSetBuilder = typename ObserverEventSet::Builder;
~Observer() override = default;
/**
* Construct a new observer with no event subscriptions.
*/
Observer() : ObserverBase() {}
/**
* Construct a new observer subscribed to events in the passed EventSet.
*/
explicit Observer(EventSet eventSet) : ObserverBase(eventSet) {}
/**
* Invoked when this observer has been added to an observer container.
*
* For the typical observer container implementation a call to `attached`
* will proceed a call to this method.
*
* The observer implementation must ensure that it remains alive as long as
* it is in this container.
*
* @param ctr Container observer has been added to.
*/
virtual void addedToObserverContainer(
ObserverContainerBase* ctr) noexcept = 0;
/**
* Invoked when this observer has been removed from an observer container.
*
* For the typical observer container implementation a call to `detached`
* will have occurred before this method is called.
*
* @param ctr Container observer has been removed from.
*/
virtual void removedFromObserverContainer(
ObserverContainerBase* ctr) noexcept = 0;
/**
* Invoked when this observer is moved from one container to another.
*
* Occurs in the case of move construction of a new object during which the
* observers in the observer container are shifted from the old object to
* the new object.
*
* @param oldCtr Container observer has been removed from.
* @param newCtr Container observer has been added to.
*/
virtual void movedToObserverContainer(
ObserverContainerBase* oldCtr,
ObserverContainerBase* newCtr) noexcept = 0;
};
/**
* Returns the object associated with the container (e.g., observed object).
*
* @return Return object associated with container or nullptr.
*/
virtual Observed* getObject() const = 0;
/**
* Adds an observer to the container.
*
* If the observer is already in the container, this is a no-op.
*
* @param observer Observer to add.
*/
virtual void addObserver(std::shared_ptr<Observer> observer) = 0;
/**
* Adds an observer to the container.
*
* If the observer is already in the container, this is a no-op.
*
* @param observer Observer to add.
*/
virtual void addObserver(Observer* observer) {
// create a shared_ptr holding an unmanaged ptr
// this does not trigger control block allocation
return addObserver(
std::shared_ptr<Observer>(std::shared_ptr<void>(), observer));
}
/**
* Removes an observer from the container.
*
* @param observer Observer to remove.
* @return Whether the observer was found and removed.
*/
virtual bool removeObserver(std::shared_ptr<Observer> observer) = 0;
/**
* Removes an observer from the container.
*
* @param observer Observer to remove.
* @return Whether the observer was found and removed.
*/
virtual bool removeObserver(Observer* observer) {
// create a shared_ptr holding an unmanaged ptr
// this does not trigger control block allocation
return removeObserver(
std::shared_ptr<Observer>(std::shared_ptr<void>(), observer));
}
/**
* Get number of observers in container.
*
* @return Number of observers in container.
*/
size_t numObservers() const { return getStoreConst().size(); }
/**
* Get a list of observers in the container of type T.
*
* @tparam T Type of observer to find.
* @return List of observers in the container of type T.
*/
template <typename T = Observer>
std::vector<T*> findObservers() {
static_assert(
std::is_base_of<Observer, T>::value,
"T must derive from ObserverContainer::Observer");
std::vector<T*> matchingObservers;
using InvokeWhileIteratingPolicy = typename ObserverContainerStoreBase<
Observer>::InvokeWhileIteratingPolicy;
getStore().invokeForEachObserver(
[&matchingObservers](Observer* observer) {
auto castPtr = dynamic_cast<T*>(observer);
if (castPtr) {
matchingObservers.emplace_back(castPtr);
}
},
InvokeWhileIteratingPolicy::CheckNoChange);
return matchingObservers;
}
/**
* Get all observers.
*
* @return List of observers in the container.
*/
std::vector<Observer*> getObservers() { return findObservers<Observer>(); }
/**
* Returns if any observer in the container is subscribed to a given event.
*
* TODO(bschlinker): The current implementation scans the entire container to
* search for an observer subscribed to the requested event; we should cache
* this information instead and update the cache on observer add / remove.
*
* @tparam event Event in EventEnum.
* @return If there are observers subscribed to the given event.
*/
template <EventEnum event>
bool hasObserversForEvent() {
bool foundObserverWithEvent = false;
using InvokeWhileIteratingPolicy = typename ObserverContainerStoreBase<
Observer>::InvokeWhileIteratingPolicy;
getStore().invokeForEachObserver(
[&foundObserverWithEvent](Observer* observer) {
foundObserverWithEvent |= observer->getEventSet().isEnabled(event);
},
InvokeWhileIteratingPolicy::CheckNoChange);
return foundObserverWithEvent;
}
/**
* Helper class for observers that attach to single object / container.
*
* Does not have any thread safety, and thus can only be used if the observer
* is driven exclusively by the same thread as the thread that controls the
* object being observed.
*
* Tracks the container the observer is in (if any). If the observer's
* destructor is triggered while it is in an container, it will be removed
* from the container during the destruction process.
*/
class ManagedObserver : public Observer {
public:
using Observer::Observer;
using EventSet = typename Observer::EventSet;
using EventSetBuilder = typename Observer::EventSetBuilder;
~ManagedObserver() override { detach(); }
/**
* Detach the observer (if currently attached).
*
* If the observer is not currently attached, this is a no-op.
*
* @return If successfully detached.
*/
bool detach() {
if (!ctr_) {
return false;
}
return ctr_->removeObserver(this);
}
/**
* Return if the observer is observing an object.
*
* @return If observer is observing an object.
*/
bool isObserving() const {
return ctr_ != nullptr && ctr_->getObject() != nullptr;
}
/**
* Get the object that is being observed or nullptr.
*
* @return Object being observed.
*/
Observed* getObservedObject() const {
if (!ctr_) {
return nullptr;
}
return ctr_->getObject();
}
private:
void addedToObserverContainer(
ObserverContainerBase* ctr) noexcept override {
CHECK(!ctr_);
ctr_ = ctr;
}
void removedFromObserverContainer(
ObserverContainerBase* ctr) noexcept override {
CHECK_EQ(ctr_, ctr);
ctr_ = nullptr;
}
void movedToObserverContainer(
ObserverContainerBase* oldCtr,
ObserverContainerBase* newCtr) noexcept override {
CHECK_EQ(ctr_, oldCtr);
CHECK_NE(ctr_, newCtr);
ctr_ = newCtr;
}
// Container the observer is in (or nullptr).
ObserverContainerBase* ctr_{nullptr};
};
protected:
virtual ObserverContainerStoreBase<Observer>& getStore() = 0;
virtual const ObserverContainerStoreBase<Observer>& getStoreConst() const = 0;
void invokeInterfaceMethodImpl(
Observed* observed,
folly::Function<void(ObserverBase*, Observed*)>&& fn,
const folly::Optional<EventEnum> maybeEvent = folly::none) noexcept {
using InvokeWhileIteratingPolicy = typename ObserverContainerStoreBase<
Observer>::InvokeWhileIteratingPolicy;
getStore().invokeForEachObserver(
[observed, maybeEvent, &fn](Observer* observer) {
if (!maybeEvent.has_value() ||
observer->getEventSet().isEnabled(maybeEvent.value())) {
observer->invokeInterfaceMethod(observed, fn, maybeEvent);
}
},
InvokeWhileIteratingPolicy::InvokeAdded);
getStore().invokeForEachObserver(
[observed, maybeEvent](ObserverBase* observer) {
if (!maybeEvent.has_value() ||
observer->getEventSet().isEnabled(maybeEvent.value())) {
observer->postInvokeInterfaceMethod(observed);
}
},
InvokeWhileIteratingPolicy::DoNotInvokeAdded);
}
};
/**
* Policy-based implementation of ObserverContainerBase.
*/
template <
typename ObserverInterface,
typename Observed,
typename ContainerPolicy,
typename StorePolicy = ObserverContainerStorePolicyDefault<>,
std::size_t MaxConstructorCallbacks = 4>
class ObserverContainer : public ObserverContainerBase<
ObserverInterface,
Observed,
ContainerPolicy> {
public:
using ContainerBase =
ObserverContainerBase<ObserverInterface, Observed, ContainerPolicy>;
using Observer = typename ContainerBase::Observer;
using EventEnum = typename ContainerBase::EventEnum;
using StoreBase = ObserverContainerStoreBase<Observer>;
using ContainerConstructorCallbackList =
ConstructorCallbackList<ObserverContainer, MaxConstructorCallbacks>;
explicit ObserverContainer(Observed* obj)
: obj_(CHECK_NOTNULL(obj)), constructorCallbackList_(this) {}
ObserverContainer(Observed* obj, ObserverContainer&& observerContainer)
: obj_(CHECK_NOTNULL(obj)), constructorCallbackList_(this) {
using InvokeWhileIteratingPolicy =
typename StoreBase::InvokeWhileIteratingPolicy;
observerContainer.getStore().invokeForEachObserver(
[this, &observerContainer](
typename StoreBase::MaybeManagedObserverPointer& observer) {
// observer may be a managed pointer (e.g., a shared_ptr), and
// invokeForEachObserver passes a reference, so we need to copy the
// observer object before calling remove so that it will not be
// destroyed upon removal from the old ObserverContainer
auto observerCopy = observer;
CHECK_NOTNULL(observerCopy.get());
// remove
const bool removed = observerContainer.getStore().remove(observer);
CHECK(removed);
// add to new, operating solely on observerCopy
const bool added = getStore().add(observerCopy);
CHECK(added);
observerCopy->movedToObserverContainer(&observerContainer, this);
observerCopy->moved(
observerContainer.getObject(), obj_, nullptr /* ctx */);
},
InvokeWhileIteratingPolicy::CheckNoAdded);
}
~ObserverContainer() override {
using InvokeWhileIteratingPolicy =
typename StoreBase::InvokeWhileIteratingPolicy;
getStore().invokeForEachObserver(
[this](Observer* observer) {
DestructorCheck::Safety dc(*observer);
observer->destroyed(obj_, nullptr /* ctx */);
if (!dc.destroyed()) {
observer->removedFromObserverContainer(this);
}
},
InvokeWhileIteratingPolicy::CheckNoAdded);
}
/**
* Returns the object associated with the container (e.g., observed object).
*
* @return Return object associated with container or nullptr.
*/
Observed* getObject() const override { return obj_; }
using ContainerBase::addObserver;
using ContainerBase::removeObserver;
/**
* Adds an observer to the container.
*
* If the observer is already in the container, this is a no-op.
*
* @param observer Observer to add.
*/
void addObserver(std::shared_ptr<Observer> observer) override {
CHECK_NOTNULL(observer.get());
if (getStore().add(observer)) {
DestructorCheck::Safety dc(*observer);
observer->addedToObserverContainer(this);
if (!dc.destroyed()) {
observer->attached(obj_);
}
}
}
/**
* Removes an observer from the container.
*
* @param observer Observer to remove.
* @return Whether the observer was found and removed.
*/
bool removeObserver(std::shared_ptr<Observer> observer) override {
CHECK_NOTNULL(observer.get());
if (getStore().remove(observer)) {
DestructorCheck::Safety dc(*observer);
observer->detached(obj_);
if (!dc.destroyed()) {
observer->removedFromObserverContainer(this);
}
return true;
}
return false;
}
/**
* Add a callback fired each time obj of the observed type is constructed.
*
* Uses ConstructorCallbackList. Can be used to attach observers to all
* objects of a given type.
*
* @throw std::length_error() if installing callback would exceed max allowed
*/
static void addConstructorCallback(
typename ContainerConstructorCallbackList::Callback cb) {
ContainerConstructorCallbackList::addCallback(std::move(cb));
}
/**
* Invokes an observer interface method on observers subscribed to an event.
*
* See instead `invokeInterfaceMethodAllObservers` to invoke an interface
* method on all observers without filtering based on observer event
* subscription.
*
* @tparam event Associated event in EventEnum. The passed function will
* only be called for observers subscribed to this event.
* @param fn Function to call for each observer that takes a pointer
* to the observer and invokes the interface method.
*/
template <EventEnum event>
void invokeInterfaceMethod(
folly::Function<void(ObserverInterface*, Observed*)>&& fn) noexcept {
this->invokeInterfaceMethodImpl(obj_, std::move(fn), event);
}
/**
* Invokes an observer interface method on all observers.
*
* @param fn Function to call for each observer that takes a pointer
* to the observer and invokes the interface method.
*/
void invokeInterfaceMethodAllObservers(
folly::Function<void(ObserverInterface*, Observed*)>&& fn) noexcept {
this->invokeInterfaceMethodImpl(obj_, std::move(fn), folly::none);
}
ObserverContainer(const ObserverContainer&) = delete;
ObserverContainer(ObserverContainer&&) = delete;
ObserverContainer& operator=(const ObserverContainer&) = delete;
ObserverContainer& operator=(ObserverContainer&& rhs) = delete;
private:
StoreBase& getStore() override { return store_; }
const StoreBase& getStoreConst() const override { return store_; }
// Object being observed.
Observed* obj_{nullptr};
// Store that contains the observers in the container.
ObserverContainerStore<Observer, StorePolicy> store_;
// Enables objects to register constructor callbacks.
//
// This can be used to enable observers to be attached to all objects of a
// given type immediately upon object construction.
//
// Initialized last and in ObserverContainer instead of ObserverContainerBase
// to ensure that the container has completed initialization and is ready to
// add observers when any constructor callbacks are called.
ContainerConstructorCallbackList constructorCallbackList_{this};
};
} // namespace folly