A bit of context: I've had a fair share of programming in Java but want to refine my C++ skills with a college task. I've wanted to focus on C++11 and C++14, as they are recent but not latest, and C++14 is the default revision of my compiler.

I want to share a header (hpp) file and a test implementation one (cpp), and would like a review of naming, aliasing (with using), use of ordering functions (operator<), encapsulation, defensive programming (invariants), proper use of data clases and algorithms and whatever you may need to point, like template use or abuse.

Here's the code, disim.hpp:

#ifndef __DISIM_HPP
#define __DISIM_HPP

// std::ostream
#include <iostream>

// std::map
#include <map>

// std::priority_queue
#include <queue>

// std::vector
#include <vector>

// std::function
#include <functional>

namespace disim {

/// Type of the Event name (const char*, std::string_view, std::string)
using EventName = const char*;

/// Collection of Event names (std::vector)
using EventNameCollection = std::vector<EventName>;

/// Type of the Event timestamp (unsigned, long)
using EventTime = unsigned;

// TODO: Proper use of a struct? Rather use a std::tuple?
struct Event {
    EventName name;
    EventTime time;

    friend auto operator<(Event const& l, Event const& r) 
        return l.time < r.time; 

    friend auto operator>(Event const& l, Event const& r) { return !(l < r) && l.time != r.time; }

// TODO: Convenience method: Useless? -> Directly disim::Event = {...}. Encapsulation?
Event make_event(EventName const& name, EventTime const& timestamp) 
    return {name, timestamp};

/// Collection of Events (std::vector)
using EventCollection = std::vector<Event>;

/// Function that generates a new Event for a given Timestamp
using EventGenerator = std::function<Event(EventName, EventTime)>;

 * TODO: How to properly document this function?
 * Function that handles an event occurrence.
 * @param 0 Current System state
 * @param 1 Current (on trigger) time
 * @param 2 Difference between Event triggers
template<class System>
using EventTrigger = std::function<EventNameCollection(System&, EventTime const&, EventTime const&)>;

// TODO: Is Name Redundant? Or as a whole? Only relevant as a Big Holder of the Three
template<class System>
struct EventHandler
    EventName name; 
    EventGenerator generator;
    EventTrigger<System> trigger;

// TODO: Convenience method: Useless? -> Directly disim::EventHandler = {...}. Encapsulation?
template<class System>
EventHandler<System> make_handler(EventName const& name, EventGenerator generator, EventTrigger<System> trigger)
    return {name, generator, trigger};

template<class System>
class DiscreteSimulation 
    using ExitFunction = std::function<bool(System const&)>;
    using EventQueue = std::priority_queue<Event, EventCollection, std::greater<Event>>;

    std::map<EventName, EventTrigger<System>> triggers;
    std::map<EventName, EventGenerator> generators;

    DiscreteSimulation(std::initializer_list<EventHandler<System>> handlers) 
        for(auto& h : handlers) {
            triggers.insert( {h.name, h.trigger} );
            generators.insert( {h.name, h.generator} );

    System execute(System const& initial_state, 
                   EventCollection const& initial_events, 
                   ExitFunction exit_trigger) const 
        if(initial_events.empty()) {
            return initial_state;

        auto state = initial_state;
        auto queue = EventQueue(std::begin(initial_events), std::end(initial_events));

        EventTime clock = queue.top().time;
        EventTime time_diff = clock;
        while(!queue.empty() && !exit_trigger(state)) 
            auto event = queue.top();

            auto trigger_it = triggers.find(event.name);
            if(trigger_it != triggers.end()) {
                EventTrigger<System> trigger = (*trigger_it).second;

                // Process event; get procced events
                auto generated_names = trigger(state, clock, time_diff);

                // Push every new event into the Queue
                for(auto const& name : generated_names) {
                    auto generator_it = generators.find(name);
                    if(generator_it != generators.end()) {
                        EventGenerator generator = (*generator_it).second;
                        Event generated_event = generator(name, clock);

                    } else {
                        // Event not generated
            } else {
                // Unhandled event

            time_diff = queue.top().time - clock;
            clock = queue.top().time;

        return state;
} // end-namespace


If you need some context on what a Discrete Event simulation is, it's basically an Agenda of events that are ordered based on "sooner to occur" which act on a system (the domain-specific aggregate of data) State. Events may trigger the scheduling of other, different Events.

The test_sim.cpp file can help showing a simple test case: A Test Event increments the iterations of a given System, and generates two new Test Events, showing the correct ordering of the events.

#include <iostream>
#include <random>
#include "disim.hpp"

struct TestSystem {
    unsigned iterations = 0;

disim::EventGenerator test_gen = [](auto name, auto time) -> auto
    static std::random_device rd;
    static std::mt19937 gen(rd());
    static std::uniform_int_distribution<unsigned> dis(1, 100);

    auto new_time = time + dis(gen);

    std::printf("Test generated for time=%d\n", new_time);
    return disim::make_event(name, new_time);

disim::EventTrigger<TestSystem> test_trigger = [](auto& sys, auto time, auto diff) -> auto
    sys.iterations += 1;

    std::printf("Evaluating Test at time=%d\n", time);

    return std::vector<const char*> {"Test", "Test"};

std::function<bool(TestSystem const&)> exit_function = [](auto& sys)
    return !(sys.iterations < 10);

int main() {
    auto simulation = disim::DiscreteSimulation<TestSystem> {
        disim::make_handler("Test", test_gen, test_trigger)

    auto initial_events = std::vector<disim::Event> {
        disim::make_event("Test", 0)

    auto state_before = TestSystem {};
    auto state_after  = simulation.execute(state_before, initial_events, exit_function);

    std::printf("State before -> iterations=%d\n", state_before.iterations);
    std::printf("State after  -> iterations=%d\n", state_after.iterations);


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.