Runtime

The Coco-generated C++ code depends on a small Cocotec-provided runtime that provides some common utilities. The C++ runtime is a header-only library that consists of two files:

  • coco/runtime.h: the main runtime file which must be available at compile-time;
  • coco/stream_logger.h: an optional file, which includes an example implementation of a coco::StateMachineLogger that is useful for debugging purposes.

The runtime is made available under the MIT license and can be downloaded from here.

Components

class coco::BaseComponent

The base class of all Coco components.

Subclassed by coco::BaseMultiThreadedEncapsulatingComponent, coco::BaseSingleThreadedComponent, coco::BaseSingleThreadedEncapsulatingComponent

Public Functions

BaseComponent()
~BaseComponent() = default
BaseComponent(const BaseComponent&) = delete
BaseComponent &operator=(const BaseComponent&) = delete
const std::string &name() const

A name for this component.

By default this will be set to the name of the type from the Coco file or, if this component is owned by a Coco encapsulating component, to the name of the owner component.

void set_name(std::string name)
void set_name(const std::string &base_name, const std::string &field_name)
StateMachineLogger *logger() const

Returns the current logger that is used by the machine.

void set_logger(StateMachineLogger *logger)

Sets the logger that should be used by the machine.

This should not be used once start() has been called.

void set_logger_recursively(StateMachineLogger *logger)

Sets the logger that should be used by this state machine, and all state machines owned by this component.

In order to enable the logging, generator.cpp.alwaysEnableLogging must be true (by default this setting is true), or the generated code must be compiled with COCO_LOGGING_ENABLE defined..

This should not be used once start() has been called. The component will hold a reference to the passed logger; the caller is responsible for ensuring that the logger is kept alive until the state machine has terminated.

void coco_start() = 0

Starts the component’s state machine.

After this call has returned, it is safe to call all of the methods on this component’s provided port. This may be called more than once in which case subsequent calls will be ignored.

void add_observer(ComponentObserver &observer)

Adds an observer who will be notified about key component lifecycle events.

Protected Functions

void send_machine_starting()

Informs all registered ComponentObserver that this machine is starting.

Do not call this directly.

void send_machine_terminated()

Informs all registered ComponentObserver that this machine has terminated.

Do not call this directly.

void set_names_recursively()

Protected Attributes

std::string name_
StateMachineLogger *coco_logger_
bool started_
class coco::BaseMultiThreadedEncapsulatingComponent : public coco::BaseComponent, protected coco::ComponentObserver

An encapsulating component that contains multi-threaded components.

Public Functions

BaseMultiThreadedEncapsulatingComponent()
~BaseMultiThreadedEncapsulatingComponent()

Protected Functions

bool have_all_machines_terminated() const

Have all machines already stopped?

Requires: mutex_ locked

void wait_for_component_termination()
void set_names_recursively() override = 0

Protected Attributes

std::mutex mutex_
class coco::ComponentObserver

Subclassed by coco::BaseMultiThreadedEncapsulatingComponent, coco::BaseSingleThreadedEncapsulatingComponent

Public Functions

~ComponentObserver() = default
void machine_starting(const BaseComponent &component)

Called when a state machine is about to be started.

void machine_terminated(const BaseComponent &component)

Called when a state machine has succesfully shutdown.

In all runtimes, this is called after StateMachineLogger::state_exit_finished() has been called for the Terminated state. Otherwise:

  • In the single-threaded runtime, this is called just before the event that caused the transition to Terminated is executed.
  • In the multi-threaded runtime, this is called as the final action of the background thread if there is one. If there is no background thread (because the component does not handle signals, or have any execution states), then this will be called from the handler of the function call that caused the transition to Terminated.

Single Threaded Runtime

class coco::BaseSingleThreadedComponent : public coco::BaseComponent, public coco::SingleThreadedSubscriber

The base class of all components that use the single-threaded runtime.

Public Functions

BaseSingleThreadedComponent()
void set_timer_executor(SingleThreadedTimerExecutor &executor)

Sets the timer executor to be used to execute any timers that this single-threaded component has.

