Add multiple ty to post_event!.
This commit is contained in:
parent
cf0ddcb406
commit
3e871ccc8d
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "eventbus"
|
name = "eventbus"
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
authors = ["SoniEx2 <endermoneymod@gmail.com>"]
|
authors = ["SoniEx2 <endermoneymod@gmail.com>"]
|
||||||
description = "Safe, fast and concurrent event system, inspired by the MinecraftForge event bus."
|
description = "Safe, fast and concurrent event system, inspired by the MinecraftForge event bus."
|
||||||
keywords = ["event", "safe", "fast", "concurrent", "bus"]
|
keywords = ["event", "safe", "fast", "concurrent", "bus"]
|
||||||
|
@ -10,10 +10,14 @@ repository = "https://cybre.tech/SoniEx2/rust.eventbus"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lazy_static = "1.1.0"
|
lazy_static = "1.1.0"
|
||||||
anymap = "0.12.1"
|
anymap = "0.12.1"
|
||||||
|
itertools = "0.7.11"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.2.5"
|
criterion = "0.2.5"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = ["compiletest"]
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "post_single_benchmark"
|
name = "post_single_benchmark"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "eventbus-compiletest"
|
||||||
|
version = "0.0.0"
|
||||||
|
authors = ["SoniEx2 <endermoneymod@gmail.com>"]
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
compiletest_rs = { version = "0.3", features = ["stable"] }
|
||||||
|
eventbus = { path = ".." }
|
|
@ -0,0 +1,3 @@
|
||||||
|
$ cargo test
|
||||||
|
|
||||||
|
boilerplate from: https://github.com/dtolnay/ref-cast/tree/ea05783b5754fc01baf671f97edf120a3f4afc7f/compiletest
|
|
@ -0,0 +1,17 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate eventbus;
|
||||||
|
|
||||||
|
use eventbus::{Event, EventBus};
|
||||||
|
|
||||||
|
struct MyEvent {
|
||||||
|
i: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Event for MyEvent {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_hygiene() {
|
||||||
|
let bus = [EventBus::new()];
|
||||||
|
let mut event = MyEvent { i: 3 };
|
||||||
|
post_event!(&bus[CANCELLABLE.load(::std::sync::atomic::Ordering::Relaxed)], &mut event, MyEvent); //~ ERROR cannot find value `CANCELLABLE` in this scope
|
||||||
|
}
|
|
@ -10,9 +10,8 @@ struct MyEvent {
|
||||||
impl Event for MyEvent {
|
impl Event for MyEvent {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_hygiene() {
|
fn test_hygiene() {
|
||||||
let bus = [EventBus::new()];
|
let bus = [EventBus::new()];
|
||||||
let mut event = MyEvent { i: 3 };
|
let mut event = MyEvent { i: 3 };
|
||||||
post_event!(&bus[EVENT_ID.load(::std::sync::atomic::Ordering::Relaxed)], MyEvent, &mut event);
|
post_event!(&bus[EVENT_ID.load(::std::sync::atomic::Ordering::Relaxed)], &mut event, MyEvent); //~ ERROR cannot find value `EVENT_ID` in this scope
|
||||||
}
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
#![cfg(test)]
|
||||||
|
|
||||||
|
extern crate compiletest_rs as compiletest;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
fn run_mode(mode: &'static str) {
|
||||||
|
let mut config = compiletest::Config::default();
|
||||||
|
|
||||||
|
config.mode = mode.parse().expect("invalid mode");
|
||||||
|
config.target_rustcflags = Some("-L ../target/debug/deps".to_owned());
|
||||||
|
if let Ok(name) = env::var("TESTNAME") {
|
||||||
|
config.filter = Some(name);
|
||||||
|
}
|
||||||
|
config.src_base = mode.into();
|
||||||
|
|
||||||
|
compiletest::run_tests(&config);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compile_fail() {
|
||||||
|
run_mode("compile-fail");
|
||||||
|
}
|
99
src/lib.rs
99
src/lib.rs
|
@ -31,7 +31,7 @@ macro_rules! register_hook {
|
||||||
static EVENT_ID: ::std::sync::atomic::AtomicUsize = std::sync::atomic::ATOMIC_USIZE_INIT;
|
static EVENT_ID: ::std::sync::atomic::AtomicUsize = std::sync::atomic::ATOMIC_USIZE_INIT;
|
||||||
static EVENT_ID_INIT: ::std::sync::Once = std::sync::ONCE_INIT;
|
static EVENT_ID_INIT: ::std::sync::Once = std::sync::ONCE_INIT;
|
||||||
// no generics allowed.
|
// no generics allowed.
|
||||||
static DUMMY: ::std::marker::PhantomData<dyn Fn(&mut $t) + Send + Sync> = ::std::marker::PhantomData;
|
static _DUMMY: ::std::marker::PhantomData<dyn Fn(&mut $t) + Send + Sync> = ::std::marker::PhantomData;
|
||||||
EVENT_ID_INIT.call_once(|| {
|
EVENT_ID_INIT.call_once(|| {
|
||||||
EVENT_ID.store($crate::get_event_id::<$t>(), std::sync::atomic::Ordering::Relaxed);
|
EVENT_ID.store($crate::get_event_id::<$t>(), std::sync::atomic::Ordering::Relaxed);
|
||||||
});
|
});
|
||||||
|
@ -47,9 +47,8 @@ macro_rules! register_hook {
|
||||||
/// Usage: `post_event!(bus, type, event)`
|
/// Usage: `post_event!(bus, type, event)`
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! post_event {
|
macro_rules! post_event {
|
||||||
// TODO allow multiple $t:ty at once and re-#[doc(hidden)] the low-level get_post_targets and
|
// prefer (bus, ty, ty, ty, ..., evt) but rust is just bad at parsing
|
||||||
// get_event_id
|
($b:expr, $e:expr, $($t:ty),+) => {
|
||||||
($b:expr, $t:ty, $e:expr) => {
|
|
||||||
{
|
{
|
||||||
// hygiene
|
// hygiene
|
||||||
let bus: &$crate::EventBus = $b;
|
let bus: &$crate::EventBus = $b;
|
||||||
|
@ -64,26 +63,38 @@ macro_rules! post_event {
|
||||||
});
|
});
|
||||||
let cancellable = CANCELLABLE.load(std::sync::atomic::Ordering::Relaxed);
|
let cancellable = CANCELLABLE.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut event_handlers = ::std::iter::empty::<(i32, Box<dyn Fn(&mut _)>)>();
|
||||||
|
$(
|
||||||
|
let handlers;
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut event_handlers = {
|
||||||
// event type setup
|
// event type setup
|
||||||
static EVENT_ID: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::ATOMIC_USIZE_INIT;
|
static EVENT_ID: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::ATOMIC_USIZE_INIT;
|
||||||
static EVENT_ID_INIT: ::std::sync::Once = ::std::sync::ONCE_INIT;
|
static EVENT_ID_INIT: ::std::sync::Once = ::std::sync::ONCE_INIT;
|
||||||
// no generics allowed.
|
// no generics allowed.
|
||||||
static DUMMY: ::std::marker::PhantomData<dyn Fn(&mut $t) + Send + Sync> = ::std::marker::PhantomData;
|
static _DUMMY: ::std::marker::PhantomData<dyn Fn(&mut $t) + Send + Sync> = ::std::marker::PhantomData;
|
||||||
EVENT_ID_INIT.call_once(|| {
|
EVENT_ID_INIT.call_once(|| {
|
||||||
EVENT_ID.store($crate::get_event_id::<$t>(), std::sync::atomic::Ordering::Relaxed);
|
EVENT_ID.store($crate::get_event_id::<$t>(), std::sync::atomic::Ordering::Relaxed);
|
||||||
});
|
});
|
||||||
let id = EVENT_ID.load(std::sync::atomic::Ordering::Relaxed);
|
let id = EVENT_ID.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
|
|
||||||
// handler retrieval and invokation
|
// handler retrieval and invokation
|
||||||
let event: &mut $t = event;
|
{
|
||||||
let handlers = $crate::get_post_targets::<$t>(bus, event, id);
|
let subevent: &mut $t = event;
|
||||||
for (_pri, fun) in handlers.iter() {
|
handlers = $crate::get_post_targets::<$t>(bus, subevent, id);
|
||||||
|
}
|
||||||
|
$crate::type_hint_trick(event, event_handlers, handlers.iter().cloned(), |f| move |evt| f(evt))
|
||||||
|
};
|
||||||
|
)+
|
||||||
|
|
||||||
|
for (_pri, fun) in event_handlers {
|
||||||
fun(event);
|
fun(event);
|
||||||
if cancellable && <$t as $crate::Event>::cancelled(event) {
|
if cancellable && <_ as $crate::Event>::cancelled(event) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cancellable && <$t as $crate::Event>::cancelled(event)
|
cancellable && <_ as $crate::Event>::cancelled(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,9 +135,25 @@ pub struct EventBus {
|
||||||
dropper: Mutex<LinkedList<Box<dyn Fn(usize) + Send + Sync>>>,
|
dropper: Mutex<LinkedList<Box<dyn Fn(usize) + Send + Sync>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn type_hint_trick<T: Event + Sized, A, B, C: Event + ?Sized, E>(_event: &mut T, event_handlers: A, handlers: B, convert: fn(::std::sync::Arc<dyn Fn(&mut C) + Send + Sync>) -> E) -> impl ::std::iter::Iterator<Item=(i32, Box<dyn for<'a> Fn(&'a mut T) + 'static>)> where
|
||||||
|
A: ::std::iter::Iterator<Item=(i32, Box<dyn Fn(&mut T)>)>,
|
||||||
|
B: ::std::iter::Iterator<Item=(i32, ::std::sync::Arc<dyn Fn(&mut C) + Send + Sync>)>,
|
||||||
|
E: Fn(&mut T) + 'static,
|
||||||
|
{
|
||||||
|
itertools::merge_join_by(event_handlers, handlers, |l, r| l.0.cmp(&r.0))
|
||||||
|
.map(move |eob: itertools::EitherOrBoth<(i32, Box<dyn Fn(&mut T)>), (i32, ::std::sync::Arc<dyn Fn(&mut C) + Send + Sync>)>| ({
|
||||||
|
eob.as_ref().left().map(|v| v.0).or_else(|| eob.as_ref().right().map(|v| v.0)).unwrap_or(0)
|
||||||
|
}, Box::new(move |evt: &mut _| {
|
||||||
|
eob.as_ref().left().map(|x| (x.1)(evt));
|
||||||
|
eob.as_ref().right().map(|x| convert(x.1.clone())(evt));
|
||||||
|
}) as Box<_>))
|
||||||
|
}
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
extern crate anymap;
|
extern crate anymap;
|
||||||
|
extern crate itertools;
|
||||||
|
|
||||||
//use std::marker::PhantomData;
|
//use std::marker::PhantomData;
|
||||||
use std::sync::atomic::AtomicUsize;
|
use std::sync::atomic::AtomicUsize;
|
||||||
|
@ -159,10 +186,11 @@ mod id_map {
|
||||||
|
|
||||||
pub fn get_event_id<T: Event + ?Sized>() -> usize {
|
pub fn get_event_id<T: Event + ?Sized>() -> usize {
|
||||||
static EVENT_ID_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;
|
static EVENT_ID_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||||
let id = EVENT_ID_MAP.lock().expect("failed to allocate event id").entry::<EventId<T>>().or_insert_with(|| EventId { id: EVENT_ID_COUNT.fetch_add(1, AtomicOrdering::SeqCst), _t: PhantomData }).id;
|
EVENT_ID_MAP.lock().expect("failed to allocate event id").entry::<EventId<T>>().or_insert_with(|| {
|
||||||
let handlers: Arc<Handlers<T>> = Default::default();
|
let handlers: Arc<Handlers<T>> = Default::default();
|
||||||
Arc::make_mut(&mut super::HANDLERS.write().expect("???")).push(handlers);
|
Arc::make_mut(&mut super::HANDLERS.write().expect("???")).push(handlers);
|
||||||
id
|
EventId { id: EVENT_ID_COUNT.fetch_add(1, AtomicOrdering::SeqCst), _t: PhantomData }
|
||||||
|
}).id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,3 +294,50 @@ impl Drop for EventBus {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test with good error messages
|
||||||
|
#[cfg(test)]
|
||||||
|
mod useful_test {
|
||||||
|
use super::{Event, EventBus};
|
||||||
|
|
||||||
|
struct MyEvent {
|
||||||
|
i: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
trait DynEvent : Event {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynEvent for MyEvent {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Event for MyEvent {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_handler(e: &mut MyEvent) {
|
||||||
|
/* adds 1 */
|
||||||
|
e.i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn no_handler(e: &mut MyEvent) {
|
||||||
|
/* does nothing */
|
||||||
|
e.i += 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_usage_internal() {
|
||||||
|
let event_bus = EventBus::new();
|
||||||
|
//let handler_id = event_bus.register(add_handler, 0);
|
||||||
|
register_hook!(&event_bus, 0, MyEvent, add_handler);
|
||||||
|
let mut event = MyEvent { i: 3 };
|
||||||
|
assert_eq!(event.i, 3);
|
||||||
|
post_event!(&event_bus, &mut event, MyEvent);
|
||||||
|
post_event!(&event_bus, &mut event, dyn DynEvent);
|
||||||
|
assert_eq!(event.i, 4);
|
||||||
|
register_hook!(&event_bus, 1, MyEvent, no_handler);
|
||||||
|
post_event!(&event_bus, &mut event, MyEvent);
|
||||||
|
assert_eq!(event.i, 5);
|
||||||
|
//event_bus.unregister(handler_id);
|
||||||
|
post_event!(&event_bus, &mut event, MyEvent);
|
||||||
|
//assert_eq!(event.i, 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -27,12 +27,12 @@ fn test_basic_usage() {
|
||||||
register_hook!(&event_bus, 0, MyEvent, add_handler);
|
register_hook!(&event_bus, 0, MyEvent, add_handler);
|
||||||
let mut event = MyEvent { i: 3 };
|
let mut event = MyEvent { i: 3 };
|
||||||
assert_eq!(event.i, 3);
|
assert_eq!(event.i, 3);
|
||||||
post_event!(&event_bus, MyEvent, &mut event);
|
post_event!(&event_bus, &mut event, MyEvent);
|
||||||
assert_eq!(event.i, 4);
|
assert_eq!(event.i, 4);
|
||||||
register_hook!(&event_bus, 1, MyEvent, no_handler);
|
register_hook!(&event_bus, 1, MyEvent, no_handler);
|
||||||
post_event!(&event_bus, MyEvent, &mut event);
|
post_event!(&event_bus, &mut event, MyEvent);
|
||||||
assert_eq!(event.i, 5);
|
assert_eq!(event.i, 5);
|
||||||
//event_bus.unregister(handler_id);
|
//event_bus.unregister(handler_id);
|
||||||
post_event!(&event_bus, MyEvent, &mut event);
|
post_event!(&event_bus, &mut event, MyEvent);
|
||||||
//assert_eq!(event.i, 5);
|
//assert_eq!(event.i, 5);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
These are currently not being automatically tested because compiletest_rs doesn't work on stable.
|
|
|
@ -42,12 +42,12 @@ fn test_dyn_usage() {
|
||||||
register_hook!(&event_bus, 0, dyn MyEvent, add_handler);
|
register_hook!(&event_bus, 0, dyn MyEvent, add_handler);
|
||||||
let mut event = MyEventImpl { i: 3 };
|
let mut event = MyEventImpl { i: 3 };
|
||||||
assert_eq!(event.i, 3);
|
assert_eq!(event.i, 3);
|
||||||
post_event!(&event_bus, dyn MyEvent, &mut event);
|
post_event!(&event_bus, &mut event, dyn MyEvent);
|
||||||
assert_eq!(event.i, 4);
|
assert_eq!(event.i, 4);
|
||||||
register_hook!(&event_bus, 1, dyn MyEvent, no_handler);
|
register_hook!(&event_bus, 1, dyn MyEvent, no_handler);
|
||||||
post_event!(&event_bus, dyn MyEvent, &mut event);
|
post_event!(&event_bus, &mut event, dyn MyEvent);
|
||||||
assert_eq!(event.i, 5);
|
assert_eq!(event.i, 5);
|
||||||
//event_bus.unregister(handler_id);
|
//event_bus.unregister(handler_id);
|
||||||
post_event!(&event_bus, dyn MyEvent, &mut event);
|
post_event!(&event_bus, &mut event, dyn MyEvent);
|
||||||
//assert_eq!(event.i, 5);
|
//assert_eq!(event.i, 5);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
#[macro_use]
|
||||||
|
extern crate eventbus;
|
||||||
|
|
||||||
|
use eventbus::{Event, EventBus};
|
||||||
|
|
||||||
|
struct MyEvent {
|
||||||
|
i: i32
|
||||||
|
}
|
||||||
|
|
||||||
|
trait DynEvent : Event {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynEvent for MyEvent {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Event for MyEvent {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_handler(e: &mut MyEvent) {
|
||||||
|
/* adds 1 */
|
||||||
|
e.i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn no_handler(e: &mut MyEvent) {
|
||||||
|
/* does nothing */
|
||||||
|
e.i += 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_usage_external() {
|
||||||
|
let event_bus = EventBus::new();
|
||||||
|
//let handler_id = event_bus.register(add_handler, 0);
|
||||||
|
register_hook!(&event_bus, 0, MyEvent, add_handler);
|
||||||
|
let mut event = MyEvent { i: 3 };
|
||||||
|
assert_eq!(event.i, 3);
|
||||||
|
post_event!(&event_bus, &mut event, MyEvent);
|
||||||
|
post_event!(&event_bus, &mut event, dyn DynEvent);
|
||||||
|
assert_eq!(event.i, 4);
|
||||||
|
register_hook!(&event_bus, 1, MyEvent, no_handler);
|
||||||
|
post_event!(&event_bus, &mut event, MyEvent);
|
||||||
|
assert_eq!(event.i, 5);
|
||||||
|
//event_bus.unregister(handler_id);
|
||||||
|
post_event!(&event_bus, &mut event, MyEvent);
|
||||||
|
//assert_eq!(event.i, 5);
|
||||||
|
}
|
Loading…
Reference in New Issue