Number
#Represents a XOD number. In current implementations typedef’ed to IEEE 754 32-bit floating point data type.
TimeMs
#Represents a big integer to store time values expressed in milliseconds. Typedef’ed to 32-bit unsigned integer.
XString
#Represents XOD string. Typedef’ed to List<char>
, so the same methods and functions may be used to manipulate it.
node {}
#The node
block describes the state and behaviour of a node. Under the hood, it is converted to a regular C++ struct
, and should be treated as such. You can add fields and methods to it.
meta {}
#The meta
block used to group some part of code that will be placed inside a struct Node
before the generated code.
Usually used to define a custom type.
Context
#An opaque type to distinguish a particular instance of node among other nodes of the same type.
Required as an argument for most node functions. Can be thought as an explicit this
in many OOP languages, or like the self
in Python.
State
(deprecated since 0.35) #A user-defined struct to store node’s data for a lifetime of the program. Each node type has its own State
definition.
typeof_$$$
#A type for each of node’s pins. Useful to access the type of a pin if its symbol is hardly accessible in other ways.
$$$
gets replaced by a pin label as is. For example, typeof_IN
, typeof_OUT
. Since pin names are unique, there is no destinction between inputs and outputs.
Automatically generated descriptor for each of node’s pins. Never instantiated. Used as a template argument in functions to access data.
$$$
gets replaced by a pin label as is. For example, input_Smin
, output_FOO
.
input_$$$
#If a node has a single unlabeled input, input_IN
is generated for it.
If a node has multiple unlabeled inputs, they are referred as input_IN1
, input_IN2
,…, input_IN7
.
output_$$$
#XOD runtime supports no more than seven outputs on C++ nodes.
If a node has a single unlabeled output, output_OUT
is generated for it.
If a node has multiple unlabeled outputs, they are referred as output_OUT1
, output_OUT2
,…, output_OUT7
.
Known at a compile time. Might be used in static asserts to ensure correctness.
constant_input_$$$
#A value of a constant input pin.
constant_output_$$$
#The descriptor for constant output of the node.
Should be defined manually as
static constexpr typeof_OUT constant_output_OUT = $$$;
$$$
should be replaced with a valid value for the output.
In case of creating an “unpack” node for the custom type, which is a struct that contains a constant value (such as PORT), you might define it as:
static constexpr typeof_OUT constant_output_OUT = typeof_DEV::port;
nodespace {}
#The nodespace
block is used to place some code outside the struct Node {}
, but inside a node namespace, so this code will be accessible only with that node.
It’s a good place to store some constants in PROGMEM, make an alias for a shared type and so on.
void evaluate(Context ctx)
#Entry-point function for any C++ node. Called by the runtime when a node requires re-evaluation for whatever reason: input data update, schedule timeout, etc. Each C++ node must implement evaluate
.
ValueT getValue<input_$$$|output_$$$>(Context ctx)
#Returns the most recent value on an input or output pin. The result type ValueT
depends on pin data type and could be bool
, Number
, etc.
Note, the pulse type has no values. It’s meaningless to call getValue
on a pulse-type pin. To know if a pulse was fired use isInputDirty
instead.
A node owns its output values, so in simple cases, they could serve both as values for downstream nodes and as node’s state storage. In such cases use getValue<output_$$$>(ctx)
to access the last emitted output value.
void emitValue<output_$$$>(Context ctx, ValueT value)
#Sets new value on an output pin. The argument type ValueT
depends on pin data type and can be bool
, Number
, etc.
A call to emitValue
causes immediate downstream nodes to be re-evaluated within the current transaction even if the output value has not changed. If it’s cheap to check, avoid sending duplicate values.
To emit a pulse, use true
as the value
.
bool isInputDirty<input_$$$>(Context ctx)
#Returns true
if and only if the input pin specified has got a newly emitted value during the current transaction.
State* getState(Context ctx)
(deprecated since 0.35) #Returns a pointer to the persistent state storage for the current node. The State
structure is defined by the node author. Fields could be updated directly, with the pointer returned, no commit is required after a change.
bool isSettingUp()
#Returns true
if the evaluation takes place inside the very first transaction. Use the function to run some initialization code once on boot, or prevent undesirable initial values emission.
TimeMs transactionTime()
#Returns time of the current transaction since the program started, in milliseconds. Stays constant for the whole duration of any particular transaction. Prefer this function to millis
to avoid time difference error accumulation.
void setTimeout(Context ctx, TimeMs timeout)
#Schedules a forced re-evaluation of a node past timeout
milliseconds after the current transaction time.
A node may have at most one scheduled update. If called multiple times, the subsequent calls cancel the previous schedule for the node.
If you need to schedule evaluation right after the current transaction completes, use setImmediate
instead.
void clearTimeout(Context ctx)
#Cancels scheduled evaluation of a node (if present). Safe to call even if the update was not scheduled.
bool isTimedOut(Context ctx)
#Returns true
if node’s schedule timed out right at the current transaction. Unless scheduled again will return false
since the next transaction.
void setImmediate()
#Schedules a forced re-evaluation of the node at the next transaction.
void raiseError<output_$$$>(Context ctx)
#Raises an error on an output pin. For value types, the error will persist until a valid value will be emitted with emitValue
. For pulse types, the error will be cleared right before the node’s next evaluation.
void raiseError(Context ctx)
#Raises errors on all output pins.
bool getError<input_$$$>(Context ctx)
#Returns true
if a pin has an upstream error, and false
if it has none.
Lists in XOD are not classical linked lists, nor vectors. They are smart (or dumb) views over existing data. The only useful thing a list can do is to create an Iterator<T>
allowing to enumerate list values from the head to the tail.
Usage example
Number sum(List<Number> numbers) {
Number result = 0;
for (Iterator<Number> it = numbers.iterate(); it; ++it)
result += *it;
return result;
}
List<T>
#A list of elements of type T
.
A List<T>
is a thin Pimpl-wrapper around a ListView<T>
. Since internally a List<T>
contains just a single pointer passing a list by value is a cheap operation.
List<T>::List()
#Constructs a new nil (null element) list.
List<T>::List(const ListView<T>* view)
#Wraps a view
into a list. The view
must be kept alive for the whole list lifetime. Otherwise, the program will crash. This requirement is not checked automatically.
Iterator<T> List<T>::iterate() const
#Creates a new iterator pointing to the first element of the list.
Iterator<T>
#An iterator of List<T>
. Iterators are expected to be short-living objects to enumerate elements of a list. They should not be cloned or passed around. An iterator points either to a valid element or it points out of list bounds. The later is used to denote iteration completed.
Iterator<T>::operator bool() const
#Implicit cast to bool
type. Returns true
if the iterator points to a valid element, i.e. if the iterator is valid.
T Iterator<T>::operator*() const
#Returns the element pointed by the iterator. Mimics pointer dereference. Returns a default value for type T
if the iterator is not valid.
bool Iterator<T>::value(T* out) const
#Checks validity and retrieves element at once. Returns true
if the iterator is valid, writes the element into out
in that case. If the iterator is invalid returns false
and keeps out
intact.
Iterator& Iterator<T>::operator++()
#Advances the iterator to the next element. May make it invalid. Conventionally returns self.
size_t length(List<T> xs)
#Returns a length of the list. To compute it iterates over all elements, so it’s not a trivial operation, so prefer caching the result over calling length
multiple times.
size_t dump(List<T> xs, T* outBuff)
#Copies elements from list xs
into the flat outBuff
. A caller must guarantee that the buffer pointed has enough size to contain all elements of the list. Otherwise, the program will crash.
TR foldl(List<T> xs, TR (*func)(TR, T), TR acc)
#Performs left fold (also known as “reduce”) of the list xs
using func
reducer function and acc
as starting value for the accumulator.
size_t formatNumber(Number value, int prec, char* str)
#Converts a Number
to a string. Like dtostrf
/ sprintf
/ to_string
, but RAM-efficient enough to be used on microcontrollers, works uniformly on any platform, and properly handles NaNs and infinity.
Returns the string length without the terminating null character.
Examples:
value | prec | str |
---|---|---|
-0.321 | 0 | "0" |
-0.321 | 2 | "-0.32" |
-0.64 | 0 | "-1" |
123.456 | 0 | "123" |
123.456 | 2 | "123.46" |
NaN | any | "NaN" |
Inf | any | "Inf" |
-Inf | any | "-Inf" |
99000000000 | any | "OVF" |
-99000000000 | any | "-OVF" |