This does not normally need to be called: it is automatically called by the runtime when required.

For components that use timers this must be called prior to BaseComponent::coco_start().

void execute_timer(int timer_id)

Executes the given timer.

This does not normally need to be called: it is automatically called by the runtime when reuqired.

This method should only be called by this component’s SingleThreadedTimerExecutor.

void coco_drain_queue() override

For components using the single-threaded runtime, this will cause them to process all items in their queues.

The component will process its queue through to completion. The component will then recursively call coco_drain_queue() on its provided port. This means that the queues will be recursively drained upwards through a single-threaded component stack.

Protected Functions

void cancel_timer(int timer_id)
void enable_timer(int timer_id, CocoClock::duration duration)
void send_drain_queue()

Drains the queue of all subscribers.

void add_single_threaded_subscriber(SingleThreadedSubscriber &subscriber)

Subscribe to drain queue calls from this component.

void remove_single_threaded_subscriber(SingleThreadedSubscriber &subscriber)

Unsubscribe drain queue calls from this component.

class coco::BaseSingleThreadedEncapsulatingComponent : public coco::BaseComponent, protected coco::ComponentObserver

An encapsulating component that contains single-threaded components.

Subclassed by coco::BaseWrappedSingleThreadedEncapsulatingComponent

Public Functions

BaseSingleThreadedEncapsulatingComponent()
~BaseSingleThreadedEncapsulatingComponent()

Protected Functions

bool have_all_machines_terminated() const
void all_machines_terminated()
void set_names_recursively() override = 0
class coco::SingleThreadedSubscriber

Subclassed by coco::BaseSingleThreadedComponent, coco::SingleThreadedGMockHelper

Public Functions

~SingleThreadedSubscriber() = default
void coco_drain_queue() = 0

For components using the single-threaded runtime, this will cause them to process all items in their queues.

The component will process its queue through to completion. The component will then recursively call coco_drain_queue() on its provided port. This means that the queues will be recursively drained upwards through a single-threaded component stack.

class coco::SingleThreadedTimerExecutor

An abstract class that can be implemented to provide an implementation of timers in the single-threaded runtime.

Subclassed by coco::SingleThreadedGMockHelper

Public Functions

SingleThreadedTimerExecutor() = default
~SingleThreadedTimerExecutor() = default
SingleThreadedTimerExecutor(const SingleThreadedTimerExecutor&) = delete
SingleThreadedTimerExecutor &operator=(const SingleThreadedTimerExecutor&) = delete
void enable_timer(BaseSingleThreadedComponent &component, int timer_id, CocoClock::duration duration) = 0

Enables a timer.

When the timer expires, the timer implementation should call execute_timer on the component with the given timer_id. Note that there is no locking provided by the single-threaded implementation: it is the responsibility of the implementor of this class to ensure that it prevents any function calls or signals from being executed whilst the timer itself is executing.

void cancel_timer(BaseSingleThreadedComponent &component, int timer) = 0

Cancels a timer.

After this call, the timer implementation must not call execute_timer on the given timer until it has been re-enabled. Note that this includes cancelling any pending execute_timer calls that have been scheduled but have not yet been dispatched.

Ports

class coco::BasePort

The base class of all Coco ports.

Subclassed by coco::BaseProvidedPort

Public Functions

BasePort()
~BasePort() = default
BasePort(const BasePort&) = delete
BasePort &operator=(const BasePort&) = delete
void coco_start()

Starts the component’s state machine.

See
BaseComponent::coco_start

class coco::BaseProvidedPort : public coco::BasePort

Subclassed by coco::BaseProvidedInterfacePort

Public Functions

void single_threaded_subscribe(SingleThreadedSubscriber &port)
void single_threaded_unsubscribe(SingleThreadedSubscriber &port)
template<class ProvidedPort>
class coco::ConnectionPoint

Represents something that requires connecting to a provided port.

The most common use case of a ConnectionPoint is a required port on an implementation component which requires connecting before the system is started. ConnectionPoints are also used on encapsulating components, in which case calling connect will connect all internal users.

Public Functions

