arapelle / arba-evnt

MIT License
0 stars 0 forks source link

Change signal class implementation #7

Open arapelle opened 3 years ago

arapelle commented 3 years ago

Use the famous Fast C++ Delegates:

Fast C++ delegates: https://www.codeproject.com/Articles/1170503/The-Impossibly-Fast-Cplusplus-Delegates-Fixed https://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=1170503 https://codereview.stackexchange.com/questions/14730/impossibly-fast-delegate-in-c11 https://www.codeproject.com/articles/11015/the-impossibly-fast-c-delegates

signal benchmark: https://medium.com/@julienjorge/testing-c-signal-slot-libraries-1994eb120826


mutex_interface_
  virtual void lock() = 0;
  virtual void unlock() = 0;

mutex_adpator_<Mut> : public Mut
  using Mut::Mut;
  virtual void lock() { this->Mut::lock(); }
  virtual void unlock()  { this->Mut::unlock(); }

dummy_mutex_
  virtual void lock() {}
  virtual void unlock()  {}

mutexes_interface_
  virtual ~mutexes_interface() = default;
  virtual mutex_interface& add_list_mutex() = 0;
  virtual mutex_interface& remove_list_mutex() = 0;
  virtual mutex_interface& active_list_mutex() = 0;

mutexes_<Mut>
  Mut add_mut_, remove_mut, active_mut_;
  virtual ~mutexes() = default;
  virtual mutex_interface& add_list_mutex() { return add_mut_; }
  virtual mutex_interface& remove_list_mutex() { return remove_mut_; }
  virtual mutex_interface& active_list_mutex()  { return active_mut_; }

signal
  std::shared_ptr<mutexes_interface_>
  intrusive_list<slot> slots_to_add_;
  intrusive_list<slot> slots_to_remove_;
  intrusive_list<slot> slots_;
  connection connect(slot);
  void disconnect_all_slots();
  void disconnect(slot_function); // if used, connection instances should not be used to disconnect after.

connection
  std::weak_ptr<mutexes_interface_>
  intrusive_weak_ptr<box> slot_box_iwptr
  void disconnect()
  {
    if (sptr m_ptr = mutex_wptr.lock(); m_ptr)
    {
        std::lock_guard lock(*m_ptr);
        slot_box_iptr->remove();
        mutex_wptr.reset();
    }
  }

scoped_connection
arapelle commented 1 year ago
#pragma once

#include <arba/core/memory/intrusive_ref_counter.hpp>
#include <arba/core/memory/intrusive_shared_ptr.hpp>
#include <iostream>

inline namespace arba
{
namespace evnt
{

template <typename... ArgsT>
class signal
{
private:
    class abstract_slot_box : public core::intrusive_ref_counter<>
    {
    public:
        virtual ~abstract_slot_box() = default;
        virtual void execute(ArgsT... args) = 0;

        core::intrusive_shared_ptr<abstract_slot_box> previous, next;
    };
    using abstract_slot_box_isptr = core::intrusive_shared_ptr<abstract_slot_box>;

    class sentinel_slot_box : public abstract_slot_box
    {
    public:
        virtual ~sentinel_slot_box() = default;
        virtual void execute(ArgsT...) { std::abort(); }
    };

    template <class SlotCallbableT>
        requires requires(SlotCallbableT& slot, ArgsT... args)
    {
        { slot(args...) };
    }
    class slot_box : public abstract_slot_box
    {
    public:
        explicit slot_box(SlotCallbableT slot) : slot(std::move(slot)) { std::cout << sizeof(*this) << std::endl; }
        virtual ~slot_box() override = default;
        virtual void execute(ArgsT... args) { slot(args...); }

        SlotCallbableT slot;
    };

public:
    signal();
    ~signal()
    {
        clear();
    }

    void clear()
    {
        while (slot_list_.next.get() != &slot_list_)
        {
            slot_list_.next->next->previous.release();
            slot_list_.next = std::move(slot_list_.next->next);
        }
    }

    template <class SlotCallbableT>
    void connect(SlotCallbableT slot)
    {
        auto* slot_ptr = new slot_box{ std::move(slot) };
        connect_(abstract_slot_box_isptr(slot_ptr));
    }

    template <class InstanceT, class MemFuncT>
    void connect(InstanceT& inst, MemFuncT mem_fn)
    {
        auto* slot_ptr = new slot_box{ [i_ptr = &inst, mem_fn](ArgsT... args){ (i_ptr->*mem_fn)(args...); } };
        connect_(abstract_slot_box_isptr(slot_ptr));
    }

    void emit(ArgsT... args)
    {
        abstract_slot_box* first = slot_list_.next.get();
        abstract_slot_box* last = &slot_list_;
        for (; first != last; first = first->next.get())
            first->execute(args...);
    }

private:
    void connect_(abstract_slot_box_isptr slot_isptr)
    {
        slot_isptr->next = slot_list_.next;
        slot_isptr->next->previous = slot_isptr;
        slot_isptr->previous = abstract_slot_box_isptr(&slot_list_);
        slot_list_.next = slot_isptr;
    }

private:
    sentinel_slot_box slot_list_;
};

template <typename... ArgsT>
signal<ArgsT...>::signal()
{
    core::intrusive_shared_ptr_add_ref(&slot_list_);
    slot_list_.next = core::intrusive_shared_ptr(&slot_list_);
    slot_list_.previous = slot_list_.next;
}

}
}
arapelle commented 1 year ago
#include "cpprd.hpp"
#include <iostream>

class slot_sv_n
{
public:
    void operator()(std::string_view sv, int n)
    {
        std::cout << "slot_sv_n(): " << sv << " " << n << std::endl;
    }

    void execute(std::string_view sv, int n)
    {
        std::cout << "slot_sv_n::execute(): " << sv << " " << n << std::endl;
    }
};

void print_sv_n(std::string_view sv, int n)
{
    std::cout << "print_sv_n(): " << sv << " " << n << std::endl;
}

int main()
{
    evnt::signal<std::string_view, int> source_location_signal;
    source_location_signal.connect([](std::string_view sv, int n){ std::cout << "lambda: " << sv << " " << n << std::endl; });
    source_location_signal.connect(slot_sv_n());
    slot_sv_n sl;
    source_location_signal.connect(sl, &slot_sv_n::execute);
    source_location_signal.connect(&print_sv_n);
    source_location_signal.emit("coucou", 5);

    std::cout << "EXIT_SUCCESS" << std::endl;
    return EXIT_SUCCESS;
}