Skip to content

File Event.hpp

File List > astrea > astro > astro > propagation > event_detection > Event.hpp

Go to the documentation of this file

#pragma once

#include <units/units.hpp>

#include <astro/platforms/Vehicle.hpp>
#include <astro/state/State.hpp>

namespace astrea {
namespace astro {

template <typename T>
concept HasGetName = requires(const T event) {
    { event.get_name() } -> std::same_as<std::string>;
};

template <typename T>
concept HasMeasureEvent = requires(const T event, const Time& time, const State& state, const Vehicle& vehicle) {
    { event.measure_event(time, state, vehicle) } -> std::same_as<Unitless>;
};

template <typename T>
concept HasIsTerminal = requires(const T event) {
    { event.is_terminal() } -> std::same_as<bool>;
};

template <typename T>
concept HasTriggerEvent = requires(const T event, const Time& time, State& state, Vehicle& vehicle) {
    { event.trigger_action(time, state, vehicle) } -> std::same_as<void>;
};


template <typename T>
concept IsUserDefinedEvent = requires(T) {
    std::is_same<T, remove_cv_ref<T>>::value;
    std::is_default_constructible<T>::value;
    std::is_copy_constructible<T>::value;
    std::is_move_constructible<T>::value;
    std::is_destructible<T>::value;
    requires HasGetName<T>;
    requires HasMeasureEvent<T>;
    requires HasIsTerminal<T>;
};

namespace detail {

struct EventInnerBase {

    virtual ~EventInnerBase() {}

    virtual std::string get_name() const = 0;

    virtual Unitless measure_event(const Time& time, const State& state, const Vehicle& vehicle) const = 0;

    virtual bool is_terminal() const = 0;

    virtual void trigger_action(const Time& time, State& state, Vehicle& vehicle) const = 0;

    virtual std::unique_ptr<EventInnerBase> clone() const = 0;

    virtual const void* get_ptr() const = 0;

    virtual void* get_ptr() = 0;
};

template <typename T>
struct EventInner final : public EventInnerBase {

    EventInner() = default;

    EventInner(const EventInner&) = delete;

    EventInner(EventInner&&) = delete;

    EventInner& operator=(const EventInner&) = delete;

    EventInner& operator=(EventInner&&) = delete;

    explicit EventInner(const T& x) :
        _value(x)
    {
    }

    explicit EventInner(T&& x) :
        _value(std::move(x))
    {
    }

    std::string get_name() const override final { return _value.get_name(); }

    Unitless measure_event(const Time& time, const State& state, const Vehicle& vehicle) const override final
    {
        return _value.measure_event(time, state, vehicle);
    }

    bool is_terminal() const override final { return _value.is_terminal(); }

    void trigger_action(const Time& time, State& state, Vehicle& vehicle) const override final
    {
        return trigger_action_impl(_value, time, state, vehicle);
    }

    template <typename U>
        requires(HasTriggerEvent<U>)
    void trigger_action_impl(const U& value, const Time& time, State& state, Vehicle& vehicle) const
    {
        value.trigger_action(time, state, vehicle);
    }

    template <typename U>
        requires(!HasTriggerEvent<U>)
    void trigger_action_impl(const U& value, const Time& time, State& state, Vehicle& vehicle) const
    {
    }

    std::unique_ptr<EventInnerBase> clone() const final { return std::make_unique<EventInner>(_value); }

    const void* get_ptr() const final { return &_value; }

    void* get_ptr() final { return &_value; }

    T _value; 
};

} // namespace detail

class Event; // Forward declaration of the Event class

template <typename T>
concept IsGenericallyConstructableEvent = requires(T) {
    requires IsUserDefinedEvent<T>;
    std::negation<std::is_same<Event, remove_cv_ref<T>>>::value;
};


class Event {

  public:
    Event();

  private:
    void generic_ctor_impl() {}

  public:
    template <typename T>
        requires(IsGenericallyConstructableEvent<T>)
    explicit Event(T&& x) :
        _ptr(std::make_unique<detail::EventInner<remove_cv_ref<T>>>(std::forward<T>(x)))
    {
        generic_ctor_impl();
    }

    Event(const Event&);

    Event(Event&&) noexcept;

    Event& operator=(Event&&) noexcept;

    Event& operator=(const Event&);

    template <typename T>
        requires(IsGenericallyConstructableEvent<T>)
    Event& operator=(T&& x)
    {
        return (*this) = Event(std::forward<T>(x));
    }

    template <typename T>
        requires(IsGenericallyConstructableEvent<T>)
    const T* extract() const noexcept
    {
        auto p = static_cast<const detail::EventInner<T>*>(ptr());
        return p == nullptr ? nullptr : &(p->_value);
    }

    Unitless measure_event(const Time& time, const State& state, const Vehicle& vehicle) const
    {
        return ptr()->measure_event(time, state, vehicle);
    }

    bool is_terminal() const { return ptr()->is_terminal(); }

    void trigger_action(const Time& time, State& state, Vehicle& vehicle) const
    {
        return ptr()->trigger_action(time, state, vehicle);
    }

    std::string get_name() const { return ptr()->get_name(); }

    const void* get_ptr() const;

    void* get_ptr();

  private:
    std::unique_ptr<detail::EventInnerBase> _ptr; 

    detail::EventInnerBase const* ptr() const
    {
        assert(_ptr.get() != nullptr);
        return _ptr.get();
    }

    detail::EventInnerBase* ptr()
    {
        assert(_ptr.get() != nullptr);
        return _ptr.get();
    }
};

} // namespace astro
} // namespace astrea