~ConnectionPoint() = default
void connect(ProvidedPort &port, bool should_subscribe = true) = 0

Connects all users of this connection point to the specified port.

Parameters
  • port: The port to connect to.
  • should_subscribe: Indicates if all internal users should be subscribed to signals from port.

Logging

class coco::StateMachineLogger

An abstract interface to log events from a Coco state machine.

Subclassed by coco::FormatterMachineLogger

Public Functions

~StateMachineLogger() = default
void machine_starting(const BaseComponent &component)
void machine_started(const BaseComponent &component, const char *current_state)

Called after a state machine has been started and its entry action fully executed.

void machine_terminated(const BaseComponent &component)
void state_entry_started(const BaseComponent &component, const char *state)

Call whenever a component’s state machine enters a state.

This called just before the body of the entry action is executed.

void state_exit_finished(const BaseComponent &component, const char *state)

Call whenever a component’s state machine exits a state.

This called just after the body of the exit action is executed.

void event_started(const BaseComponent &component, const char *current_state, const char *port, const char *event)

Called when an event is about to be processed by the state machine, but before a handler has been chosen.

Parameters
  • component: The component whose state machine is processing the event.
  • current_state: The string name of the current state of the state machine.
  • port: The string name corresponding to the port from which the event was received.
  • event: The string name of the event being received.

void event_finished(const BaseComponent &component, const char *current_state, const char *port, const char *event)

Called after the event has been processed to completion.

This includes after all relevant entry/exit actions have been fired. Further, in the single-threaded model it will only be fired after the queue has finished being drained.

void message(const BaseComponent &component, const char *current_state, const char *message)

Called whenever a call to Coco’s log() function is executed.

Parameters
  • component: The component whose state machine is processing the event.
  • current_state: The string name of the current state of the state machine.
  • message: The formatted message.

void message(const BaseComponent &component, const char *current_state, const std::string &message)
void abort(const BaseComponent &component, const char *current_state)

Called whenever a call to Coco’s abort() function is executed in a state machine.

Subclasses do not need to call abort: the calling component will immediately call abort afterwards instead.

Parameters
  • component: The component whose state machine is aborting.
  • current_state: The string name of the current state of the state machine.

EventLogger &event_started(const BaseComponent &component, const char *port, const char *event)

As per event_started, but this overload allows parameters and return values to be logged.

EventLogger &event_finished(const BaseComponent &component, const char *port, const char *event)

As per event_finished, but this overload allows parameters and return values to be logged.

EventLogger &message(const BaseComponent &component)

As per event_finished, but this overload allows parameters and return values to be logged.

class EventLogger

Subclassed by coco::FormatterEventLogger

Public Functions

EventLogger() = default
~EventLogger() = default
EventLogger(const EventLogger&) = delete
EventLogger &operator=(const EventLogger&) = delete
template<typename T>
EventLogger &current_state(const T &value)
EventLogger &message(const char *str)
EventLogger &message(const std::string &str)
template<typename T>
EventLogger &parameter(const char *parameter_name, const T &value)
template<typename T>
EventLogger &return_value(const T &value)
void done() = 0

Protected Functions

Formatter &formatter() = 0
void begin_parameter(const char *parameter_name) = 0
void end_parameter(const char *parameter_name) = 0
void begin_return_value() = 0
void end_return_value() = 0
void begin_current_state() = 0
void end_current_state() = 0
void message(const char *message, size_t message_size) = 0
class coco::StreamMachineLogger : public coco::FormatterMachineLogger, private coco::Formatter

A simple logger that outputs to a std::ostream.

This is not intended for production usage, but as an example of how a logger could be implemented.

Public Static Functions

StreamMachineLogger *cout()

Returns a pointer to a shared logger that logs to stdout.

This can be directly passed to BaseComponent::set_logger_recursively, for example:

component.set_logger_recursively(StreamMachineLogger::cout);

StreamMachineLogger *cerr()

Returns a pointer to a shared logger that logs to stderr.

This can be directly passed to BaseComponent::set_logger_recursively, for example:

component.set_logger_recursively(StreamMachineLogger::cerr);

