<commit_before>#pragma once
#include <cassert>
#include <functional>
#include <memory>
#include <mutex>
#include <type_traits>
#include <utility>
#include <unordered_map>
#include "observable/subscription.hpp"
#include "observable/value.hpp"
#include "observable/expression/tree.hpp"

namespace observable { inline namespace expr {

//! Expression evaluators can be used to manually evaluate multiple expressions at
//! the same time.
//!
//! You can use this type as-is or extend it.
class expression_evaluator
{
public:
    //! Evaluate all expressions associated with this evaluator instance.
    //!
    //! \warning This method cannot be safely called concurrently.
    void eval_all() const
    {
        for(auto && kv : data_->funs)
            kv.second();
    }

    //! Destructor.
    virtual ~expression_evaluator() { }

private:
    using id = void const *;

    //! Register a new expression to be evaluated by this evaluator.
    //!
    //! \return Instance id that can be used to unregister the expression.
    //! \note This method can be safely called in parallel, from multiple threads.
    template <typename ExpressionType>
    auto insert(ExpressionType * expr)
    {
        assert(expr);

        std::lock_guard<std::mutex> const lock { data_->mutex };
        data_->funs.emplace(expr, [=]() { expr->eval(); });
        return id { expr };
    }

    //! Unregister a previously registered expression.
    //!
    //! \param[in] instance_id Id that has been returned by `insert()`.
    //! \note This method can be safely called in parallel, from multiple threads.
    void remove(id instance_id)
    {
        std::lock_guard<std::mutex> const lock { data_->mutex };
        data_->funs.erase(instance_id);
    }

private:
    struct data {
        std::unordered_map<id, std::function<void()>> funs;
        std::mutex mutex;
    };

    std::shared_ptr<data> data_ { std::make_shared<data>() };

    template <typename ValueType, typename UpdaterType>
    friend class expression;
};

//! Expressions manage expression tree updates.
//!
//! \tparam ValueType The expression's result value type. This is what `get()`
//!                   returns.
//! \tparam EvaluatorType An instance of expression_evaluator or a type derived
//!                       from it.
//! \warning None of the methods in this class can be safely called concurrently.
template <typename ValueType, typename EvaluatorType=expression_evaluator>
class expression : public value_updater<ValueType>
{<commit_msg>Fix expression evaluation order and tests.<commit_after>#pragma once
#include <cassert>
#include <deque>
#include <functional>
#include <memory>
#include <mutex>
#include <type_traits>
#include <utility>
#include "observable/subscription.hpp"
#include "observable/value.hpp"
#include "observable/expression/tree.hpp"

namespace observable { inline namespace expr {

//! Expression evaluators can be used to manually evaluate multiple expressions at
//! the same time.
//!
//! You can use this type as-is or extend it.
class expression_evaluator
{
public:
    //! Evaluate all expressions associated with this evaluator instance.
    //!
    //! \note This method can be safely called in parallel.
    void eval_all() const
    {
        std::lock_guard<std::mutex> const lock { data_->mutex };
        for(auto && p : data_->funs)
            p.second();
    }

    //! Destructor.
    virtual ~expression_evaluator() { }

private:
    // Expressions are inserted and removed in the order in which they are
    // created. Evaluating them in this order guarantees that the evaluation
    // order is correct (insert is called from the constructor, nothing can
    // have a dependency on the inserted expression yet).

    using id = void const *;

    //! Register a new expression to be evaluated by this evaluator.
    //!
    //! \return Instance id that can be used to unregister the expression.
    //! \note This method can be safely called in parallel, from multiple threads.
    template <typename ExpressionType>
    auto insert(ExpressionType * expr)
    {
        assert(expr);
        std::lock_guard<std::mutex> const lock { data_->mutex };

        data_->funs.emplace_back(expr, [=]() { expr->eval(); });
        return id { expr };
    }

    //! Unregister a previously registered expression.
    //!
    //! \param[in] instance_id Id that has been returned by `insert()`.
    //! \note This method can be safely called in parallel, from multiple threads.
    void remove(id instance_id)
    {
        std::lock_guard<std::mutex> const lock { data_->mutex };

        auto const it = find_if(begin(data_->funs),
                                end(data_->funs),
                                [&](auto && p) { return p.first == instance_id; });
        assert(it != end(data_->funs));
        if(it != end(data_->funs))
            data_->funs.erase(it);
    }

private:
    struct data {
        std::deque<std::pair<id, std::function<void()>>> funs;
        std::mutex mutex;
    };

    std::shared_ptr<data> data_ { std::make_shared<data>() };

    template <typename ValueType, typename UpdaterType>
    friend class expression;
};

//! Expressions manage expression tree updates.
//!
//! \tparam ValueType The expression's result value type. This is what `get()`
//!                   returns.
//! \tparam EvaluatorType An instance of expression_evaluator or a type derived
//!                       from it.
//! \warning None of the methods in this class can be safely called concurrently.
template <typename ValueType, typename EvaluatorType=expression_evaluator>
class expression : public value_updater<ValueType>
{
{'fraction_non_alphanumeric': 0.11401803075835248, 'fraction_numerical': 0.0, 'mean_word_length': 2.84375, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 56, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '360273', 'n_tokens_mistral': 1571, 'n_tokens_neox': 1524, 'n_words': 540}