<commit_before>#pragma once

#include "iobserver.h"
#include <vector>
#include <algorithm>
#include <typeinfo>
#include <functional>
#include <mutex>

namespace gg
{

//!
//! \brief Every IVideoSource that broadcasts
//! video frames needs to implement this interface,
//! which defines the observable (subject / publisher)
//! part of the observer design pattern (aka
//! subscriber-publisher).
//!
//! Note that no new buffer is created when broadcasting
//! data, i.e. every IObserver is responsible for
//! ensuring data is promptly copied to their own
//! data buffer.
//!
class IObservable
{
protected:
    //!
    //! \brief
    //!
    std::vector< std::reference_wrapper<IObserver> > _observers;

    //!
    //! \brief
    //!
    std::mutex _observers_lock;

public:
    //!
    //! \brief
    //!
    virtual ~IObservable()
    {

    }

public:
    //!
    //! \brief Attach given observer to this observable.
    //! If observer already attached, no action is taken,
    //! no action is taken.
    //! \param observer
    //! \throw ObserverError with a detailed message if
    //! attach attempt unsuccessful
    //!
    virtual void attach(IObserver & observer)
    {
        std::lock_guard<std::mutex> lock_guard(_observers_lock);
        if (not attached(observer))
        {
            _observers.push_back(observer);
            if (not attached(observer))
                throw ObserverError("Could not attach observer");
        }
    }

    //!
    //! \brief Detach given observer from this observable
    //! \param observer
    //! \throw ObserverError with a detailed message if
    //! detach attempt unsuccessful
    //!
    virtual void detach(IObserver & observer)
    {
        std::lock_guard<std::mutex> lock_guard(_observers_lock);
        _observers.erase(std::find_if(
                             _observers.begin(),
                             _observers.end(),
                             [&](const std::reference_wrapper<IObserver> &o)
                                {
                                    return &(o.get()) == &observer;
                                }),
                         _observers.end());
        if (attached(observer))
            throw ObserverError("Could not detach observer");
    }

    //!
    //! \brief Notify all attached observers of new data
    //! \param frame
    //!
    virtual void notify(VideoFrame & frame) noexcept
    {
        std::lock_guard<std::mutex> lock_guard(_observers_lock);
        for (IObserver & observer : _observers)
        {
            observer.update(frame);
        }
    }

protected:
    //!
    //! \brief Check whether passed observer is already
    //! attached
    //! \param observer
    //! \return
    //!
    bool attached(const IObserver & observer) const noexcept
    {
        return std::find_if(
                    _observers.begin(),
                    _observers.end(),
                    [&](const std::reference_wrapper<IObserver> &o)
                       {
                           return &(o.get()) == &observer;
                       }) != _observers.end();
    }
};

}<commit_msg>Issue #86: clarification added to attached function of IObservable<commit_after>#pragma once

#include "iobserver.h"
#include <vector>
#include <algorithm>
#include <typeinfo>
#include <functional>
#include <mutex>

namespace gg
{

//!
//! \brief Every IVideoSource that broadcasts
//! video frames needs to implement this interface,
//! which defines the observable (subject / publisher)
//! part of the observer design pattern (aka
//! subscriber-publisher).
//!
//! Note that no new buffer is created when broadcasting
//! data, i.e. every IObserver is responsible for
//! ensuring data is promptly copied to their own
//! data buffer.
//!
class IObservable
{
protected:
    //!
    //! \brief
    //!
    std::vector< std::reference_wrapper<IObserver> > _observers;

    //!
    //! \brief
    //!
    std::mutex _observers_lock;

public:
    //!
    //! \brief
    //!
    virtual ~IObservable()
    {

    }

public:
    //!
    //! \brief Attach given observer to this observable.
    //! If observer already attached, no action is taken,
    //! no action is taken.
    //! \param observer
    //! \throw ObserverError with a detailed message if
    //! attach attempt unsuccessful
    //!
    virtual void attach(IObserver & observer)
    {
        std::lock_guard<std::mutex> lock_guard(_observers_lock);
        if (not attached(observer))
        {
            _observers.push_back(observer);
            if (not attached(observer))
                throw ObserverError("Could not attach observer");
        }
    }

    //!
    //! \brief Detach given observer from this observable
    //! \param observer
    //! \throw ObserverError with a detailed message if
    //! detach attempt unsuccessful
    //!
    virtual void detach(IObserver & observer)
    {
        std::lock_guard<std::mutex> lock_guard(_observers_lock);
        _observers.erase(std::find_if(
                             _observers.begin(),
                             _observers.end(),
                             [&](const std::reference_wrapper<IObserver> &o)
                                {
                                    return &(o.get()) == &observer;
                                }),
                         _observers.end());
        if (attached(observer))
            throw ObserverError("Could not detach observer");
    }

    //!
    //! \brief Notify all attached observers of new data
    //! \param frame
    //!
    virtual void notify(VideoFrame & frame) noexcept
    {
        std::lock_guard<std::mutex> lock_guard(_observers_lock);
        for (IObserver & observer : _observers)
        {
            observer.update(frame);
        }
    }

protected:
    //!
    //! \brief Check whether passed observer is already
    //! attached. Because this function is protected, no
    //! thread-safety operation is performed inside it.
    //! \param observer
    //! \return
    //! \sa _observers_lock
    //!
    bool attached(const IObserver & observer) const noexcept
    {
        return std::find_if(
                    _observers.begin(),
                    _observers.end(),
                    [&](const std::reference_wrapper<IObserver> &o)
                       {
                           return &(o.get()) == &observer;
                       }) != _observers.end();
    }
};

}
{'fraction_non_alphanumeric': 0.12070840453627467, 'fraction_numerical': 0.00031070374398011494, 'mean_word_length': 1.7666523420713365, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '77079', 'n_tokens_mistral': 1752, 'n_tokens_neox': 1681, 'n_words': 472}