Mocks

class coco::MultiThreadedGMockHelper

A simple helper class to assist with creating multi-threaded gmocks.

This uses a background thread to execute actions asynchronously. The intended use is that you create a single instance of MultiThreadedGMockHelper inside your test case, and use InvokeAsync to invoke actions asynchronously. These will then be executed by a background thread. For example:

ClientMock client_mock;
coco::MultiThreadedGMockHelper helper;
...
EXPECT_CALL(client_mock, pinger_pong(value))
  .WillOnce(helper.InvokeAsync(Invoke(&client_mock.pinger().connected(), &PPingerProvided::terminate)))

Public Functions

MultiThreadedGMockHelper()
~MultiThreadedGMockHelper()
testing::PolymorphicAction<ExecuteAsyncAction> InvokeAsync(const testing::Action<void()> &action)

Executes an arbitrary gMock action asynchronously.

Note that this will be immediately added to a queue which will execute concurrently with this, meaning that the execution of the action could happen before this function even returns.

testing::PolymorphicAction<ExecuteAsyncAction> InvokeAsync(const testing::Action<void()> &actionCocoClock::duration delay, )

Executes an arbitrary gMock action asynchronously after the specified delay has elapsed.

void execute_async(std::function<void()> &&function)
void execute_async(std::function<void()> &&functionCocoClock::duration delay, )
class coco::SingleThreadedGMockHelper : public coco::SingleThreadedSubscriber, public coco::SingleThreadedTimerExecutor

A simple helper class to assist with creating multi-threaded gmocks.

This accumulates background actions in a queue and provides an easy way to execute them. The intended use is that you create a single instance of SingleThreadedGMockHelper inside your test case, and use InvokeAsync to invoke actions asynchronously. You should then periodically call coco_drain_queue to execute the accumulated actions. For example:

ClientMock client_mock;
coco::SingleThreadedGMockHelper helper;
...
EXPECT_CALL(client_mock, pinger_pong(value))
  .WillOnce(helper.InvokeAsync(Invoke(&client_mock.pinger().connected(), &PPingerProvided::terminate)))

while (...) { helper.coco_drain_queue(); }

Public Functions

SingleThreadedGMockHelper()
~SingleThreadedGMockHelper()
testing::PolymorphicAction<ExecuteAsyncAction> InvokeAsync(const testing::Action<void()> &action)

Executes an arbitrary gMock action asynchronously.

Note that this will be immediately added to a queue which will execute concurrently with this, meaning that the execution of the action could happen before this function even returns.

testing::PolymorphicAction<ExecuteAsyncAction> InvokeAsync(const testing::Action<void()> &actionCocoClock::duration delay, )

Executes an arbitrary gMock action asynchronously after the specified delay has elapsed.

void execute_async(std::function<void()> &&function)
void execute_async(std::function<void()> &&functionCocoClock::duration delay, )
void coco_drain_queue() override

For components using the single-threaded runtime, this will cause them to process all items in their queues.

The component will process its queue through to completion. The component will then recursively call coco_drain_queue() on its provided port. This means that the queues will be recursively drained upwards through a single-threaded component stack.

void enable_timer(BaseSingleThreadedComponent &component, int timer_id, CocoClock::duration duration) override

Enables a timer.

When the timer expires, the timer implementation should call execute_timer on the component with the given timer_id. Note that there is no locking provided by the single-threaded implementation: it is the responsibility of the implementor of this class to ensure that it prevents any function calls or signals from being executed whilst the timer itself is executing.

void cancel_timer(BaseSingleThreadedComponent &component, int timer_id) override

Cancels a timer.

After this call, the timer implementation must not call execute_timer on the given timer until it has been re-enabled. Note that this includes cancelling any pending execute_timer calls that have been scheduled but have not yet been dispatched.

Utility

template<typename ...Ts>
class coco::variant

Provides support for Coco’s tagged enums. Equivalent to C++17’s std::variant type.

Public Functions

