Hello,
I'm developing a simulation program that is separated into a library (with the core functionalities) and a binary (to provide parameters and collect/process data). In the public interface of the library I would like to give access to monitor the very fine details of the current state, and allow to collect 'any' data in a user defined struct. So if later I (or other users) need other results of the simulation, it's enought to modify the binary.
The goal would be to allow the user of the library to define some struct(s) (to collect data and maybe interact with the simulation in a limited way) that has functions which are called at well defined points during the simulation (e.g. after every event or step, and at the end of the simulation). Something like:
//binary
pub struct Collections {
data_field1 : DataType1,
...
}
impl Collections {
pub fn end_of_event_collect(&mut self, event: Event) {...}
}
//libraray
pub struct Event {...}
impl Event {
pub fn get_data1(&self) -> DataType1 {...}
fn new(...) -> Event {...}
fn process(&mut self, ...) {...}
}
struct EventManager {
collections: Collections,
...
}
impl EventManager {
fn run_events (&mut self, number_of_events: u32) {
for _ in 0..number_of_events {
let event = Event::new(...);
event.process(...);
self.collections.end_of_event_collect(event);
}
}
}
So far I could think of 3 solutions:
-
Separate the library into a
core
anduser
module, and put the data collection in theuser
module. But if later the users would like to write some different versions of theuser
module (for different applications), than thecore
should be copied for each of it. (Although currently the functionality of the program is limited, it might be extended later.) -
Propagate every event (and/or step) to the binary (e.g. with messages) so the library don't have to know about the
Collections
. This way the usage of the library would be more complicated, and might cause some serious overhead. -
Declare a
Collections
trait in the library, and use a trait object to collect the data inside the library. This seems to be the best solution from these, but still has some incoviniences. E.g. the user has to deal with the downcasting in the binary. And the (trait) object has to be created in the binary (than propaget through(/register in) the library).
So I'm asking if there might be any better solution(s)?
Solution:
I sticked with the trait objects. (3. option)