Weakly typed architecture question


#1

Hi!

I have a bit of an ambitious project. It’s a components system combined with events, the goal is to have it used in games. It works a little like this:

The user defines a set of components (using a macro). The components consist of a struct that holds some data, and a global static list of all instances of the component, protected by a mutex.

Then the user defines a set of events, and a set of event handlers. The event handlers are a bit special. Their inputs are not just an event data struct, but also any components the event handler might want to work with. So an event handler definition might look like this:

        event!{ mouse_click , x: i64, y: i64 }

	on!( mouse_click, { positions: Position }), data => {
		positions[0].1.x = data.x ;
	}

So what’s happening is we’re subscribing to the mouse_click event, the events data is put in to the data variable and we’re expecting the positions variable to hold the list of Position components. So now we can update our first position components x property to be equal to the x property of the event data.

So far so good, I’ve implemented these macros and tested them. But the next step is implementing the actual dispatching of the events. First I’m trying to write a simple scheduler, basically collect the events that have been triggered, get their handlers, acquire the mutexes for the components they need, and then call the handler function with those components passed in.

The problem is the ‘acquire the mutexes’ part. Every component has its own little namespace and struct associated with it. A register function makes sure I can get some information about their existence into the scheduler. The handlers are similarly registered. The tricky thing about this is that the scheduler doesn’t know about the types it is acquiring the mutexes for. A handler looks like this:

pub fn $event_name(data: &Vec<$event_name::Data>, components: Vec<&Any>, mut mut_components: Vec<&mut Any>) {
	let mut components_iter = components.iter();
	let mut mut_components_iter = mut_components.iter_mut();

	$(
		let $name : &SharedMutexReadGuard<ComponentList<$typ>> = components_iter
			.next().expect("Event components list too short.")
			.downcast_ref().expect("Event component not of expected type.");
	)*

	$(
		let $mut_name: &mut SharedMutexWriteGuard<ComponentList<$mut_typ>> = mut_components_iter
			.next().expect("Event mut_components list too short.")
			.downcast_mut().expect("Event component not of expected type.");
	)*

	state.write().expect("Event state corrupted").$event_name(
		data,
		$($name),*
		$($mut_name),*
	);
}

So after this long explanation this is basically the part that is missing:

I want my scheduler function to take a component name, get a reference to some object that wraps access to the corresponding component list. Call a function that uses the object to get a read/write lock on the component list mutex and return the guard, so it can be passed to the handler function as an any, and downcasted back to the specific type the handler desires.

What would the object look like? How do I get a mutex write guard around something I don’t know the type of? I’ve had a couple of goes at this and I keep running into trouble with things like wanting to return a Trait, but a trait is not sized. I’m going to try another time right after posting this question, and when I get stuck again I’ll post what I tried as a comment here.