variant()
variant(const variant &other)
variant(variant &&other) noexcept
template<class T, typename Enable = typename std::enable_if<!std::is_same<variant<Ts...>, typename std::decay<T>::type>::value>::type>
variant(const T &other)
template<class T, typename Enable = typename std::enable_if<!std::is_same<variant<Ts...>, typename std::decay<T>::type>::value>::type>
variant(T &&other)
template<size_t ix, class T>
variant(in_place_index_t<ix>, const T &other)
template<size_t ix, class T>
variant(in_place_index_t<ix>, T &&other)
~variant()
variant &operator=(const variant &other)
variant &operator=(variant &&other) noexcept
std::size_t index() const
template<class T>
T &get()
template<class T>
const T &get() const
template<size_t ix>
detail::type_of<ix, Ts...>::type &get()
template<size_t ix>
const detail::type_of<ix, Ts...>::type &get() const
bool operator==(const variant &right) const
bool operator!=(const variant &right) const
template<typename T>
class coco::optional

Provides support for Coco’s optional type. Equivalent to C++17’s std::optional type.

Public Functions

optional()
optional(const T &value)
optional(const optional &other)
optional(T &&value)
optional(optional &&other) noexcept
~optional()
optional<T> &operator=(const optional &other)
optional<T> &operator=(optional &&other) noexcept
optional<T> &operator=(const T &other)
optional<T> &operator=(T &&other)
bool has_value() const
T &value()
const T &value() const
T value_or(T &&default_value) const
void reset()
template<typename T, typename E>
class coco::expected

A simple P0323-like implementation of std::expected, suitable for coco’s Result type.

Suppose you have a function such as:

coco::expected<std::vector<bool>, int> mayFail();
Within the body of mayFail, you can use:
return coco::make_unexpected(5);
to return a specific error code, or:
return std::vector<bool>()
to return the expected value.

When calling mayFail(), the accessors has_value(), value() and error() can be used to either access the value or the container error, as required.

Template Parameters
  • T: the type of a success case.
  • E: the type of an error.

Public Functions

expected()
expected(const expected&) = default
expected &operator=(const expected&) = default
expected(expected &&other) noexcept
expected &operator=(expected &&other) noexcept
~expected() = default
expected(const T &value)
expected(T &&value)
expected(const unexpected<E> &error)
expected(unexpected<E> &&error)
bool has_error() const

Returns true if this expected contains an error rather than a value.

bool has_value() const

Returns true if this expected contains a value rather than an error.

E &error()

If has_value is false, then returns the error.

const E &error() const
T &value()

If has_value is true, then returns the error.

const T &value() const
T value_or(T &&value) const
bool operator==(const expected<T, E> &right) const
bool operator!=(const expected<T, E> &right) const
template<typename E>
class coco::unexpected

A helper class for coco::expected which can be used to return an error from a function that returns expected<T, E>.

@seealso expected

Public Functions

unexpected() = delete
unexpected(const E &error)
unexpected(E &&error)
unexpected(const unexpected&) = default
unexpected &operator=(const unexpected&) = default
unexpected(unexpected &&other) noexcept
unexpected &operator=(unexpected &&other) noexcept
~unexpected() = default
E &value()
const E &value() const
class coco::IllegalException : public exception

If illegalBehaviour is set to Exception, then an exception that is thrown when Coco encounters an illegal event.

Note that after receiving this exception, you must not call back into the Coco system, since the state of the system cannot be guaranteed.

Public Functions

IllegalException(std::string component_name)
IllegalException(std::string component_name, const char *state_name)
~IllegalException()
IllegalException(const IllegalException&) = default
IllegalException &operator=(const IllegalException&) = default
const char *what() const override
const std::string &component_name() const
const char *state_name() const

Traits

template<class T>
struct Iterable

Used to map Coco’s Iterable trait.

This is for internal Coco usage only: users should not attempt to specialise this, nor use it from their own code.

template<class T>
struct coco::Iterable<Range<T>>

Used to map Coco’s Iterable trait instance for Range.

Public Types

typedef T ElementType
template<class T, size_t size>
struct coco::Iterable<std::array<T, size>>

Used to map Coco’s Iterable trait instance for Arrays.

Public Types

typedef T ElementType